Provide VsLangProj.ProjectProperties through VSProject export#1842
Provide VsLangProj.ProjectProperties through VSProject export#1842basoundr merged 14 commits intodotnet:masterfrom
Conversation
| Dim throwAwayObject As Decimal = Nothing | ||
| If (TypeOf (value) Is UInteger OrElse | ||
| (CpsPropertyDescriptorWrapper.IsAnyCpsComponent(m_Objects) AndAlso Decimal.TryParse(DirectCast(value, String), throwAwayObject))) Then | ||
| iBaseAddress = CUInt(value) |
There was a problem hiding this comment.
@jviau BaseAddress property is currently backed by a StringProperty in the XAML. I still get a string value, if I change the Property type to IntProperty from the StringProperty. For eg: "123456"
As it turns out, to obtain the value, DynamicType's GetValue is called, which always return a string. Is there a way it could return the actual type that the property is initially backed with?
There was a problem hiding this comment.
I don't think there's a way today to get anything other than a string. There are other places in the prop pages where such code exists. However, for compat, it'll be good if the object returned here was the right data type instead of string.
There was a problem hiding this comment.
@srivatsn is correct. Right now dynamic type system is always strings, because it is for UI and also msbuild properties are always strings. However, the various implementations of object IProperty.GetValueAsync() does try to return the value as the correct type. In this piece of code we are commenting on, what is the CPS object you are working on? Is it already the value from the DynamicType or something else?
There was a problem hiding this comment.
It is the value from DynamicType since we get that from DynamicTypeBrowseObject
| public object __project => throw new NotImplementedException(); | ||
|
|
||
| public string StartupObject { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } | ||
| public prjOutputType OutputType |
There was a problem hiding this comment.
Can you group the implemented ones together? Also there are some weird new lines - either have newlines between props or dont
There was a problem hiding this comment.
sure. With CodeLens adornment, it is tough to see them.
| _projectProperties = projectProperties; | ||
| } | ||
|
|
||
|
|
|
|
||
| namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties | ||
| { | ||
| internal class VsLangProjectProperties : VSProject, VSLangProj.ProjectProperties |
There was a problem hiding this comment.
Perhaps make this a partial class, and put the implementation of each interface in a different file? There's a lot of stuff here.
There was a problem hiding this comment.
Partial class, by roslyn convention, would mean a nested type. So let me try to group the implementation separately without partial class. That way, it does not seem unorganized.
There was a problem hiding this comment.
No, we have non-nested partial classes.
There was a problem hiding this comment.
Lots of them, actually: AbstractProject, AbstractLegacyProject, CSharpProjectShim, etc.
| get | ||
| { | ||
| var configurationGeneralBrowseObjectProperties = JoinableTaskFactory.Run(ProjectProperties.GetConfigurationGeneralBrowseObjectPropertiesAsync); | ||
| var value = (IEnumValue)JoinableTaskFactory.Run(configurationGeneralBrowseObjectProperties.OutputTypeEx.GetValueAsync); |
There was a problem hiding this comment.
OutputTypeEx [](start = 107, length = 12)
You shouldn't need this, in fact that should be completely killed from the rules once you have the project properties completed. There should only be an OutputType (string) on the project and depending on the property that's getting asked it should convert it back to either projOutputType or prjOutputTypeEx
There was a problem hiding this comment.
OutputType does not support AppContainerExe or WinMDObj. We could use this property to do the conversion but it looks unnecessary given that we can backup that property directly. Also, I dont see why we have to remove OutputTypeEx completely given that it is a VsLangProj property.
There was a problem hiding this comment.
@srivatsn may have more context here but my understanding is that it was added to the rules page as a workaround to try and provide this property. In the old project system it's the same property (which is usually how "Ex" type properties work). I believe the current provider (which you should be able to delete) is doing that conversion if OutputType is set to either of the UWP values (it converts them to either exe or library).
There was a problem hiding this comment.
That sounds right but I don't remember the details. You'll have to look at history.
There was a problem hiding this comment.
Looking at the history, it does seem to be the case. But we will have to look at what breaks if we remove this. I can create an issue to track this work.
|
|
||
| public prjOriginatorKeyMode AssemblyOriginatorKeyMode { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } | ||
|
|
||
| public bool DelaySign { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } |
There was a problem hiding this comment.
public bool DelaySign { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } [](start = 8, length = 113)
Property pages use this so leaving it unimplemented will cause issues there (or false results).
There was a problem hiding this comment.
DelaySign property is not from Common properties. It is backed up by DynamicTypeBrowseObject from here. This will not be a problem.
There was a problem hiding this comment.
Please file bugs for these unimplemented properties.
| } | ||
|
|
||
| [Fact] | ||
| public void VsLangProjectProperties_OutputType_Get() |
There was a problem hiding this comment.
I was not able to write the tests for set methods because of Moq. I tried the same test below and added the following code but it does not seem to work.
var mockObject = Mock.Get(projectProperties.GetConfigurationGeneralBrowseObjectPropertiesAsync().Result.OutputType); // Get the mock object of IProperty, which is [here](https://github.com/dotnet/roslyn-project-system/blob/master/src/Microsoft.VisualStudio.ProjectSystem.Managed.UnitTests/Mocks/ProjectPropertiesFactory.cs#L110)
mockObject.Verify(m => m.SetValueAsync(It.IsAny<object>()));Is this valid? Am I missing something here?
There was a problem hiding this comment.
I want to mock the call SetValueAsync of the OutputType property.
SetupProperty can be used to mock properties that are overrideable. In this case though, the property will be OutputType, from ConfigurationGeneralBrowseObject , which is not overrideable. So it cannot be mocked.
I tried something like this:
[Fact]
public void VsLangProjectProperties_AssemblyName_Get()
{
var project = UnconfiguredProjectFactory.Create();
var data = new PropertyPageData()
{
Category = ConfigurationGeneral.SchemaName,
PropertyName = ConfigurationGeneral.AssemblyNameProperty,
Value = "Blah"
};
var projectProperties = ProjectPropertiesFactory.Create(project, data);
var activeConfiguredProject = ActiveConfiguredProjectFactory.ImplementValue(() => projectProperties);
var vsLangProjectProperties = new VSProject(Mock.Of<VSLangProj.VSProject>(), IProjectThreadingServiceFactory.Create(), activeConfiguredProject);
Assert.Equal(vsLangProjectProperties.AssemblyName, "Blah");
var mockObject = Mock.Get(projectProperties.GetConfigurationGeneralPropertiesAsync().Result.AssemblyName);
mockObject.Setup(prop => prop.SetValueAsync(It.IsAny<string>()))
.Verifiable();
vsLangProjectProperties.AssemblyName = "Testing";
mockObject.Verify();
}and it results in
Moq.MockVerificationException : The following setups were not matched:
IEvaluatedProperty prop => prop.SetValueAsync(It.IsAny<String>())
Stack Trace:
at Moq.Mock.Verify()
|
Tagging @dotnet/project-system again. Tests are in. |
|
|
||
| public prjOriginatorKeyMode AssemblyOriginatorKeyMode { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } | ||
|
|
||
| public bool DelaySign { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } |
There was a problem hiding this comment.
Please file bugs for these unimplemented properties.
|
|
||
| set | ||
| { | ||
| var configurationGeneralBrowseObjectProperties = JoinableTaskFactory.Run(ProjectProperties.GetConfigurationGeneralBrowseObjectPropertiesAsync); |
There was a problem hiding this comment.
Use IProjectThreadingService.ExecuteSynchronously and wrap the whole method instead of running in multiple contexts.
| { | ||
| get | ||
| { | ||
| return new VsLangProjectProperties(_vsProject, _threadingService, _projectProperties); |
There was a problem hiding this comment.
This should be cached - there should be one of these.
|
|
||
| [Export(ExportContractNames.VsTypes.VSProject, typeof(VSProject))] | ||
| [AppliesTo(ProjectCapability.CSharpOrVisualBasic)] | ||
| [Order(10)] |
There was a problem hiding this comment.
This order looks arbitrary how did you decide it?
There was a problem hiding this comment.
The CPS's implementation is exported with default value(0) . Having an order 10, will give the ability for extensions to export their implementation with an order between 0 &10 and the ability to export with an order greater than 10. We dont want to export our's with MaxValue becauseno one else can have their export ordered higher than ours.
There was a problem hiding this comment.
I think we need a pattern for this - I'm going to introduce one.
| /// <see cref="VSLangProj.ProjectProperties"/>. This enables us to provide | ||
| /// ProjectProperties to the Project Property Pages and maintain Backward Compatibility. | ||
| /// </summary> | ||
| internal class VsLangProjectPropertiesProvider |
There was a problem hiding this comment.
Why does this class even exist, why can't we just export the VsLangProjectProperties?
| /// <summary> | ||
| /// See <see cref="VsLangProjectPropertiesProvider"/> for more info. | ||
| /// </summary> | ||
| internal partial class VsLangProjectProperties : VSLangProj.ProjectProperties |
There was a problem hiding this comment.
This is our VSProject implementation and should be named as such.
| /// This implementation of VSProject just redirects the VSProject call to the contained | ||
| /// VSProject object imported from CPS | ||
| /// </summary> | ||
| internal partial class VsLangProjectProperties : VSProject |
There was a problem hiding this comment.
Can you file a bug to implement VSProject2 here?
|
|
||
| public VSProjectEvents Events => _vsProject.Events; | ||
|
|
||
| // Implementation of VSProject to redirect the call to the actual VSProject object |
There was a problem hiding this comment.
This comment applies the entire class right? Why is it in the middle of it?
| using EnvDTE; | ||
| using VSLangProj; | ||
|
|
||
| namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties |
There was a problem hiding this comment.
This should live in the Automation folder/namespace.
| } | ||
|
|
||
| [Fact] | ||
| public void VsLangProjectProperties_OutputType_Get() |
Remove the ExportVsProfferedProjectService export
Export the VSProject with a higher order attribute This way, this export gets picked up before the CPS export which is exported with Zero ordering Convert obj string to Decimal for Lib Base address This property is backed by the rule which says to get the value after context which only returns the string formatted value even if the property is backed by a PageIntProperty
Few other refactorings
…interfaces VsProject and ProjectProperties Add doc comments
Add Properties to access ProjectProperties and Threading service easily Order the methods after the properties
…rties, which was also being implemented
…ectSystem.VS.Automation.
6a65ab3 to
097e3bf
Compare
097e3bf to
403d07f
Compare
|
@davkean Feedback addressed |
This change exports
VsLangProjectPropertiesasVsProject. ThisVsLangProjectProperties, in addition toVSProject, implements VsLangProj.ProjectProperties as well. This object will now start providing the values in the Project Property pages.Currently I have implemented just the properties, expected from the CommonProperty, that are used in the Property Pages. We can implement the rest of them as and when required. In the future, this object might also have to implement CSharpProjectProperties7 and VBProjectProperties7, again, since we dont need them now, this change will not handle those cases.
Tagging @dotnet/project-system @davkean @srivatsn for review