diff --git a/src/Common/XSharpConstants.cs b/src/Common/XSharpConstants.cs index ed63d95e2d..8bf74ec7e7 100644 --- a/src/Common/XSharpConstants.cs +++ b/src/Common/XSharpConstants.cs @@ -22,6 +22,7 @@ internal static class XSharpConstants internal const string EditorFactoryGuidString = "B4829761-2BFA-44B7-8F8F-D2625EBCF218"; internal const string guidCpsProjectTypeString = "AB494DCE-A11B-4583-851E-D4A86775B3F0"; internal const string guidProjectSelectorString = "DF89AA3B-6AE6-4971-8F78-1A03B346A7D5"; + internal const string guidXSharpCpsProjectPkgString = "BAE3ADAE-83B3-44CC-BA79-A18F03F0D121"; internal static readonly Guid guidXSharpProjectFactory = new Guid(guidXSharpProjectFactoryString); internal static readonly Guid guidVOMenuEditorFactory = new Guid(guidXSharpVOMenuEditor); internal static readonly Guid guidVODbServerEditorFactory = new Guid(guidXSharpVODbServerEditor); diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/Rules/ApplicationPropertyPage.XSharp.xaml b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/ApplicationPropertyPage.XSharp.xaml new file mode 100644 index 0000000000..76d871fe51 --- /dev/null +++ b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/ApplicationPropertyPage.XSharp.xaml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + (has-evaluated-value "Application" "ResourceSpecificationKind" "IconAndManifest") + + + + + + + + + + + + + + + + (and + (has-evaluated-value "Application" "ApplicationManifestKind" "CustomManifest") + (has-evaluated-value "Application" "ResourceSpecificationKind" "IconAndManifest")) + + + + + + + + + + + + + + + + + (has-evaluated-value "Application" "ResourceSpecificationKind" "ResourceFile") + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/Rules/BuildPropertyPage.XSharp.xaml b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/BuildPropertyPage.XSharp.xaml new file mode 100644 index 0000000000..5c9f479083 --- /dev/null +++ b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/BuildPropertyPage.XSharp.xaml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + (has-evaluated-value "Build" "WarningLevelOverridden" false) + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/Rules/DialectPage.XSharp.xaml b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/DialectPage.XSharp.xaml new file mode 100644 index 0000000000..648e6635a8 --- /dev/null +++ b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/DialectPage.XSharp.xaml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + (not (has-evaluated-value "Language" "Dialect" "Core")) + + + + + + + + (not (has-evaluated-value "Language" "Dialect" "Core")) + + + + + + + + (not (has-evaluated-value "Language" "Dialect" "Core")) + + + + + + + + (not (has-evaluated-value "Language" "Dialect" "Core")) + + + + + + + + (not (has-evaluated-value "Language" "Dialect" "Core")) + + + + + + + + (not (has-evaluated-value "Language" "Dialect" "Core")) + + + + + + + + (not (has-evaluated-value "Language" "Dialect" "Core")) + + + + + + + + (not (has-evaluated-value "Language" "Dialect" "Core")) + + + + + + + + (not (has-evaluated-value "Language" "Dialect" "Core")) + + + + + + + + (not (has-evaluated-value "Language" "Dialect" "Core")) + + + + + + + + + (has-evaluated-value "Language" "Dialect" "XPP") + + + + + + + + + (has-evaluated-value "Language" "Dialect" "FoxPro") + + + + + + + + (not (has-evaluated-value "Language" "Dialect" "Core")) + + + + + + + + (has-evaluated-value "Language" "Dialect" "FoxPro") + + + + + + diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/Rules/LanguagePage.XSharp.xaml b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/LanguagePage.XSharp.xaml new file mode 100644 index 0000000000..22ee8e4d55 --- /dev/null +++ b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/LanguagePage.XSharp.xaml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (not (has-evaluated-value "Language" "Dialect" "Core")) + + + + + + + + (has-evaluated-value "Language" "MemVar" true) + + + + + + + + + + + + + + + + diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/Rules/NativeResource.BrowseObject.xaml b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/NativeResource.BrowseObject.xaml new file mode 100644 index 0000000000..bcba14d58e --- /dev/null +++ b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/NativeResource.BrowseObject.xaml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/Rules/NativeResource.xaml b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/NativeResource.xaml new file mode 100644 index 0000000000..5e28db5c84 --- /dev/null +++ b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/NativeResource.xaml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/Rules/ProjectItemsSchema.XSharp.xaml b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/ProjectItemsSchema.XSharp.xaml new file mode 100644 index 0000000000..b02cde9ef4 --- /dev/null +++ b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/ProjectItemsSchema.XSharp.xaml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/Rules/ReferencesPage.XSharp.xaml b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/ReferencesPage.XSharp.xaml new file mode 100644 index 0000000000..4001371c2b --- /dev/null +++ b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/ReferencesPage.XSharp.xaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + true + + + + + + + diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/Rules/VOBinary.BrowseObject.xaml b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/VOBinary.BrowseObject.xaml new file mode 100644 index 0000000000..19446f9949 --- /dev/null +++ b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/VOBinary.BrowseObject.xaml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/Rules/VOBinary.xaml b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/VOBinary.xaml new file mode 100644 index 0000000000..9add003e83 --- /dev/null +++ b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/VOBinary.xaml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/Rules/XSharpCompilerCommandLineArgs.xaml b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/XSharpCompilerCommandLineArgs.xaml new file mode 100644 index 0000000000..da8ca1a8fd --- /dev/null +++ b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/XSharpCompilerCommandLineArgs.xaml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/Rules/XSharpProjectProperties.xaml b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/XSharpProjectProperties.xaml new file mode 100644 index 0000000000..c598ad3125 --- /dev/null +++ b/src/Compiler/src/Compiler/XSharpBuildTask/Rules/XSharpProjectProperties.xaml @@ -0,0 +1,19 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.Build.csproj b/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.Build.csproj index 4f7230f281..204f4fa28b 100644 --- a/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.Build.csproj +++ b/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.Build.csproj @@ -73,6 +73,42 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.CrossTargeting.targets b/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.CrossTargeting.targets index 067451c607..28d70de313 100644 --- a/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.CrossTargeting.targets +++ b/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.CrossTargeting.targets @@ -14,6 +14,13 @@ Copyright (C) XSharp BV. All rights reserved. + + $(MSBuildThisFileDirectory)XSharp.DesignTime.targets + + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\Managed\Microsoft.Managed.DesignTime.targets + + + diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.CurrentVersion.targets b/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.CurrentVersion.targets index 40a8b1d186..dcbbdf573e 100644 --- a/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.CurrentVersion.targets +++ b/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.CurrentVersion.targets @@ -374,6 +374,13 @@ Copyright (c) XSharp B.V. All Rights Reserved + + $(MSBuildThisFileDirectory)XSharp.DesignTime.targets + + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\Managed\Microsoft.Managed.DesignTime.targets + + + diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.DesignTime.targets b/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.DesignTime.targets index 2ed7abc2f1..f7e9e38cd3 100644 --- a/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.DesignTime.targets +++ b/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.DesignTime.targets @@ -6,14 +6,18 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and created a backup copy. Incorrect changes to this file will make it impossible to load or build your projects from the command-line or the IDE. - This file contains Visual Studio and designer-related properties and items for X# projects. - It is based on Microsoft.CSharp.DesignTime.targets - It gets install inside the \MSBuild\Microsoft\VisualStudio\Managed folder. - This folder is also known as "ManagedXamlResourcesDirectory" - We inherit the property pages from Microsoft.Managed.DesignTime.targets and extend some of them - with X# specific properties. Our pages are called .XSharp.Xaml. - The order attribute in the xaml files determines the order in which pages are shown in the UI. + This file contains Visual Studio and designer-related properties and items for X# projects + that are loaded by the CPS based X# project system (XSharp.ProjectSystemCPS). + It is based on Microsoft.CSharp.DesignTime.targets. + It is imported by XSharp.CurrentVersion.targets and XSharp.CrossTargeting.targets, but only when + the property XSharpCpsProjectSystem is 'true'. The CPS project type sets that property as a global + property, so projects loaded by the MPFproj project system and command line builds are not affected. + + It imports Microsoft.Managed.DesignTime.targets from the Visual Studio installation + (\MSBuild\Microsoft\VisualStudio\Managed). That file declares the managed capabilities + (.NET, DependenciesTree, PackageReferences, ...) and the property page rules of the managed project system. + X# specific rules live in the Rules subfolder of this folder. Copyright (c) XSharp B.V. All Rights Reserved @@ -24,30 +28,74 @@ inspired by Microsoft.CSharp.DesignTime.targets - ProjectDebugger - $(MSBuildThisFileDirectory)\Microsoft + $(MSBuildExtensionsPath)\Microsoft\VisualStudio\Managed\Microsoft.Managed.DesignTime.targets + $(MSBuildThisFileDirectory)Rules\ - - + Properties false X# - {694DD9B6-B865-4C5B-AD85-86356E9C88DC} + + {E9B6EE0F-9BFA-4CFF-A60B-51A08BBC5050} XSharp - {FAE04EC0-301F-11d3-BF4B-00C04F79EFBC} - {FAE04EC1-301F-11d3-BF4B-00C04F79EFBC} - {FAE04EC1-301F-11d3-BF4B-00C04F79EFBC} + + {AA6C8D78-22FF-423A-9C7C-5F2393824E04} + {AA6C8D78-22FF-423A-9C7C-5F2393824E04} + {AA6C8D78-22FF-423A-9C7C-5F2393824E04} Private Camel Declarations - - false + + <_GlobalPropertiesToRemoveFromProjectReferences>$(_GlobalPropertiesToRemoveFromProjectReferences);XSharpCpsProjectSystem + + + + + Project + + + + File + + + BrowseObject + + + + ProjectSubscriptionService + + + + + diff --git a/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.SDK.Props b/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.SDK.Props index de18bdbd9b..ccca2ea88b 100644 --- a/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.SDK.Props +++ b/src/Compiler/src/Compiler/XSharpBuildTask/XSharp.SDK.Props @@ -23,6 +23,10 @@ Copyright (C) XSharp BV. All rights reserved. is imported after this file) and is listed so the value is picked up should that order ever change. The output directories are added explicitly for the same reason - they are only appended to DefaultItemExcludes later, which is too late for the globs below. --> + + $(DefaultItemExcludes);**/*.ShadowDesigner/** $(DefaultItemExcludes);$(DefaultExcludesInProjectFolder) $(XSharpDefaultItemExcludes);$(BaseOutputPath)/** $(XSharpDefaultItemExcludes);$(BaseIntermediateOutputPath)/** diff --git a/src/Master.slnx b/src/Master.slnx index bc59130651..a29ffe6d10 100644 --- a/src/Master.slnx +++ b/src/Master.slnx @@ -289,6 +289,7 @@ + diff --git a/src/VSIntegration2022.sln b/src/VSIntegration2022.sln index bc753ca6fb..b80997753a 100644 --- a/src/VSIntegration2022.sln +++ b/src/VSIntegration2022.sln @@ -51,6 +51,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeDomProvider", "VisualSt EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Support2022", "VisualStudio\Support\Support2022.csproj", "{FB387BE5-A620-46EA-A7A6-1CCB9177F7C1}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectSystemCPS", "VisualStudio\ProjectSystemCPS\ProjectSystemCPS.csproj", "{99708D46-58A9-46B6-B77F-003ADAF15FF6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -217,6 +219,18 @@ Global {FB387BE5-A620-46EA-A7A6-1CCB9177F7C1}.Release|arm64.Build.0 = Release|Any CPU {FB387BE5-A620-46EA-A7A6-1CCB9177F7C1}.Release|x86.ActiveCfg = Release|Any CPU {FB387BE5-A620-46EA-A7A6-1CCB9177F7C1}.Release|x86.Build.0 = Release|Any CPU + {99708D46-58A9-46B6-B77F-003ADAF15FF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {99708D46-58A9-46B6-B77F-003ADAF15FF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99708D46-58A9-46B6-B77F-003ADAF15FF6}.Debug|arm64.ActiveCfg = Debug|Any CPU + {99708D46-58A9-46B6-B77F-003ADAF15FF6}.Debug|arm64.Build.0 = Debug|Any CPU + {99708D46-58A9-46B6-B77F-003ADAF15FF6}.Debug|x86.ActiveCfg = Debug|Any CPU + {99708D46-58A9-46B6-B77F-003ADAF15FF6}.Debug|x86.Build.0 = Debug|Any CPU + {99708D46-58A9-46B6-B77F-003ADAF15FF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {99708D46-58A9-46B6-B77F-003ADAF15FF6}.Release|Any CPU.Build.0 = Release|Any CPU + {99708D46-58A9-46B6-B77F-003ADAF15FF6}.Release|arm64.ActiveCfg = Release|Any CPU + {99708D46-58A9-46B6-B77F-003ADAF15FF6}.Release|arm64.Build.0 = Release|Any CPU + {99708D46-58A9-46B6-B77F-003ADAF15FF6}.Release|x86.ActiveCfg = Release|Any CPU + {99708D46-58A9-46B6-B77F-003ADAF15FF6}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -234,6 +248,7 @@ Global {960714E8-FF93-4A42-B9BA-610B0F2014DB} = {0D3C69B5-D236-42B3-B70D-2C9ABC9D434C} {7B7D9574-66A7-4719-AA48-C2386D7BF153} = {9F285506-97D3-486E-B59E-7C30C56B95AE} {FB387BE5-A620-46EA-A7A6-1CCB9177F7C1} = {0D3C69B5-D236-42B3-B70D-2C9ABC9D434C} + {99708D46-58A9-46B6-B77F-003ADAF15FF6} = {0D3C69B5-D236-42B3-B70D-2C9ABC9D434C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6F61AAF2-9C04-4D27-887F-8A475F079D9C} diff --git a/src/VisualStudio/CodeDomProvider/XSharpCodeGenerator.cs b/src/VisualStudio/CodeDomProvider/XSharpCodeGenerator.cs index 0cac38dc6a..508b460ec4 100644 --- a/src/VisualStudio/CodeDomProvider/XSharpCodeGenerator.cs +++ b/src/VisualStudio/CodeDomProvider/XSharpCodeGenerator.cs @@ -809,7 +809,8 @@ protected override void GenerateFieldReferenceExpression(CodeFieldReferenceExpre { this.GenerateExpression(e.TargetObject); // If we have a Type, we must use a dot as a selector - if (e.TargetObject.GetType() == typeof(CodeTypeReferenceExpression)) + // (also for XCodeTypeReferenceExpression, which the parser creates, like in GeneratePropertyReferenceExpression) + if (e.TargetObject is CodeTypeReferenceExpression) { base.Output.Write(this.staticSelector); } diff --git a/src/VisualStudio/ProjectBase/FileNode.cs b/src/VisualStudio/ProjectBase/FileNode.cs index 5665d3234b..b9108165e0 100644 --- a/src/VisualStudio/ProjectBase/FileNode.cs +++ b/src/VisualStudio/ProjectBase/FileNode.cs @@ -1244,30 +1244,15 @@ protected virtual bool RenameDocument(string oldName, string newName, out Hierar bool bDependantItem = !string.IsNullOrEmpty(dependentOf); childAdded.ItemNode.RemoveFromProjectFile(); - if (this.ProjectMgr.IsSdkProject) - { - // For SDK Projects we retrieve the new element from the - // build project since the item was added to the project file and MSBuild will have created a new item for it. - this.ProjectMgr.BuildProject.ReevaluateIfNecessary(); - var items = this.ProjectMgr.BuildProject.Items.ToArray(); - foreach (var item in items.Where(i => i.EvaluatedInclude == newInclude && i.ItemType == this.ItemNode.ItemName)) - { - childAdded.ItemNode = new ProjectElement(ProjectMgr, item,false); - break; - } - } - else + // Assign the existing MSBuild item to the new child node and update its path. + childAdded.ItemNode = this.ItemNode; + childAdded.ItemNode.RefreshProperties(); + if (!childAdded.IsImported) { - // Assign the existing MSBuild item to the new child node and update its path. - childAdded.ItemNode = this.ItemNode; - childAdded.ItemNode.RefreshProperties(); - if (!childAdded.IsImported) - { - childAdded.ItemNode.Item.ItemType = this.ItemNode.ItemName; - childAdded.ItemNode.Item.Xml.Include = newInclude; - if (bDependantItem) - childAdded.ItemNode.SetMetadata(ProjectFileConstants.DependentUpon, dependentOf); - } + childAdded.ItemNode.Item.ItemType = this.ItemNode.ItemName; + childAdded.ItemNode.Item.Xml.Include = newInclude; + if (bDependantItem) + childAdded.ItemNode.SetMetadata(ProjectFileConstants.DependentUpon, dependentOf); } childAdded.ItemNode.RefreshProperties(); diff --git a/src/VisualStudio/ProjectBase/ProjectElement.cs b/src/VisualStudio/ProjectBase/ProjectElement.cs index 00e3a2b7a8..7f8cc23618 100644 --- a/src/VisualStudio/ProjectBase/ProjectElement.cs +++ b/src/VisualStudio/ProjectBase/ProjectElement.cs @@ -355,19 +355,15 @@ public void RefreshProperties() if(this.IsVirtual) return; - bool isSdk = !string.IsNullOrEmpty(itemProject?.BuildProject?.Xml?.Sdk); - if (!isSdk) + // prevent duplicate items. + itemProject.BuildProject.ReevaluateIfNecessary(); + IEnumerable items = itemProject.BuildProject.GetItems(this.item.ItemType); + foreach (ProjectItem projectItem in items) { - // prevent duplicate items. - itemProject.BuildProject.ReevaluateIfNecessary(); - IEnumerable items = itemProject.BuildProject.GetItems(this.item.ItemType); - foreach (ProjectItem projectItem in items) + if (string.Equals(projectItem?.UnevaluatedInclude, item.UnevaluatedInclude, StringComparison.OrdinalIgnoreCase)) { - if (string.Equals(projectItem?.UnevaluatedInclude, item.UnevaluatedInclude, StringComparison.OrdinalIgnoreCase)) - { - this.item = projectItem; - return; - } + this.item = projectItem; + return; } } } diff --git a/src/VisualStudio/ProjectBase/ProjectFactory.cs b/src/VisualStudio/ProjectBase/ProjectFactory.cs index d9cd9be9b9..91a716128a 100644 --- a/src/VisualStudio/ProjectBase/ProjectFactory.cs +++ b/src/VisualStudio/ProjectBase/ProjectFactory.cs @@ -107,7 +107,6 @@ protected ProjectFactory(Microsoft.VisualStudio.Shell.Package package) #region abstract methods protected abstract ProjectNode CreateProject(); - protected abstract ProjectNode CreateSdkProject(); #endregion #region overriden methods @@ -169,16 +168,9 @@ protected override object PreCreateForOuter(IntPtr outerProjectIUnknown) // Please be very carefull what is initialized here on the ProjectNode. Normally this should only instantiate and return a project node. // The reason why one should very carefully add state to the project node here is that at this point the aggregation has not yet been created and anything that would cause a CCW for the project to be created would cause the aggregation to fail // Our reasoning is that there is no other place where state on the project node can be set that is known by the Factory and has to execute before the Load method. - bool IsSdkProject = !String.IsNullOrEmpty(this.buildProject.Xml.Sdk); - ProjectNode node; - if (IsSdkProject) - { - node = this.CreateSdkProject(); - } - else - { - node = this.CreateProject(); - } + // SDK-style projects are rejected before this point (XSharpProjectFactory.CreateProject): they are + // loaded by the CPS project system. + ProjectNode node = this.CreateProject(); Utilities.CheckNotNull(node, "The project failed to be created"); node.BuildEngine = this.buildEngine; node.BuildProject = this.buildProject; diff --git a/src/VisualStudio/ProjectBase/ProjectNode.cs b/src/VisualStudio/ProjectBase/ProjectNode.cs index 94eb049870..d0f3381747 100644 --- a/src/VisualStudio/ProjectBase/ProjectNode.cs +++ b/src/VisualStudio/ProjectBase/ProjectNode.cs @@ -1001,8 +1001,6 @@ public IServiceProvider Site } } - public virtual bool IsSdkProject => false; - /// /// Gets an ImageHandler for the project node. /// @@ -7466,15 +7464,6 @@ private string GetComponentPickerDirectories() public int UpdateTargetFramework(IVsHierarchy pHier, string currentTargetFramework, string newTargetFramework) { ThreadHelper.ThrowIfNotOnUIThread(); - ThreadHelper.ThrowIfNotOnUIThread(); - if (this.IsSdkProject) - { - // Let the .NET SDK infer TargetFrameworkIdentifier/Version/Profile and the - // TargetPlatform* properties from the full moniker. Setting them here would - // suppress that inference and drop the platform suffix (e.g. net8.0-windows7.0). - SetProjectProperty("TargetFramework", newTargetFramework); - return VSConstants.S_OK; - } FrameworkName moniker = new FrameworkName(newTargetFramework); SetProjectProperty(ProjectFileConstants.TargetFrameworkIdentifier, moniker.Identifier); SetProjectProperty(ProjectFileConstants.TargetFrameworkVersion, "v" + moniker.Version); diff --git a/src/VisualStudio/ProjectBase/ProjectReferenceNode.cs b/src/VisualStudio/ProjectBase/ProjectReferenceNode.cs index 20c60bea25..d01e914576 100644 --- a/src/VisualStudio/ProjectBase/ProjectReferenceNode.cs +++ b/src/VisualStudio/ProjectBase/ProjectReferenceNode.cs @@ -420,7 +420,7 @@ public ProjectReferenceNode(ProjectNode root, ProjectElement element) ThreadHelper.ThrowIfNotOnUIThread(); this.ReferencedProjectName = this.ItemNode.GetMetadata(ProjectFileConstants.Name); - if (guidString == String.Empty && ! root.IsSdkProject) + if (guidString == String.Empty) { guidString = Guid.NewGuid().ToString("B"); } @@ -429,10 +429,7 @@ public ProjectReferenceNode(ProjectNode root, ProjectElement element) ReferencedProjectName = Path.GetFileNameWithoutExtension(referencedProjectRelativePath); } - // An SDK style project file does not store the guid of the referenced project, so we may - // not have one here, for example when the referenced project has not been loaded yet. - // Do not throw in that case: the node must appear in the hierarchy anyway. The guid is - // filled in later, see XSharpProjectNode.FixReferences() and UpdateReferencedProjectGuid(). + // Do not throw for an invalid guid: the node must appear in the hierarchy anyway. if (!Guid.TryParse(guidString, out var guid)) { guid = Guid.Empty; diff --git a/src/VisualStudio/ProjectPackage/CodeDomProvider/VSXsharpCodeDomProvider.cs b/src/VisualStudio/ProjectPackage/CodeDomProvider/VSXsharpCodeDomProvider.cs index 00f32658f4..c5c80746fb 100644 --- a/src/VisualStudio/ProjectPackage/CodeDomProvider/VSXsharpCodeDomProvider.cs +++ b/src/VisualStudio/ProjectPackage/CodeDomProvider/VSXsharpCodeDomProvider.cs @@ -14,6 +14,7 @@ using Microsoft; using Microsoft.VisualStudio.Project; using XSharp.Settings; +using XSharpModel; namespace XSharp.Project { @@ -30,6 +31,8 @@ public XDocDataTextReader(DocData docData, CodeTypeDeclaration classname) :base( public class VSXSharpCodeDomProvider : XSharp.CodeDom.XSharpCodeDomProvider { private XSharpFileNode _fileNode; + // Full path of the file when there is no MPFproj file node (see the second constructor) + private string _filePath; // The parameterless constructor is called by the WPF designer ? @@ -40,22 +43,34 @@ public VSXSharpCodeDomProvider(XSharpFileNode fileNode) _projectNode = projectNode.ProjectModel; } + /// + /// For a file of a project without MPFproj file nodes (an X# project loaded by the CPS project system): + /// the code model project of the file and its full path. + /// + public VSXSharpCodeDomProvider(XProject project, string filePath) + { + _projectNode = project; + _filePath = filePath; + } + #region helper functions private bool IsFormSubType { - get { return _fileNode.HasDesigner; } + get { return _fileNode != null && _fileNode.HasDesigner; } } private string GetFilePath() { + if (_fileNode == null) + return _filePath; return Path.Combine(Path.GetDirectoryName(_fileNode.GetMkDocument()), _fileNode.FileName); } private string GetDesignerFilePath() { - return Path.Combine(Path.GetDirectoryName(_fileNode.GetMkDocument()), - Path.GetFileNameWithoutExtension(_fileNode.FileName) + + return Path.Combine(Path.GetDirectoryName(GetFilePath()), + Path.GetFileNameWithoutExtension(GetFilePath()) + ".Designer.prg"); } @@ -139,17 +154,23 @@ private void SaveSource(string filename, string source, Encoding encoding, bool { source = XSettings.SynchronizeKeywordCase(source, filename); - XSharpFileNode node = _fileNode.FindChild(filename) as XSharpFileNode; + // assign the source to the open buffer when possible + string url = null; + if (_fileNode != null) + { + XSharpFileNode node = _fileNode.FindChild(filename) as XSharpFileNode; + url = node?.Url; + } + else + { + url = filename; + } bool done = false; - if (node != null) + if (url != null && XDocuments.IsOpen(url)) { - // assign the source to the open buffer when possible - if (XDocuments.IsOpen(node.Url)) + if (XDocuments.SetText(url, source)) { - if (XDocuments.SetText(node.Url, source)) - { - done = true; - } + done = true; } } if (!done && SaveToDisk) diff --git a/src/VisualStudio/ProjectPackage/CodeDomProvider/XSharpCodeDomHelper.cs b/src/VisualStudio/ProjectPackage/CodeDomProvider/XSharpCodeDomHelper.cs index f82a805830..7de4d49e4b 100644 --- a/src/VisualStudio/ProjectPackage/CodeDomProvider/XSharpCodeDomHelper.cs +++ b/src/VisualStudio/ProjectPackage/CodeDomProvider/XSharpCodeDomHelper.cs @@ -10,7 +10,14 @@ namespace XSharp.CodeDom { + // Also compiled into XSharp.ProjectSystemCPS (shadow WinForms designer), as an internal copy. It does not + // live in XSharp.CodeDomProvider because the X# installation puts that assembly in the GAC: a newer + // VSIX would then run against the older installed version. +#if XSHARP_PROJECTSYSTEMCPS + internal class XSharpCodeDomHelper +#else public class XSharpCodeDomHelper +#endif { /// /// Merge both CodeCompileUnit. The main type (class) will come from formCompileUnit diff --git a/src/VisualStudio/ProjectPackage/Commands/CommandAddDesignerFile.cs b/src/VisualStudio/ProjectPackage/Commands/CommandAddDesignerFile.cs index 5cead49a07..44d96095d0 100644 --- a/src/VisualStudio/ProjectPackage/Commands/CommandAddDesignerFile.cs +++ b/src/VisualStudio/ProjectPackage/Commands/CommandAddDesignerFile.cs @@ -31,9 +31,14 @@ private async Task CheckAvailabilityAsync() bool visible = false; foreach (var item in items) { - if (item is PhysicalFile file) + if (item is PhysicalFile file && IsXSharpSourceFile(file)) { var subtype = await file.GetAttributeAsync(ProjectFileConstants.SubType); + if (string.IsNullOrEmpty(subtype) && !await Commands.ProjectIsMpfProjectAsync()) + { + // SDK-style projects (CPS) rarely have a SubType + subtype = InferSubType(file.FullPath); + } if (subtype == ProjectFileAttributeValue.Form || subtype == ProjectFileAttributeValue.UserControl) { visible = true; @@ -46,6 +51,12 @@ private async Task CheckAvailabilityAsync() currentFile = null; } } + if (visible && File.Exists(XSharpCodeDomHelper.BuildDesignerFileName(file.FullPath))) + { + // a designer file that is not nested below the form + visible = false; + currentFile = null; + } } } } @@ -57,36 +68,103 @@ private async Task CheckAvailabilityAsync() } } + private static bool IsXSharpSourceFile(PhysicalFile file) + { + return string.Equals(Path.GetExtension(file.FullPath), ".prg", StringComparison.OrdinalIgnoreCase) && + !file.FullPath.EndsWith(".designer.prg", StringComparison.OrdinalIgnoreCase) && + file.ContainingProject.IsXSharp(); + } + + /// + /// Form or UserControl when the first class of the file inherits from one of them, directly or through + /// its base classes. The same rule as XSharpFileNode.DetermineSubType, from the code model. + /// + private static string InferSubType(string path) + { + var xFile = XSolution.FindFile(path); + if (xFile?.Project == null) + return null; + var first = XDatabase.GetTypesInFile(xFile).FirstOrDefault(c => c.TypeName != XLiterals.GlobalName); + if (first == null) + return null; + var usings = xFile.Usings.Concat(xFile.StaticUsings).ToList(); + var baseType = first.BaseTypeName; + // a limit against cycles in broken code + for (int i = 0; i < 20 && !string.IsNullOrEmpty(baseType); i++) + { + var subType = XSharpFileNode.TypeNameToSubtype(baseType); + if (!string.IsNullOrEmpty(subType)) + return subType; + baseType = xFile.Project.FindType(baseType, usings)?.BaseTypeName; + } + return null; + } + protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) + { + try + { + await AddDesignerFileAsync(); + } + catch (Exception ex) + { + // The command framework would only write it to the activity log + XSettings.Exception(ex); + await VS.MessageBox.ShowErrorAsync(CommandTitle, ex.Message); + } + finally + { + await VS.StatusBar.ShowMessageAsync(""); + } + } + + private const string CommandTitle = "Add .designer file"; + + private async Task AddDesignerFileAsync() { if (currentFile != null) { var newfile = System.IO.Path.ChangeExtension(currentFile.FullPath, ".designer.prg"); - await VS.StatusBar.ShowMessageAsync("Creating designer file:" + newfile); - System.IO.File.WriteAllText(newfile, ""); var project = currentFile.ContainingProject; - XSharpProjectNode projectNode = null; - if (project != null) + var xproject = project != null ? XSolution.FindProjectByFileName(project.FullPath) : null; + if (xproject != null) { - var xproject = XSolution.FindProjectByFileName(project.FullPath); - if (xproject != null) + XSettings.Information($"Reading file data for {currentFile.FullPath} from database"); + VSXSharpCodeDomProvider provider; + if (xproject.ProjectNode is XSharpProjectNode projectNode && + projectNode.FindChild(currentFile.FullPath) is XSharpFileNode fileNode) { - xproject.ProjectNode.AddFileNode(newfile); - projectNode = (XSharpProjectNode)xproject.ProjectNode; + provider = new VSXSharpCodeDomProvider(fileNode); + } + else + { + provider = new VSXSharpCodeDomProvider(xproject, currentFile.FullPath); } - - XSettings.Information($"Reading file data for {currentFile.FullPath} from database"); - XSharpFileNode fileNode; - fileNode = (XSharpFileNode) projectNode.FindChild(currentFile.FullPath); - var provider = new VSXSharpCodeDomProvider(fileNode); provider.FileName = currentFile.FullPath; var source = File.ReadAllText(currentFile.FullPath); var reader = new StringReader(source); var ccuForm = provider.Parse(reader) as XCodeCompileUnit; + // The parser recognizes InitializeComponent (returning System.Void) and the types in it only with + // the references of the project. Right after the solution was opened they may not be loaded yet; + // then the generated code would be wrong (':' instead of '.' for static members). + var formClass = ccuForm == null ? null : XSharpCodeDomHelper.FindDesignerClass(ccuForm) as XCodeTypeDeclaration; + if (formClass == null) + { + await VS.MessageBox.ShowErrorAsync(CommandTitle, + $"Could not find the form class with its InitializeComponent method in {Path.GetFileName(currentFile.FullPath)}.\n\n" + + "When the solution was just opened, try again after the project has finished loading."); + return; + } ccuForm.Source = source; ccuForm.FileName = currentFile.FullPath; ccuForm.MustWrite = true; - var formClass = (XCodeTypeDeclaration) XSharpCodeDomHelper.FindDesignerClass(ccuForm); + + await VS.StatusBar.ShowMessageAsync("Creating designer file:" + newfile); + System.IO.File.WriteAllText(newfile, ""); + // MPFproj adds the designer file below the form. The CPS project system does nothing here: the + // SDK globs include the new file, and XSharp.SDK.Props makes it depend on the form. + xproject.ProjectNode.AddFileNode(newfile); + var designerClass = new XCodeTypeDeclaration(formClass.Name); var ccuDesigner = new XCodeCompileUnit(); ccuDesigner.FileName = newfile; @@ -146,10 +224,11 @@ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) var writer = new StringWriter(); var options = new CodeGeneratorOptions(); provider.GenerateCodeFromCompileUnit(mergedccu, writer, options); - var newSource = writer.ToString().ToLower(); - if (newSource.Contains("#endregion") && !newSource.Contains("#region")) + var newSource = writer.ToString(); + // only the check is case insensitive: the form file is written as generated + var lowerSource = newSource.ToLower(); + if (lowerSource.Contains("#endregion") && !lowerSource.Contains("#region")) { - newSource = writer.ToString(); var lines = Regex.Split(newSource, "\r\n|\r|\n"); var sb = new StringBuilder(); foreach (var line in lines) @@ -161,9 +240,6 @@ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) } File.WriteAllText(currentFile.FullPath, newSource); } - - - await VS.StatusBar.ShowMessageAsync(""); } } } diff --git a/src/VisualStudio/ProjectPackage/Commands/CommandBuild.cs b/src/VisualStudio/ProjectPackage/Commands/CommandBuild.cs deleted file mode 100644 index ebc78b3064..0000000000 --- a/src/VisualStudio/ProjectPackage/Commands/CommandBuild.cs +++ /dev/null @@ -1,227 +0,0 @@ -using Community.VisualStudio.Toolkit; - -using Microsoft.VisualStudio; -using OLE=Microsoft.VisualStudio.OLE.Interop; -using Microsoft.VisualStudio.Shell; - -using System; -using System.Diagnostics; -using System.IO; -using System.Threading.Tasks; -using System.Reflection.Metadata.Ecma335; - -namespace XSharp.Project -{ - internal abstract class CommandBuild : BaseCommand where T : class, new() - { - protected abstract string CommandName { get; } - protected abstract string CommandDescription { get; } - protected CommandProgression DoCmd() - { - bool ourProject = false; - ThreadHelper.JoinableTaskFactory.Run(async () => - { - ourProject = await Commands.ProjectIsXSharpProjectAsync(); - if (ourProject) - await DoCmdAsync(); - - - }); - if (ourProject) - return CommandProgression.Stop; - return CommandProgression.Continue; - } - - - protected override async Task InitializeCompletedAsync() - { - await base.InitializeCompletedAsync(); - var cmd = await VS.Commands.FindCommandAsync(CommandName); - if (cmd != null) - { - await VS.Commands.InterceptAsync(CommandName, () => DoCmd()); - } - } - protected string projectPath; - protected async Task VerifySdkProjectAsync() - { - await VS.Commands.ExecuteAsync(KnownCommands.File_SaveAll); - var project = await VS.Solutions.GetActiveProjectAsync(); - if (project == null) - { - await VS.MessageBox.ShowErrorAsync(CommandDescription, "No active project selected."); - return false; - } - projectPath = project.FullPath; - var ourProject = await Commands.ProjectIsXSharpProjectAsync(); - if (!ourProject) - { - return false; - } - var prj = XSharpProjectNode.FindProject(projectPath); - if (prj == null || !prj.IsSdkProject) - { - await VS.MessageBox.ShowErrorAsync(CommandDescription, $"The {CommandDescription} command is only available for SDK-style projects."); - return false; - } - return true; - } - - protected async Task CreateProcessAsync(string arguments, string output) - { - var psi = new ProcessStartInfo - { - FileName = "dotnet", - Arguments = arguments, - WorkingDirectory = Path.GetDirectoryName(projectPath), - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true - }; - var process = new Process { StartInfo = psi }; - process.OutputDataReceived += Process_OutputDataReceived; - process.ErrorDataReceived += Process_ErrorDataReceived; ; - - await EnsureOutputPaneAsync(); - await outputPane.ActivateAsync(); - await outputPane.WriteLineAsync(output); - await outputPane.WriteLineAsync($"Command: dotnet {arguments}"); - await outputPane.WriteLineAsync(""); - process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - - await Task.Run(() => process.WaitForExit()); - - if (process.ExitCode == 0) - { - await outputPane.WriteLineAsync(""); - var msg = CommandDescription + " succeeded."; - await outputPane.WriteLineAsync(msg); - await VS.StatusBar.ShowMessageAsync(msg); - } - else - { - await outputPane.WriteLineAsync(""); - await outputPane.WriteLineAsync($"{CommandDescription} failed with exit code {process.ExitCode}."); - await VS.StatusBar.ShowMessageAsync(CommandDescription+" failed."); - await VS.MessageBox.ShowErrorAsync(CommandDescription, CommandDescription + " failed. See Output window for details."); - } - - return process.ExitCode; - } - - private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs ea) - { - ThreadHelper.JoinableTaskFactory.Run(async () => - { - if (!string.IsNullOrEmpty(ea.Data)) - { - await outputPane.WriteLineAsync($"ERROR: {ea.Data}"); - } - }); - } - - private void Process_OutputDataReceived(object sender, DataReceivedEventArgs ea) - { - ThreadHelper.JoinableTaskFactory.Run(async () => - { - try - { - if (!string.IsNullOrEmpty(ea.Data)) - { - await outputPane.WriteLineAsync(ea.Data); - } - } - catch { } - }); - - } - - protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) - { - await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - await DoCmdAsync(); - } - protected abstract Task DoCmdAsync(); - protected static OutputWindowPane outputPane = null; - protected async Task EnsureOutputPaneAsync() - { - - if (outputPane == null) - { - var guid = VSConstants.GUID_BuildOutputWindowPane; - outputPane = await VS.Windows.GetOutputWindowPaneAsync(guid); - } - } - protected override void BeforeQueryStatus(EventArgs e) - { - base.BeforeQueryStatus(e); - ThreadHelper.JoinableTaskFactory.Run(CheckAvailabilityAsync); - } - protected async Task CheckAvailabilityAsync() - { - Command.Visible = await Commands.ProjectIsXSharpProjectAsync(); - if (Command.Visible) - { - var project = await VS.Solutions.GetActiveProjectAsync(); - if (project == null) - { - Command.Visible = false; - Command.Enabled = false; - return; - } - var path = project.FullPath; - var prj = XSharpProjectNode.FindProject(path); - // Only show for SDK-style projects as they support dotnet pack/publish - Command.Visible = prj != null && prj.IsSdkProject; - Command.Enabled = Command.Visible; - } - } - - - } - class BuildCommandFilter : OLE.IOleCommandTarget - { - static readonly Guid PackGuid = new Guid("{568ABDF7-D522-474D-9EED-34B5E5095BA5}"); - - public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, - OLE.OLECMD[] prgCmds, IntPtr pCmdText) - { - bool isXSharp = false; - ThreadHelper.JoinableTaskFactory.Run(async delegate - { - - isXSharp = await Commands.ProjectIsXSharpProjectAsync(); - }); - if (!isXSharp) - { - return VSConstants.S_OK; - } - var mustCheck = false; - var cmd = prgCmds[0].cmdID; - if (pguidCmdGroup == CommandPack.CommandGroup && cmd == CommandPack.CommandID) - mustCheck = true; - else if (pguidCmdGroup == CommandPublish.CommandGroup && cmd == CommandPublish.CommandID) - mustCheck = true; - if (mustCheck) - { - bool show = false; - ThreadHelper.JoinableTaskFactory.Run(async delegate - { - show = await Commands.ProjectIsXSharpSdkProjectAsync(); - }); - prgCmds[0].cmdf = show - ? (uint)(OLE.OLECMDF.OLECMDF_SUPPORTED | OLE.OLECMDF.OLECMDF_ENABLED) - : (uint)(OLE.OLECMDF.OLECMDF_SUPPORTED | OLE.OLECMDF.OLECMDF_INVISIBLE); - return VSConstants.S_OK; - } - return (int) OLE.Constants.OLECMDERR_E_NOTSUPPORTED; - } - - public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, - IntPtr pvaIn, IntPtr pvaOut) - => (int)OLE.Constants.OLECMDERR_E_NOTSUPPORTED; - } -} diff --git a/src/VisualStudio/ProjectPackage/Commands/CommandConvertXsRuntime.cs b/src/VisualStudio/ProjectPackage/Commands/CommandConvertXsRuntime.cs index 40a15bb796..0ab1c4dbca 100644 --- a/src/VisualStudio/ProjectPackage/Commands/CommandConvertXsRuntime.cs +++ b/src/VisualStudio/ProjectPackage/Commands/CommandConvertXsRuntime.cs @@ -24,7 +24,7 @@ private async Task CheckAvailabilityAsync() var project = await VS.Solutions.GetActiveProjectAsync(); var path = project.FullPath; var prj = XSharpProjectNode.FindProject(path); - Command.Visible = prj != null && !prj.IsSdkProject; + Command.Visible = prj != null; } } diff --git a/src/VisualStudio/ProjectPackage/Commands/CommandEditProjectFile.cs b/src/VisualStudio/ProjectPackage/Commands/CommandEditProjectFile.cs index 7611ad3f8e..51c9273b52 100644 --- a/src/VisualStudio/ProjectPackage/Commands/CommandEditProjectFile.cs +++ b/src/VisualStudio/ProjectPackage/Commands/CommandEditProjectFile.cs @@ -36,7 +36,8 @@ protected override void BeforeQueryStatus(EventArgs e) } private async Task CheckAvailabilityAsync() { - Command.Visible = await Commands.ProjectIsXSharpProjectAsync(); + // CPS projects have their own "Edit Project File" command (capability OpenProjectFile) + Command.Visible = await Commands.ProjectIsMpfProjectAsync(); } protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) { @@ -54,9 +55,6 @@ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) var projectNode = XSharpProjectNode.FindProject(project.FullPath); if (projectNode != null && projectNode.QueryEditProjectFile(true)) { -#if DEV17 - projectNode.ClearSdkProjectReferences(); -#endif projectNode.SetProjectFileDirty(false); Directory.CreateDirectory(tempProjectDir); var prjFile = project.FullPath; diff --git a/src/VisualStudio/ProjectPackage/Commands/CommandGenerateWinForm.cs b/src/VisualStudio/ProjectPackage/Commands/CommandGenerateWinForm.cs index 57169be525..54c876907c 100644 --- a/src/VisualStudio/ProjectPackage/Commands/CommandGenerateWinForm.cs +++ b/src/VisualStudio/ProjectPackage/Commands/CommandGenerateWinForm.cs @@ -86,16 +86,19 @@ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) } var newdesignerform = Path.ChangeExtension(newform, ".designer.prg"); var x = await project.AddExistingFilesAsync(new[] { newform, newdesignerform }); - var prjNode = (XSharpProjectNode) xproject.ProjectNode; - var file = prjNode.FindChild(newform); - if (file is XSharpFileNode fileNode && fileNode.IsLink) + // MPFproj only: remove the Link metadata. CPS projects are not an XSharpProjectNode. + if (xproject.ProjectNode is XSharpProjectNode prjNode) { - file.ItemNode.SetMetadata("Link", null); - } - file = prjNode.FindChild(newdesignerform); - if (file is XSharpFileNode designerfileNode && designerfileNode.IsLink) - { - file.ItemNode.SetMetadata("Link", null); + var file = prjNode.FindChild(newform); + if (file is XSharpFileNode fileNode && fileNode.IsLink) + { + file.ItemNode.SetMetadata("Link", null); + } + file = prjNode.FindChild(newdesignerform); + if (file is XSharpFileNode designerfileNode && designerfileNode.IsLink) + { + file.ItemNode.SetMetadata("Link", null); + } } diff --git a/src/VisualStudio/ProjectPackage/Commands/CommandPack.cs b/src/VisualStudio/ProjectPackage/Commands/CommandPack.cs deleted file mode 100644 index 5b918ffbb3..0000000000 --- a/src/VisualStudio/ProjectPackage/Commands/CommandPack.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Community.VisualStudio.Toolkit; - -using System; -using System.IO; - -using Task = System.Threading.Tasks.Task; - -namespace XSharp.Project -{ - [Command(PackageIds.idPackProject)] - internal sealed class CommandPack : CommandBuild - { - protected override string CommandName => "Build.PackSelection"; - protected override string CommandDescription => "Pack"; - public static readonly Guid CommandGroup = new Guid("{568ABDF7-D522-474D-9EED-34B5E5095BA5}"); - public const int CommandID = 8193; - - protected override async Task DoCmdAsync() - { - if (!await VerifySdkProjectAsync()) - { - return; - } - // Show confirmation dialog - var result = await VS.MessageBox.ShowAsync( - "Create NuGet Package", - "This will create a NuGet package from the project using 'dotnet pack'.\n\n" + - "The package will be created in the project's bin folder.\n\n" + - "Continue?", - Microsoft.VisualStudio.Shell.Interop.OLEMSGICON.OLEMSGICON_QUERY, - Microsoft.VisualStudio.Shell.Interop.OLEMSGBUTTON.OLEMSGBUTTON_OKCANCEL); - - if (result == Microsoft.VisualStudio.VSConstants.MessageBoxResult.IDOK) - { - await PackProjectAsync(projectPath); - } - } - - private async Task PackProjectAsync(string projectPath) - { - try - { - await VS.StatusBar.ShowMessageAsync("Creating NuGet package..."); - - // Build dotnet pack command - var arguments = $"pack \"{projectPath}\" -c Release"; - - var result = await CreateProcessAsync(arguments, - $"Creating NuGet package for: {Path.GetFileName(projectPath)}"); - - if (result == 0) - { - string packagePath = null; - var message = "NuGet package created successfully."; - if (!string.IsNullOrEmpty(packagePath)) - { - message += $"\n\nPackage location:\n{packagePath}"; - } - await VS.MessageBox.ShowAsync(CommandDescription, - message, - Microsoft.VisualStudio.Shell.Interop.OLEMSGICON.OLEMSGICON_INFO, - Microsoft.VisualStudio.Shell.Interop.OLEMSGBUTTON.OLEMSGBUTTON_OK); - } - } - catch (Exception ex) - { - await VS.MessageBox.ShowErrorAsync(CommandDescription+" Error", $"Failed to create NuGet package:\n{ex.Message}"); - } - } - } -} diff --git a/src/VisualStudio/ProjectPackage/Commands/CommandPublish.cs b/src/VisualStudio/ProjectPackage/Commands/CommandPublish.cs deleted file mode 100644 index e1bbb1a89b..0000000000 --- a/src/VisualStudio/ProjectPackage/Commands/CommandPublish.cs +++ /dev/null @@ -1,244 +0,0 @@ -using Community.VisualStudio.Toolkit; - -using Microsoft.VisualStudio; -using Microsoft.VisualStudio.Project; - -using Newtonsoft.Json; - -using System; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; - -using Task = System.Threading.Tasks.Task; - -namespace XSharp.Project -{ - - - - [Command(PackageIds.idPublishProject)] - internal sealed class CommandPublish : CommandBuild - { - protected override string CommandName => "Build.PublishSelection"; - protected override string CommandDescription => "Publish"; - - public const int CommandID = (int)2005; // ECMD_PUBLISHSELECTION - public static readonly Guid CommandGroup = VsMenus.guidStandardCommandSet2K; - - protected override async Task DoCmdAsync() - { - if (!await VerifySdkProjectAsync()) - { - return; - } - - // Show publish dialog to get options - var dialog = new PublishDialog(projectPath); - var result = dialog.ShowDialog(); - - if (result == true) - { - await PublishProjectAsync(projectPath, dialog.PublishOptions); - } - } - - private async Task PublishProjectAsync(string projectPath, PublishOptions options) - { - try - { - await VS.StatusBar.ShowMessageAsync("Publishing project..."); - - var arguments = BuildPublishArguments(projectPath, options); - options.Save(); - var result = await CreateProcessAsync(arguments, - $"Publishing project: {Path.GetFileName(projectPath)}"); - - if (result == 0) - { - await VS.MessageBox.ShowAsync(CommandDescription, - $"{CommandDescription} published successfully to:\n{options.OutputPath}", - Microsoft.VisualStudio.Shell.Interop.OLEMSGICON.OLEMSGICON_INFO, - Microsoft.VisualStudio.Shell.Interop.OLEMSGBUTTON.OLEMSGBUTTON_OK); - } - } - catch (Exception ex) - { - await VS.MessageBox.ShowErrorAsync(CommandDescription+" Error", $"Failed to publish project:\n{ex.Message}"); - } - } - - private string BuildPublishArguments(string projectPath, PublishOptions options) - { - var args = $"publish \"{projectPath}\""; - - if (!string.IsNullOrEmpty(options.Configuration)) - { - args += $" -c {options.Configuration}"; - } - - if (!string.IsNullOrEmpty(options.TargetFramework)) - { - args += $" -f {options.TargetFramework}"; - } - - if (!string.IsNullOrEmpty(options.Runtime)) - { - args += $" -r {options.Runtime}"; - } - - if (!string.IsNullOrEmpty(options.OutputPath)) - { - args += $" -o \"{options.OutputPath}\""; - } - - if (options.SelfContained.HasValue) - { - args += options.SelfContained.Value ? " --self-contained" : " --no-self-contained"; - } - - if (options.SingleFile) - { - args += " -p:PublishSingleFile=true"; - } - - if (options.ReadyToRun) - { - args += " -p:PublishReadyToRun=true"; - } - - if (options.Trimmed) - { - args += " -p:PublishTrimmed=true"; - } - - return args; - } - } - - public class PublishOptions : INotifyPropertyChanged - { - private string _projectDir; - private string _projectName; - private string _jsonFile; - private string _configuration = "Release"; - private string _targetFramework; - private string _runtime; - private string _outputPath; - private bool? _selfContained; - private bool _singleFile; - private bool _readyToRun; - private bool _trimmed; - - public event PropertyChangedEventHandler PropertyChanged; - - public PublishOptions() - { - - } - public PublishOptions(string projectPath) - { - // Set default output path based on project location - _projectDir = Path.GetDirectoryName(projectPath); - _projectName = Path.GetFileNameWithoutExtension(projectPath); - OutputPath = Path.Combine(_projectDir, "bin", "publish", _projectName); - _jsonFile = Path.Combine(_projectDir, $"{_projectName}.publishsettings.json"); - var project = XSharpProjectNode.FindProject( projectPath ) as XSharpSdkProjectNode; - if (project != null) - { - TargetFramework = project.TargetFrameworks.FirstOrDefault(); - Runtime = "win-x86"; // Default runtime, could be improved to detect from project - } - } - - public string Configuration - { - get => _configuration; - set => SetProperty(ref _configuration, value); - } - - public string TargetFramework - { - get => _targetFramework; - set => SetProperty(ref _targetFramework, value); - } - - public string Runtime - { - get => _runtime; - set => SetProperty(ref _runtime, value); - } - - public string OutputPath - { - get => _outputPath; - set => SetProperty(ref _outputPath, value); - } - - public bool? SelfContained - { - get => _selfContained; - set => SetProperty(ref _selfContained, value); - } - - public bool SingleFile - { - get => _singleFile; - set => SetProperty(ref _singleFile, value); - } - - public bool ReadyToRun - { - get => _readyToRun; - set => SetProperty(ref _readyToRun, value); - } - - public bool Trimmed - { - get => _trimmed; - set => SetProperty(ref _trimmed, value); - } - - protected void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - protected bool SetProperty(ref T field, T value, [CallerMemberName] string propertyName = null) - { - if (Equals(field, value)) return false; - field = value; - OnPropertyChanged(propertyName); - return true; - } - - public void Save() - { - var str = JsonConvert.SerializeObject(this, Formatting.Indented); - System.IO.File.WriteAllText(_jsonFile, str); - - - } - public void Restore() - { - if (System.IO.File.Exists(_jsonFile)) - { - var str = System.IO.File.ReadAllText(_jsonFile); - var options = JsonConvert.DeserializeObject(str); - if (options != null) - { - Configuration = options.Configuration; - TargetFramework = options.TargetFramework; - Runtime = options.Runtime; - OutputPath = options.OutputPath; - SelfContained = options.SelfContained; - SingleFile = options.SingleFile; - ReadyToRun = options.ReadyToRun; - Trimmed = options.Trimmed; - } - } - - } - } -} diff --git a/src/VisualStudio/ProjectPackage/Commands/CommandSyncDesignerChanges.cs b/src/VisualStudio/ProjectPackage/Commands/CommandSyncDesignerChanges.cs index e03c6133ba..959579cdd7 100644 --- a/src/VisualStudio/ProjectPackage/Commands/CommandSyncDesignerChanges.cs +++ b/src/VisualStudio/ProjectPackage/Commands/CommandSyncDesignerChanges.cs @@ -11,7 +11,7 @@ using System.Linq; using System.Threading.Tasks; -using XSharp.Project.ShadowDesigner; +using XSharp.ProjectSystem.ShadowDesigner; using XSharpModel; namespace XSharp.Project @@ -26,12 +26,14 @@ namespace XSharp.Project [Command(PackageIds.idSyncDesignerChanges)] internal sealed class CommandSyncDesignerChanges : BaseCommand { - private XSharpFileNode _currentFile; + private string _currentPath; + private XProject _currentProject; protected override void BeforeQueryStatus(EventArgs e) { base.BeforeQueryStatus(e); - _currentFile = null; + _currentPath = null; + _currentProject = null; ThreadHelper.JoinableTaskFactory.Run(CheckAvailabilityAsync); } @@ -43,14 +45,13 @@ private async Task CheckAvailabilityAsync() { if (item is PhysicalFile file) { - var project = await VS.Solutions.GetActiveProjectAsync(); - var xproject = project != null ? XSolution.FindProject(project.FullPath, "") : null; - if (xproject?.ProjectNode is XSharpProjectNode prjNode && - prjNode.FindChild(file.FullPath) is XSharpFileNode fileNode && - fileNode.HasDesigner && - prjNode is XSharpSdkProjectNode) + // SDK-style projects, loaded by the CPS project system + var xproject = XSolution.FindFile(file.FullPath)?.Project; + bool sdkProject = ShadowDesignerBridge.IsCpsProject(xproject); + if (sdkProject && ShadowDesignerBridge.HasDesignerFile(file.FullPath)) { - _currentFile = fileNode; + _currentPath = file.FullPath; + _currentProject = xproject; visible = true; } } @@ -62,11 +63,11 @@ private async Task CheckAvailabilityAsync() protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - if (_currentFile == null) return; + if (_currentPath == null) return; await VS.Commands.ExecuteAsync(KnownCommands.File_SaveAll); - if (!ShadowDesignerBridge.TryResolveCompanionPaths(_currentFile, out var location, out string error)) + if (!ShadowDesignerBridge.TryResolveCompanionPaths(_currentPath, _currentProject, out var location, out string error)) { await VS.MessageBox.ShowErrorAsync("X# WinForms Designer", error); return; diff --git a/src/VisualStudio/ProjectPackage/Commands/CommandSyncEventHandlers.cs b/src/VisualStudio/ProjectPackage/Commands/CommandSyncEventHandlers.cs index da28fd8c2d..9cf9fe1d3e 100644 --- a/src/VisualStudio/ProjectPackage/Commands/CommandSyncEventHandlers.cs +++ b/src/VisualStudio/ProjectPackage/Commands/CommandSyncEventHandlers.cs @@ -10,7 +10,7 @@ using System; using System.Threading.Tasks; -using XSharp.Project.ShadowDesigner; +using XSharp.ProjectSystem.ShadowDesigner; using XSharpModel; namespace XSharp.Project @@ -25,12 +25,14 @@ namespace XSharp.Project [Command(PackageIds.idSyncEventHandlers)] internal sealed class CommandSyncEventHandlers : BaseCommand { - private XSharpFileNode _currentFile; + private string _currentPath; + private XProject _currentProject; protected override void BeforeQueryStatus(EventArgs e) { base.BeforeQueryStatus(e); - _currentFile = null; + _currentPath = null; + _currentProject = null; ThreadHelper.JoinableTaskFactory.Run(CheckAvailabilityAsync); } @@ -42,14 +44,13 @@ private async Task CheckAvailabilityAsync() { if (item is PhysicalFile file) { - var project = await VS.Solutions.GetActiveProjectAsync(); - var xproject = project != null ? XSolution.FindProject(project.FullPath, "") : null; - if (xproject?.ProjectNode is XSharpProjectNode prjNode && - prjNode.FindChild(file.FullPath) is XSharpFileNode fileNode && - fileNode.HasDesigner && - prjNode is XSharpSdkProjectNode) + // SDK-style projects, loaded by the CPS project system + var xproject = XSolution.FindFile(file.FullPath)?.Project; + bool sdkProject = ShadowDesignerBridge.IsCpsProject(xproject); + if (sdkProject && ShadowDesignerBridge.HasDesignerFile(file.FullPath)) { - _currentFile = fileNode; + _currentPath = file.FullPath; + _currentProject = xproject; visible = true; } } @@ -61,13 +62,13 @@ private async Task CheckAvailabilityAsync() protected override async Task ExecuteAsync(OleMenuCmdEventArgs e) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); - if (_currentFile == null) return; + if (_currentPath == null) return; // The Designer-added stub/wiring can sit unsaved in an open document buffer -- // both sync steps read the companion files from disk, not the live buffer. await VS.Commands.ExecuteAsync(KnownCommands.File_SaveAll); - if (!ShadowDesignerBridge.TryResolveCompanionPaths(_currentFile, out var location, out string error)) + if (!ShadowDesignerBridge.TryResolveCompanionPaths(_currentPath, _currentProject, out var location, out string error)) { await VS.MessageBox.ShowErrorAsync("X# WinForms Designer", error); return; diff --git a/src/VisualStudio/ProjectPackage/Commands/PublishDialog.xaml b/src/VisualStudio/ProjectPackage/Commands/PublishDialog.xaml deleted file mode 100644 index 5f995c110c..0000000000 --- a/src/VisualStudio/ProjectPackage/Commands/PublishDialog.xaml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -