Skip to content

Commit

Permalink
Merge pull request #9564 from drewnoakes/avoid-empty-target-framework…
Browse files Browse the repository at this point in the history
…-options

Fix showing empty target framework options in the Project Properties UI
  • Loading branch information
drewnoakes authored Oct 21, 2024
2 parents 7abba98 + e73bb19 commit 2ef8923
Showing 1 changed file with 65 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,93 +2,89 @@

using Microsoft.Build.Framework.XamlTypes;
using Microsoft.VisualStudio.ProjectSystem.Properties;
using EnumCollection = System.Collections.Generic.ICollection<Microsoft.VisualStudio.ProjectSystem.Properties.IEnumValue>;

namespace Microsoft.VisualStudio.ProjectSystem.VS.Frameworks
namespace Microsoft.VisualStudio.ProjectSystem.VS.Frameworks;

/// <summary>
/// Responsible for producing valid values for the TargetFramework property from evaluation.
/// </summary>
[ExportDynamicEnumValuesProvider("SupportedTargetFrameworksEnumProvider")]
[AppliesTo(ProjectCapability.DotNet)]
[method: ImportingConstructor]
internal sealed class SupportedTargetFrameworksEnumProvider(ConfiguredProject project, IProjectSubscriptionService subscriptionService)
: SupportedValuesProvider(project, subscriptionService)
{
/// <summary>
/// Responsible for producing valid values for the TargetFramework property from evaluation.
/// </summary>
[ExportDynamicEnumValuesProvider("SupportedTargetFrameworksEnumProvider")]
[AppliesTo(ProjectCapability.DotNet)]
internal class SupportedTargetFrameworksEnumProvider : SupportedValuesProvider
protected override string[] RuleNames => [SupportedNETCoreAppTargetFramework.SchemaName, SupportedNETFrameworkTargetFramework.SchemaName, SupportedNETStandardTargetFramework.SchemaName, ConfigurationGeneral.SchemaName];

protected override ICollection<IEnumValue> Transform(IProjectSubscriptionUpdate input)
{
protected override string[] RuleNames => new[] { SupportedNETCoreAppTargetFramework.SchemaName, SupportedNETFrameworkTargetFramework.SchemaName, SupportedNETStandardTargetFramework.SchemaName, ConfigurationGeneral.SchemaName };
IProjectRuleSnapshot configurationGeneral = input.CurrentState[ConfigurationGeneral.SchemaName];

[ImportingConstructor]
public SupportedTargetFrameworksEnumProvider(ConfiguredProject project, IProjectSubscriptionService subscriptionService)
: base(project, subscriptionService)
string? targetFrameworkIdentifier = configurationGeneral.Properties.GetStringProperty(ConfigurationGeneral.TargetFrameworkIdentifierProperty);

if (StringComparers.FrameworkIdentifiers.Equals(targetFrameworkIdentifier, TargetFrameworkIdentifiers.NetCoreApp))
{
return GetSupportedTargetFrameworksFromItems(SupportedNETCoreAppTargetFramework.SchemaName);
}

protected override EnumCollection Transform(IProjectSubscriptionUpdate input)
else if (StringComparers.FrameworkIdentifiers.Equals(targetFrameworkIdentifier, TargetFrameworkIdentifiers.NetFramework))
{
IProjectRuleSnapshot configurationGeneral = input.CurrentState[ConfigurationGeneral.SchemaName];

string? targetFrameworkIdentifier = configurationGeneral.Properties.GetStringProperty(ConfigurationGeneral.TargetFrameworkIdentifierProperty);
return GetSupportedTargetFrameworksFromItems(SupportedNETFrameworkTargetFramework.SchemaName);
}
else if (StringComparers.FrameworkIdentifiers.Equals(targetFrameworkIdentifier, TargetFrameworkIdentifiers.NetStandard))
{
return GetSupportedTargetFrameworksFromItems(SupportedNETStandardTargetFramework.SchemaName);
}
else
{
string? targetFramework = configurationGeneral.Properties.GetStringProperty(ConfigurationGeneral.TargetFrameworkProperty);
string? targetFrameworkMoniker = configurationGeneral.Properties.GetStringProperty(ConfigurationGeneral.TargetFrameworkMonikerProperty);

if (StringComparers.FrameworkIdentifiers.Equals(targetFrameworkIdentifier, TargetFrameworkIdentifiers.NetCoreApp))
{
return GetSupportedTargetFrameworksFromItems(SupportedNETCoreAppTargetFramework.SchemaName);
}
else if (StringComparers.FrameworkIdentifiers.Equals(targetFrameworkIdentifier, TargetFrameworkIdentifiers.NetFramework))
{
return GetSupportedTargetFrameworksFromItems(SupportedNETFrameworkTargetFramework.SchemaName);
}
else if (StringComparers.FrameworkIdentifiers.Equals(targetFrameworkIdentifier, TargetFrameworkIdentifiers.NetStandard))
{
return GetSupportedTargetFrameworksFromItems(SupportedNETStandardTargetFramework.SchemaName);
}
else
// This is the case where the TargetFrameworkProperty has a value we recognize but it's not in the supported lists the SDK sends us.
// We decided we will show it in the UI.
if (!Strings.IsNullOrEmpty(targetFramework))
{
string? targetFramework = configurationGeneral.Properties.GetStringProperty(ConfigurationGeneral.TargetFrameworkProperty);
string? targetFrameworkMoniker = configurationGeneral.Properties.GetStringProperty(ConfigurationGeneral.TargetFrameworkMonikerProperty);

// This is the case where the TargetFrameworkProperty has a value we recognize but it's not in the supported lists the SDK sends us.
// We decided we will show it in the UI.
if (!Strings.IsNullOrEmpty(targetFramework))
{
return new IEnumValue[]
return
[
new PageEnumValue(new EnumValue
{
new PageEnumValue(new EnumValue
{
Name = targetFrameworkMoniker ?? targetFramework,
DisplayName = targetFrameworkIdentifier ?? targetFramework
})
};
}

return Array.Empty<IEnumValue>();
Name = targetFrameworkMoniker ?? targetFramework,
DisplayName = targetFrameworkIdentifier ?? targetFramework
})
];
}

EnumCollection GetSupportedTargetFrameworksFromItems(string ruleName)
{
IProjectRuleSnapshot snapshot = input.CurrentState[ruleName];

int capacity = snapshot.Items.Count;
var list = new List<IEnumValue>(capacity);

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;
}
// The user has not entered a value for the TargetFramework property. We've had a report of
// seeing this during a manual attempt to migrate a project from legacy CSPROJ to SDK-style.
// We should not show an empty list. Instead, assume the user wants to use .NETCoreApp.
return GetSupportedTargetFrameworksFromItems(SupportedNETCoreAppTargetFramework.SchemaName);
}

protected override IEnumValue ToEnumValue(KeyValuePair<string, IImmutableDictionary<string, string>> item)
ICollection<IEnumValue> GetSupportedTargetFrameworksFromItems(string ruleName)
{
return new PageEnumValue(new EnumValue()
{
// Example: <SupportedTargetFramework Include=".NETCoreApp,Version=v5.0"
// DisplayName=".NET 5.0" />
IProjectRuleSnapshot snapshot = input.CurrentState[ruleName];

var list = new List<IEnumValue>(capacity: snapshot.Items.Count);

Name = item.Key,
DisplayName = item.Value[SupportedTargetFramework.DisplayNameProperty],
});
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;
}
}

protected override int SortValues(IEnumValue a, IEnumValue b)
protected override IEnumValue ToEnumValue(KeyValuePair<string, IImmutableDictionary<string, string>> item)
{
return new PageEnumValue(new EnumValue()
{
return NaturalStringComparer.Instance.Compare(a.DisplayName, b.DisplayName);
}
// Example: <SupportedTargetFramework Include=".NETCoreApp,Version=v5.0"
// DisplayName=".NET 5.0" />

Name = item.Key,
DisplayName = item.Value[SupportedTargetFramework.DisplayNameProperty],
});
}

protected override int SortValues(IEnumValue a, IEnumValue b)
{
return NaturalStringComparer.Instance.Compare(a.DisplayName, b.DisplayName);
}
}

0 comments on commit 2ef8923

Please sign in to comment.