diff --git a/src/NuGet.Config b/src/NuGet.Config index 6a6b25e19d..1458db55b2 100644 --- a/src/NuGet.Config +++ b/src/NuGet.Config @@ -4,6 +4,7 @@ + diff --git a/src/VSIntegration2022.sln b/src/VSIntegration2022.sln index bc753ca6fb..1da52e1b7a 100644 --- a/src/VSIntegration2022.sln +++ b/src/VSIntegration2022.sln @@ -33,6 +33,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger2022", "VisualStudi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectPackage2022", "VisualStudio\ProjectPackage\ProjectPackage2022.csproj", "{F4ADEBDB-CD20-4532-B4E3-FE7424F5369D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XSharp.ProjectSystem2022", "VisualStudio\ProjectSystem\XSharp.ProjectSystem2022.csproj", "{AB494DCE-A11B-4583-851E-D4A86775B3F0}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Vs2022", "Vs2022", "{0D3C69B5-D236-42B3-B70D-2C9ABC9D434C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{9F285506-97D3-486E-B59E-7C30C56B95AE}" @@ -133,6 +135,18 @@ Global {F4ADEBDB-CD20-4532-B4E3-FE7424F5369D}.Release|arm64.Build.0 = Release|arm64 {F4ADEBDB-CD20-4532-B4E3-FE7424F5369D}.Release|x86.ActiveCfg = Release|x86 {F4ADEBDB-CD20-4532-B4E3-FE7424F5369D}.Release|x86.Build.0 = Release|x86 + {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Debug|arm64.ActiveCfg = Debug|Any CPU + {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Debug|arm64.Build.0 = Debug|Any CPU + {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Debug|x86.ActiveCfg = Debug|Any CPU + {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Debug|x86.Build.0 = Debug|Any CPU + {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Release|Any CPU.Build.0 = Release|Any CPU + {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Release|arm64.ActiveCfg = Release|Any CPU + {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Release|arm64.Build.0 = Release|Any CPU + {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Release|x86.ActiveCfg = Release|Any CPU + {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Release|x86.Build.0 = Release|Any CPU {B837AC3B-9C61-49E4-9F4F-C8AD1FAF21CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B837AC3B-9C61-49E4-9F4F-C8AD1FAF21CC}.Debug|Any CPU.Build.0 = Debug|Any CPU {B837AC3B-9C61-49E4-9F4F-C8AD1FAF21CC}.Debug|arm64.ActiveCfg = Debug|Any CPU @@ -228,6 +242,7 @@ Global {4484939E-DDA8-4033-8B1C-E8F60A2BAFE1} = {0D3C69B5-D236-42B3-B70D-2C9ABC9D434C} {16272550-75E6-4B9D-91E3-653BD141C756} = {0D3C69B5-D236-42B3-B70D-2C9ABC9D434C} {F4ADEBDB-CD20-4532-B4E3-FE7424F5369D} = {0D3C69B5-D236-42B3-B70D-2C9ABC9D434C} + {AB494DCE-A11B-4583-851E-D4A86775B3F0} = {0D3C69B5-D236-42B3-B70D-2C9ABC9D434C} {B837AC3B-9C61-49E4-9F4F-C8AD1FAF21CC} = {9F285506-97D3-486E-B59E-7C30C56B95AE} {7619DB88-6286-4EF1-8273-2A08CC8AC70E} = {9F285506-97D3-486E-B59E-7C30C56B95AE} {EED6843B-B727-4F08-AA32-CA0F48FDDE3F} = {9F285506-97D3-486E-B59E-7C30C56B95AE} diff --git a/src/VisualStudio/ProjectSystem/Properties/AssemblyInfo.cs b/src/VisualStudio/ProjectSystem/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..59dde995bc --- /dev/null +++ b/src/VisualStudio/ProjectSystem/Properties/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General information +[assembly: AssemblyTitle("XSharp CPS Project System")] +[assembly: AssemblyDescription("CPS-based XSharp project system for Visual Studio 2022 and later")] +[assembly: AssemblyCompany("XSharp B.V.")] +[assembly: AssemblyProduct("XSharp")] +[assembly: AssemblyCopyright("Copyright © XSharp B.V. 2024")] +[assembly: ComVisible(false)] diff --git a/src/VisualStudio/ProjectSystem/PropertyPages/XSharpBuildPropertyPageProvider.cs b/src/VisualStudio/ProjectSystem/PropertyPages/XSharpBuildPropertyPageProvider.cs new file mode 100644 index 0000000000..99fb3bb8b6 --- /dev/null +++ b/src/VisualStudio/ProjectSystem/PropertyPages/XSharpBuildPropertyPageProvider.cs @@ -0,0 +1,38 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// + +using System.ComponentModel.Composition; + +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +namespace XSharp.VisualStudio.ProjectSystem.PropertyPages +{ + /// + /// CPS property page metadata for the XSharp "Build" page. + /// + /// This page surfaces the MSBuild properties defined in + /// Rules/XSharp.xaml under the Build rule category. + /// It mirrors XSharpBuildPropertyPage in the MPF AppDesigner. + /// + [Export(typeof(IPageMetadata))] + [AppliesTo(XSharpProjectCapabilities.XSharp)] + internal sealed class XSharpBuildPropertyPageProvider : IPageMetadata + { + /// + public string Name => "Build"; + + /// + public System.Guid PageGuid => + new System.Guid(XSharpConstants.BuildPropertiesPage); + + /// + public int PageOrder => 200; + + /// + public bool HasConfigurationCondition => true; + } +} diff --git a/src/VisualStudio/ProjectSystem/PropertyPages/XSharpDebugPropertyPageProvider.cs b/src/VisualStudio/ProjectSystem/PropertyPages/XSharpDebugPropertyPageProvider.cs new file mode 100644 index 0000000000..234140ea9e --- /dev/null +++ b/src/VisualStudio/ProjectSystem/PropertyPages/XSharpDebugPropertyPageProvider.cs @@ -0,0 +1,38 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// + +using System.ComponentModel.Composition; + +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.ProjectSystem.VS.Properties; + +namespace XSharp.VisualStudio.ProjectSystem.PropertyPages +{ + /// + /// CPS property page metadata for the XSharp "Debug" page. + /// + /// This page surfaces the MSBuild properties defined in + /// Rules/XSharp.xaml under the Debug rule category. + /// It mirrors XSharpDebugPropertyPage in the MPF AppDesigner. + /// + [Export(typeof(IPageMetadata))] + [AppliesTo(XSharpProjectCapabilities.XSharp)] + internal sealed class XSharpDebugPropertyPageProvider : IPageMetadata + { + /// + public string Name => "Debug"; + + /// + public System.Guid PageGuid => + new System.Guid(XSharpConstants.DebugPropertiesPage); + + /// + public int PageOrder => 300; + + /// + public bool HasConfigurationCondition => true; + } +} diff --git a/src/VisualStudio/ProjectSystem/PropertyPages/XSharpGeneralPropertyPageProvider.cs b/src/VisualStudio/ProjectSystem/PropertyPages/XSharpGeneralPropertyPageProvider.cs new file mode 100644 index 0000000000..472fac43c7 --- /dev/null +++ b/src/VisualStudio/ProjectSystem/PropertyPages/XSharpGeneralPropertyPageProvider.cs @@ -0,0 +1,41 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// + +using System.ComponentModel.Composition; +using System.Runtime.InteropServices; + +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.ProjectSystem.VS.Properties; +using Microsoft.VisualStudio.Shell; + +namespace XSharp.VisualStudio.ProjectSystem.PropertyPages +{ + /// + /// CPS property page metadata for the XSharp "Application" (General) page. + /// + /// This page surfaces the MSBuild properties defined in + /// Rules/XSharp.xaml under the Application rule category. + /// It mirrors the information shown by XSharpGeneralPropertyPage + /// in the MPF-based AppDesigner. + /// + [Export(typeof(IPageMetadata))] + [AppliesTo(XSharpProjectCapabilities.XSharp)] + internal sealed class XSharpGeneralPropertyPageProvider : IPageMetadata + { + /// + public string Name => "Application"; + + /// + public System.Guid PageGuid => + new System.Guid(XSharpConstants.GeneralPropertiesPage); + + /// + public int PageOrder => 100; + + /// + public bool HasConfigurationCondition => false; + } +} diff --git a/src/VisualStudio/ProjectSystem/README.md b/src/VisualStudio/ProjectSystem/README.md new file mode 100644 index 0000000000..2fa3457a50 --- /dev/null +++ b/src/VisualStudio/ProjectSystem/README.md @@ -0,0 +1,66 @@ +# XSharp CPS Project System + +This folder contains the **CPS-based (Common Project System)** implementation of the XSharp project system for Visual Studio 2022 and later. + +## Overview + +| Path | Purpose | +|---|---| +| `XSharp.ProjectSystem2022.csproj` | Project file (net472, VS2022 SDK references) | +| `XSharpProjectCapabilities.cs` | CPS capability constants (`XSharp`, `Managed`, `MultipleTargetFrameworks`, …) | +| `XSharpProjectCapabilitiesProvider.cs` | MEF export that advertises capabilities to CPS | +| `XSharpProjectType.cs` | Binds the `.xsproj` extension and project GUID to this CPS project system | +| `XSharpProjectTreePropertiesProvider.cs` | Custom icons for `.xsfrm`, `.xsmnu`, `.prg`, `.xaml`, etc. | +| `XSharpProjectBuildProvider.cs` | Build-property integration + up-to-date check stub | +| `XSharpProjectProperties.cs` | Strongly-typed wrappers around MSBuild project properties | +| `XSharpTargetFrameworkProvider.cs` | Multi-targeting support (VS2022+ only — reads ``) | +| `XSharpCodeDomIntegration.cs` | Bridge to the existing XSharp CodeDom provider for Windows Forms designer | +| `PropertyPages/*.cs` | CPS `IPageMetadata` exports for Application, Build, and Debug pages | +| `Rules/XSharp.xaml` | MSBuild property rules (XAML format) surfaced in the project designer | +| `Rules/XSharpMultiTarget.xaml` | `TargetFrameworks` rule for multi-targeting (VS2022+ only) | +| `XSharpCpsPackage.cs` | VS AsyncPackage that registers CPS components | +| `XSharpCpsPackage.pkgdef` | Registry entries that activate CPS for `.xsproj` in VS2022+ | + +## VS Version Strategy + +| Feature | VS2019 | VS2022 / VS2026 | +|---|---|---| +| Project system | **MPF** (`ProjectBase` + `ProjectPackage`) | **CPS** (this assembly) | +| Multi-targeting | ❌ not supported | ✅ via `` | +| VO binary editors (`.xsfrm`, `.xsmnu`, …) | ✅ | ✅ | +| CodeDom / WinForms designer | ✅ | ✅ | +| Project designer pages | ✅ AppDesigner | ✅ AppDesigner + CPS rules | + +The MPF code in `ProjectBase` and `ProjectPackage` is **not modified**. The CPS package activates only in VS2022+ via the `ProjectSystemPackage` registry entry in `XSharpCpsPackage.pkgdef`. + +## Multi-Targeting (VS2022+ only) + +SDK-style `.xsproj` files can use `` to target multiple frameworks simultaneously: + +```xml + + + net48;net6.0-windows + Core + + +``` + +> **Note:** The VS2019 / MPF path does **not** support multi-targeting. +> Use a single `` value when VS2019 compatibility is required. + +## Custom Editors + +The VO binary file editors (`.xsfrm`, `.xsmnu`, `.xsdbs`, `.xsfs`) are implemented in the `ProjectPackage` assembly and registered both there (for MPF) and in `XSharpCpsPackage.pkgdef` (for CPS). No code duplication is required — the factory implementations are shared. + +## CodeDom / Windows Forms + +`XSharpCodeDomIntegration` locates `VSXsharpCodeDomProvider` in the `ProjectPackage` assembly at run-time and exposes it through the MEF `ICodeDomProviderIntegration` interface so the Windows Forms designer can generate XSharp source code in the CPS context. + +## Building + +This project is included in `src/VSIntegration2022.sln`. It is **not** included in `src/VSIntegration.sln` (VS2019). + +``` +msbuild src/VSIntegration2022.sln /p:Configuration=Release +``` diff --git a/src/VisualStudio/ProjectSystem/Rules/XSharp.xaml b/src/VisualStudio/ProjectSystem/Rules/XSharp.xaml new file mode 100644 index 0000000000..9ae664be22 --- /dev/null +++ b/src/VisualStudio/ProjectSystem/Rules/XSharp.xaml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/VisualStudio/ProjectSystem/Rules/XSharpMultiTarget.xaml b/src/VisualStudio/ProjectSystem/Rules/XSharpMultiTarget.xaml new file mode 100644 index 0000000000..54ee2afbd5 --- /dev/null +++ b/src/VisualStudio/ProjectSystem/Rules/XSharpMultiTarget.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + diff --git a/src/VisualStudio/ProjectSystem/XSharp.ProjectSystem2022.csproj b/src/VisualStudio/ProjectSystem/XSharp.ProjectSystem2022.csproj new file mode 100644 index 0000000000..843376060b --- /dev/null +++ b/src/VisualStudio/ProjectSystem/XSharp.ProjectSystem2022.csproj @@ -0,0 +1,164 @@ + + + + + + + + Library + XSharp.VisualStudio.ProjectSystem + XSharp.ProjectSystem2022 + v4.8 + win + {AB494DCE-A11B-4583-851E-D4A86775B3F0} + AnyCPU + true + + XSharp\Project + v3 + false + False + true + + + + true + full + false + TRACE;DEBUG;XSHARP;DEV17 + false + false + 1762;VSSDK006;NU1903 + + + + pdbonly + true + XSHARP;TRACE;DEV17 + false + VSTHRD010;VSSDK006;1762;NU1903 + + + + + + CommonAssemblyInfo.cs + + + Constants.cs + + + XSharpConstants.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + MSBuild:GenerateRuleSourceFromXaml + MSBuild + + + Designer + MSBuild:GenerateRuleSourceFromXaml + MSBuild + + + + + + + false + PreserveNewest + + + + + + + + 17.10.60 + + + 17.10.60 + + + + + 17.10.40171 + + + 17.10.40171 + + + + + 17.0.551 + + + + + 17.10.48 + + + 17.10.48 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + 17.10.46 + + + + + 10.0.5 + + + + + + + {eed6843b-b727-4f08-aa32-ca0f48fdde3f} + CodeModel + + + + {5891B814-A2E0-4e64-9A2F-2C2ECAB940FE} + ProjectPackage2022 + + + + + diff --git a/src/VisualStudio/ProjectSystem/XSharpCodeDomIntegration.cs b/src/VisualStudio/ProjectSystem/XSharpCodeDomIntegration.cs new file mode 100644 index 0000000000..8519f4d4ad --- /dev/null +++ b/src/VisualStudio/ProjectSystem/XSharpCodeDomIntegration.cs @@ -0,0 +1,87 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// + +using System.CodeDom.Compiler; +using System.ComponentModel.Composition; +using System.Runtime.InteropServices; + +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.Shell; + +// The XSharp CodeDom provider assembly is produced by ProjectPackage. +// We use a type-forward so this assembly does not need a hard dependency on the +// internal VSXsharpCodeDomProvider type; instead we resolve it via the VS service +// provider at runtime. + +namespace XSharp.VisualStudio.ProjectSystem +{ + /// + /// Bridges the XSharp CodeDom provider into the CPS project system so that the + /// Windows Forms designer can generate XSharp source code. + /// + /// In the MPF path the CodeDom provider is registered via + /// [ProvideCodeDomProvider] attributes on the package. The CPS path + /// additionally needs to export the provider through MEF so that CPS design-time + /// build services can locate it. + /// + /// The actual implementation lives in + /// XSharp.Project.VSXsharpCodeDomProvider (ProjectPackage assembly). + /// This class locates and delegates to it at run-time, keeping the two assemblies + /// loosely coupled. + /// + [Export(typeof(ICodeDomProviderIntegration))] + [AppliesTo(XSharpProjectCapabilities.XSharp)] + internal sealed class XSharpCodeDomIntegration : ICodeDomProviderIntegration + { + private readonly UnconfiguredProject _project; + + [ImportingConstructor] + public XSharpCodeDomIntegration(UnconfiguredProject project) + { + _project = project; + } + + /// + /// Returns an instance of the XSharp for use + /// by the Windows Forms code generator. + /// + /// The provider is obtained from the VS service provider so that it uses the + /// same registration path as the MPF-based implementation. + /// + public CodeDomProvider GetCodeDomProvider() + { + // Resolve the provider type by name to avoid a hard assembly reference. + // The XSharp.Project.VSXsharpCodeDomProvider class is registered in the + // global VS MEF container by the ProjectPackage assembly. + const string providerTypeName = + "XSharp.Project.VSXsharpCodeDomProvider, XSharp.ProjectPackage2022"; + + System.Type providerType = System.Type.GetType(providerTypeName); + + if (providerType != null && + System.Activator.CreateInstance(providerType) is CodeDomProvider provider) + { + return provider; + } + + // Fallback: return a C# provider to avoid hard failures. + // In practice this branch should never be reached when the ProjectPackage + // assembly is loaded. + return CodeDomProvider.CreateProvider("CSharp"); + } + } + + /// + /// Marker interface used by to allow + /// MEF discovery without pulling in Windows Forms or CodeDom namespaces at + /// the interface declaration site. + /// + public interface ICodeDomProviderIntegration + { + /// Gets the for XSharp source generation. + CodeDomProvider GetCodeDomProvider(); + } +} diff --git a/src/VisualStudio/ProjectSystem/XSharpCpsPackage.cs b/src/VisualStudio/ProjectSystem/XSharpCpsPackage.cs new file mode 100644 index 0000000000..8cf82d8aa4 --- /dev/null +++ b/src/VisualStudio/ProjectSystem/XSharpCpsPackage.cs @@ -0,0 +1,166 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +using Community.VisualStudio.Toolkit; + +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +using Task = System.Threading.Tasks.Task; + +namespace XSharp.VisualStudio.ProjectSystem +{ + /// + /// VS Package that registers the CPS-based XSharp project system. + /// + /// This package is loaded only in VS2022 and later (enforced by the + /// MinimumVisualStudioVersion entry in the VSIX manifest and by the + /// [$RootKey$\Projects\...] registry entry in + /// .pkgdef, which is only deployed for VS2022+). + /// + /// For VS2019 the existing MPF-based XSharpProjectPackage continues to + /// handle .xsproj files unchanged. + /// + /// Responsibilities + /// ──────────────── + /// • Ensures the CPS MEF composition is bootstrapped before any project is loaded. + /// • Registers the VO binary editor factories so they work in the CPS context. + /// (The factories themselves live in the ProjectPackage assembly; we just make + /// sure they are registered when the CPS package loads.) + /// • Exposes a VS version check so other components can query whether CPS is + /// the active project system. + /// + [Description(XSharpConstants.ProjectSystemName + " (CPS)")] + [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] + [ProvideAutoLoad( + VSConstants.UICONTEXT.SolutionExists_string, + PackageAutoLoadFlags.BackgroundLoad)] + [Guid(XSharpConstants.guidCpsProjectTypeString)] + // ── Project type registration ────────────────────────────────────────────── + // The .xsproj extension is bound to the CPS project type via the .pkgdef file. + // We also keep the VO editor extensions registered here for VS2022+. + [ProvideEditorExtension( + editorType: typeof(XSharpCpsPackage), // placeholder — real factory is in ProjectPackage + extension: ".xsfrm", + priority: 0x42, + DefaultName = "XSharp VO Form Editor", + NameResourceID = 80110)] + [ProvideEditorExtension( + editorType: typeof(XSharpCpsPackage), + extension: ".xsmnu", + priority: 0x42, + DefaultName = "XSharp VO Menu Editor", + NameResourceID = 80111)] + [ProvideEditorExtension( + editorType: typeof(XSharpCpsPackage), + extension: ".xsdbs", + priority: 0x42, + DefaultName = "XSharp VO DbServer Editor", + NameResourceID = 80112)] + [ProvideEditorExtension( + editorType: typeof(XSharpCpsPackage), + extension: ".xsfs", + priority: 0x42, + DefaultName = "XSharp VO FieldSpec Editor", + NameResourceID = 80113)] + public sealed class XSharpCpsPackage : AsyncPackage + { + // ─── VS version threshold ──────────────────────────────────────────────────── + + /// + /// The minimum VS version that activates the CPS project system. + /// VS2022 = 17.x. + /// + private const int MinCpsMajorVersion = 17; + + private static bool? s_isCpsEnabled; + + /// + /// Returns true when the host VS instance is new enough to use the + /// CPS-based project system (VS2022, version 17.0 or later). + /// + public static bool IsCpsEnabled + { + get + { + if (s_isCpsEnabled.HasValue) + return s_isCpsEnabled.Value; + + // Default to false until the version check succeeds. + s_isCpsEnabled = false; + + try + { + // The reliable check is the VS shell release version string. + var shell = ServiceProvider.GlobalProvider.GetService( + typeof(SVsShell)) as IVsShell; + if (shell != null) + { + shell.GetProperty( + (int)__VSSPROPID5.VSSPROPID_ReleaseVersion, + out object versionObj); + if (versionObj is string versionStr && + System.Version.TryParse( + versionStr.Split(' ')[0], out System.Version vsVersion)) + { + s_isCpsEnabled = vsVersion.Major >= MinCpsMajorVersion; + } + } + } + catch + { + s_isCpsEnabled = false; + } + + return s_isCpsEnabled.Value; + } + } + + // ─── Package initialisation ────────────────────────────────────────────────── + + /// + protected override async Task InitializeAsync( + CancellationToken cancellationToken, + IProgress progress) + { + await base.InitializeAsync(cancellationToken, progress); + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + // Guard: only do CPS-specific work when running in VS2022+. + if (!IsCpsEnabled) + return; + + // Log successful initialisation. + XSettings.Logger.Information( + $"[XSharpCpsPackage] CPS project system initialized " + + $"(VS {GetVsVersionString()})."); + } + + private static string GetVsVersionString() + { + try + { + var shell = ServiceProvider.GlobalProvider.GetService(typeof(SVsShell)) as IVsShell; + if (shell != null) + { + shell.GetProperty( + (int)__VSSPROPID5.VSSPROPID_ReleaseVersion, + out object v); + return v?.ToString() ?? "unknown"; + } + } + catch { } + return "unknown"; + } + } +} diff --git a/src/VisualStudio/ProjectSystem/XSharpCpsPackage.pkgdef b/src/VisualStudio/ProjectSystem/XSharpCpsPackage.pkgdef new file mode 100644 index 0000000000..666d5c51fb --- /dev/null +++ b/src/VisualStudio/ProjectSystem/XSharpCpsPackage.pkgdef @@ -0,0 +1,87 @@ +// XSharpCpsPackage.pkgdef +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// +// Registry entries required to activate the CPS-based XSharp project system +// in Visual Studio 2022 and later. +// +// These entries are deployed as part of the XSharp VSIX and are installed under +// the VS private registry hive. They are NOT written for VS2019, where the +// MPF-based XSharpProjectPackage remains responsible for .xsproj files. +// +// Substitution variables used below: +// $RootKey$ – VS private registry root (e.g. Software\Microsoft\VisualStudio\17.0_...) +// $PackageFolder$ – folder where this .pkgdef lives (the VSIX install dir) +// +// ─── Project type → CPS package binding ───────────────────────────────────── +// +// Tell VS that the XSharp project type (GUID aa6c8d78-...) should be handled by +// the CPS project system package (GUID AB494DCE-...) rather than the legacy MPF +// package. This entry is the key that switches .xsproj loading from MPF to CPS. + +[$RootKey$\Projects\{aa6c8d78-22ff-423a-9c7c-5f2393824e04}] +"Language"="XSharp" +"DisplayName"="XSharp Project" +"Package"="{AB494DCE-A11B-4583-851E-D4A86775B3F0}" +"ProjectSystemPackage"="{AB494DCE-A11B-4583-851E-D4A86775B3F0}" +"DefaultProjectExtension"="xsproj" + +// ─── Project file extension → project type ────────────────────────────────── +// Map .xsproj to the XSharp project type GUID so VS opens the right project system. + +[$RootKey$\Projects\{aa6c8d78-22ff-423a-9c7c-5f2393824e04}\Extensions] +"xsproj"="{aa6c8d78-22ff-423a-9c7c-5f2393824e04}" + +// ─── CPS project type capabilities ────────────────────────────────────────── +// CPS reads project capabilities from the MSBuild Sdk; this entry provides a +// fallback for cases where the project file has not yet been fully evaluated. + +[$RootKey$\Projects\{aa6c8d78-22ff-423a-9c7c-5f2393824e04}\Capabilities] +@="XSharp Managed CSharp ProjectReferences AssemblyReferences PackageReferences" + +// ─── VO / XSharp binary editor registrations (VS2022+) ────────────────────── +// The VO binary editors are registered in XSharpProjectPackage for MPF. Repeat +// the registrations here so that they are also active when the CPS package is the +// primary project system package. + +[$RootKey$\Editors\{fc9f8e69-f338-4fa6-aca3-aa41e445849a}] +@="XSharp VO Form Editor" + +[$RootKey$\Editors\{fc9f8e69-f338-4fa6-aca3-aa41e445849a}\Extensions] +"xsfrm"=dword:00000042 +"vnfrm"=dword:00000042 + +[$RootKey$\Editors\{fc9f8e69-f338-4fa6-aca3-aa41e445849a}\LogicalViews] +"{7651a703-06e5-11d1-8ebd-00a0c90f26ea}"="" + +[$RootKey$\Editors\{e4ae4582-98ae-40c8-9e48-6f3be61ebf79}] +@="XSharp VO Menu Editor" + +[$RootKey$\Editors\{e4ae4582-98ae-40c8-9e48-6f3be61ebf79}\Extensions] +"xsmnu"=dword:00000042 +"vnmnu"=dword:00000042 + +[$RootKey$\Editors\{e4ae4582-98ae-40c8-9e48-6f3be61ebf79}\LogicalViews] +"{7651a703-06e5-11d1-8ebd-00a0c90f26ea}"="" + +[$RootKey$\Editors\{c7e4c5f6-98b8-4826-9000-6b9b94dc2d97}] +@="XSharp VO DbServer Editor" + +[$RootKey$\Editors\{c7e4c5f6-98b8-4826-9000-6b9b94dc2d97}\Extensions] +"xsdbs"=dword:00000042 +"vndbs"=dword:00000042 + +[$RootKey$\Editors\{c7e4c5f6-98b8-4826-9000-6b9b94dc2d97}\LogicalViews] +"{7651a703-06e5-11d1-8ebd-00a0c90f26ea}"="" + +[$RootKey$\Editors\{8c5d0bae-7a69-437b-ad0e-1e1b89721ebd}] +@="XSharp VO FieldSpec Editor" + +[$RootKey$\Editors\{8c5d0bae-7a69-437b-ad0e-1e1b89721ebd}\Extensions] +"xsfs"=dword:00000042 +"vnfs"=dword:00000042 + +[$RootKey$\Editors\{8c5d0bae-7a69-437b-ad0e-1e1b89721ebd}\LogicalViews] +"{7651a703-06e5-11d1-8ebd-00a0c90f26ea}"="" diff --git a/src/VisualStudio/ProjectSystem/XSharpProjectBuildProvider.cs b/src/VisualStudio/ProjectSystem/XSharpProjectBuildProvider.cs new file mode 100644 index 0000000000..ac25808579 --- /dev/null +++ b/src/VisualStudio/ProjectSystem/XSharpProjectBuildProvider.cs @@ -0,0 +1,102 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// + +using System.Collections.Immutable; +using System.ComponentModel.Composition; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.ProjectSystem.Build; + +namespace XSharp.VisualStudio.ProjectSystem +{ + /// + /// Provides XSharp-specific MSBuild build properties to the CPS build pipeline. + /// + /// The properties exposed here augment what CPS reads automatically from the + /// project file, adding XSharp dialect and language options that CPS would + /// otherwise not know about. + /// + [Export(typeof(IBuildPropertiesProvider))] + [AppliesTo(XSharpProjectCapabilities.XSharp)] + internal sealed class XSharpProjectBuildProvider : IBuildPropertiesProvider + { + // Reserved for future use: inject or transform per-project build properties + // (e.g. dialect, include paths) at build time. + // private readonly ConfiguredProject _configuredProject; + + [ImportingConstructor] + public XSharpProjectBuildProvider(ConfiguredProject configuredProject) + { + // configuredProject reserved for future build-property injection. + } + + /// + public Task> GetBuildPropertiesAsync( + BuildPropertyContext context, + CancellationToken cancellationToken) + { + // Return an empty dictionary — the actual XSharp build properties + // (Dialect, VOCompatibility options, etc.) are already expressed in the + // .xsproj MSBuild file and are picked up by CPS automatically. + // Override this method to inject or transform properties at build time + // if needed in the future. + IImmutableDictionary empty = + ImmutableDictionary.Empty; + + return Task.FromResult(empty); + } + } + + /// + /// Implements a fast "up-to-date" check for XSharp projects, telling VS that the + /// project needs a full rebuild only when its source or project file has actually + /// changed. + /// + /// This is a lightweight placeholder implementation. It currently delegates to + /// CPS's default behaviour (always returns false so that MSBuild performs + /// the real incremental check). A full implementation would inspect timestamps of + /// .prg / .xs files and compare them against output assembly timestamps. + /// + [Export(typeof(IBuildUpToDateCheckProvider))] + [AppliesTo(XSharpProjectCapabilities.XSharp)] + [ExportMetadata("Name", "XSharpUpToDateCheck")] + internal sealed class XSharpUpToDateCheckProvider : IBuildUpToDateCheckProvider + { + // Reserved for future use: compare source timestamps against output assembly + // to implement a fast "up-to-date" check without invoking MSBuild. + // private readonly ConfiguredProject _configuredProject; + + [ImportingConstructor] + public XSharpUpToDateCheckProvider(ConfiguredProject configuredProject) + { + // configuredProject reserved for future up-to-date timestamp checking. + } + + /// + /// + /// Returns false to defer to MSBuild's own incremental build logic. + /// Set to an explanatory message when + /// returning false so that the Output window explains why a rebuild + /// was triggered. + /// + public Task IsUpToDateAsync( + BuildAction buildAction, + TextWriter logger, + CancellationToken cancellationToken = default) + { + // Defer to MSBuild for incremental build decisions. + return Task.FromResult(false); + } + + /// + public Task IsUpToDateCheckEnabledAsync(CancellationToken cancellationToken = default) + { + return Task.FromResult(true); + } + } +} diff --git a/src/VisualStudio/ProjectSystem/XSharpProjectCapabilities.cs b/src/VisualStudio/ProjectSystem/XSharpProjectCapabilities.cs new file mode 100644 index 0000000000..803fb2ab13 --- /dev/null +++ b/src/VisualStudio/ProjectSystem/XSharpProjectCapabilities.cs @@ -0,0 +1,64 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// + +namespace XSharp.VisualStudio.ProjectSystem +{ + /// + /// Well-known CPS project capability strings advertised by the XSharp project system. + /// These are consumed by MEF components that use [AppliesTo] to scope themselves to + /// XSharp projects only. + /// + internal static class XSharpProjectCapabilities + { + // ─── XSharp-specific ──────────────────────────────────────────────────────── + + /// + /// The primary capability that identifies an XSharp project. + /// All XSharp-specific MEF exports should use [AppliesTo(XSharp)]. + /// + public const string XSharp = nameof(XSharp); + + // ─── Standard CPS capabilities ────────────────────────────────────────────── + + /// Marks this as a managed (.NET) project. + public const string Managed = ProjectCapabilities.Managed; + + /// + /// Reuses the C# capability string so the Roslyn language service (which + /// already supports XSharp via its own MEF exports) can be associated with + /// this project type without extra plumbing. + /// + public const string CSharp = ProjectCapabilities.CSharp; + + /// The project supports project-to-project references. + public const string ProjectReferences = ProjectCapabilities.ProjectReferences; + + /// The project supports raw assembly (DLL) references. + public const string AssemblyReferences = ProjectCapabilities.AssemblyReferences; + + /// The project supports NuGet package references. + public const string PackageReferences = ProjectCapabilities.PackageReferences; + + /// The project exposes output groups (for VSIX packaging, etc.). + public const string OutputGroups = ProjectCapabilities.OutputGroups; + + /// All target output groups are available. + public const string AllTargetOutputGroups = ProjectCapabilities.AllTargetOutputGroups; + + /// The project supports multi-targeting via <TargetFrameworks>. + /// + /// Multi-targeting is a VS2022+ / CPS-only feature. + /// The MPF-based path (VS2019) does NOT support this capability. + /// + public const string MultipleTargetFrameworks = ProjectCapabilities.MultipleTargetFrameworks; + + /// The project supports pre-/post-build events. + public const string BuildEvents = nameof(BuildEvents); + + /// The project supports source control operations. + public const string SourceControlled = ProjectCapabilities.SourceControlled; + } +} diff --git a/src/VisualStudio/ProjectSystem/XSharpProjectCapabilitiesProvider.cs b/src/VisualStudio/ProjectSystem/XSharpProjectCapabilitiesProvider.cs new file mode 100644 index 0000000000..4b9a0a6399 --- /dev/null +++ b/src/VisualStudio/ProjectSystem/XSharpProjectCapabilitiesProvider.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// + +using System.Collections.Immutable; +using System.ComponentModel.Composition; + +using Microsoft.VisualStudio.ProjectSystem; + +namespace XSharp.VisualStudio.ProjectSystem +{ + /// + /// Exports the set of CPS capabilities that every XSharp project advertises. + /// CPS uses capabilities to decide which MEF components apply to a given project. + /// + [Export(typeof(IProjectCapabilitiesProvider))] + [AppliesTo(XSharpProjectCapabilities.XSharp)] + internal sealed class XSharpProjectCapabilitiesProvider : IProjectCapabilitiesProvider + { + private static readonly ImmutableHashSet s_capabilities = + ImmutableHashSet.Create( + XSharpProjectCapabilities.XSharp, + XSharpProjectCapabilities.Managed, + XSharpProjectCapabilities.CSharp, + XSharpProjectCapabilities.ProjectReferences, + XSharpProjectCapabilities.AssemblyReferences, + XSharpProjectCapabilities.PackageReferences, + XSharpProjectCapabilities.OutputGroups, + XSharpProjectCapabilities.AllTargetOutputGroups, + XSharpProjectCapabilities.MultipleTargetFrameworks, + XSharpProjectCapabilities.BuildEvents, + XSharpProjectCapabilities.SourceControlled); + + /// + public IImmutableSet GetProjectCapabilities(IProjectTree projectTree) => + s_capabilities; + } +} diff --git a/src/VisualStudio/ProjectSystem/XSharpProjectProperties.cs b/src/VisualStudio/ProjectSystem/XSharpProjectProperties.cs new file mode 100644 index 0000000000..518ff7fe50 --- /dev/null +++ b/src/VisualStudio/ProjectSystem/XSharpProjectProperties.cs @@ -0,0 +1,181 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// + +using System.ComponentModel.Composition; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.ProjectSystem.Properties; + +namespace XSharp.VisualStudio.ProjectSystem +{ + /// + /// Provides strongly-typed access to the XSharp-specific MSBuild project + /// properties that are defined in . + /// + /// CPS exposes project properties through the + /// / abstraction. This class wraps that + /// abstraction and adds convenience methods for the XSharp build options. + /// + [Export] + [AppliesTo(XSharpProjectCapabilities.XSharp)] + internal sealed class XSharpProjectProperties + { + private readonly ConfiguredProject _project; + + [ImportingConstructor] + public XSharpProjectProperties(ConfiguredProject project) + { + _project = project; + } + + // ─── Helper ───────────────────────────────────────────────────────────────── + + private IProjectProperties GetCommonProperties() + { + return _project.Services.ProjectPropertiesProvider + .GetCommonProperties(); + } + + private IProjectProperties GetConfigurationProperties() + { + // Retrieve properties with a per-configuration context so that + // configuration-specific values (e.g. OutputPath, DefineConstants) are + // read from the active configuration rather than the common project properties. + var context = ProjectPropertiesContext.GetContext( + _project, + propertyPageSchemaElement: null, + itemType: null, + itemName: null); + return _project.Services.ProjectPropertiesProvider + .GetProperties(context.File, context.ItemType, context.ItemName); + } + + // ─── Application properties ────────────────────────────────────────────────── + + public Task GetAssemblyNameAsync(CancellationToken ct = default) => + GetCommonProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.AssemblyName); + + public Task GetOutputTypeAsync(CancellationToken ct = default) => + GetCommonProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.OutputType); + + public Task GetRootNamespaceAsync(CancellationToken ct = default) => + GetCommonProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.RootNamespace); + + public Task GetStartupObjectAsync(CancellationToken ct = default) => + GetCommonProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.StartupObject); + + public Task GetApplicationIconAsync(CancellationToken ct = default) => + GetCommonProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.ApplicationIcon); + + // ─── Target framework ──────────────────────────────────────────────────────── + + /// Single-target framework (e.g. net48). + public Task GetTargetFrameworkAsync(CancellationToken ct = default) => + GetCommonProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.TargetFramework); + + /// + /// Semi-colon-separated list of target frameworks for multi-targeting + /// (e.g. net48;net6.0-windows). VS2022+ / CPS only. + /// + public Task GetTargetFrameworksAsync(CancellationToken ct = default) => + GetCommonProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.TargetFrameworks); + + // ─── XSharp dialect / language options ────────────────────────────────────── + + public Task GetDialectAsync(CancellationToken ct = default) => + GetCommonProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.Dialect); + + public Task GetDefineConstantsAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.DefineConstants); + + public Task GetIncludePathsAsync(CancellationToken ct = default) => + GetCommonProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.IncludePaths); + + public Task GetWarningLevelAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.WarningLevel); + + public Task GetNoWarnAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.NoWarn); + + public Task GetTreatWarningsAsErrorsAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.TreatWarningsAsErrors); + + // ─── Build / output ────────────────────────────────────────────────────────── + + public Task GetOutputPathAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.OutputPath); + + public Task GetPlatformTargetAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.PlatformTarget); + + public Task GetOptimizeAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.Optimize); + + public Task GetDocumentationFileAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.DocumentationFile); + + // ─── Debug ─────────────────────────────────────────────────────────────────── + + public Task GetDebugTypeAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.DebugType); + + public Task GetDebuggerCommandAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.DebuggerCommand); + + public Task GetDebuggerCommandArgumentsAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.DebuggerCommandArguments); + + public Task GetDebuggerWorkingDirectoryAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.DebuggerWorkingDirectory); + + // ─── Signing ───────────────────────────────────────────────────────────────── + + public Task GetSignAssemblyAsync(CancellationToken ct = default) => + GetCommonProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.SignAssembly); + + public Task GetAssemblyOriginatorKeyFileAsync(CancellationToken ct = default) => + GetCommonProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.AssemblyOriginatorKeyFile); + + // ─── Build events ──────────────────────────────────────────────────────────── + + public Task GetPreBuildEventAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.PreBuildEvent); + + public Task GetPostBuildEventAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.PostBuildEvent); + + public Task GetRunPostBuildEventAsync(CancellationToken ct = default) => + GetConfigurationProperties().GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.RunPostBuildEvent); + } +} diff --git a/src/VisualStudio/ProjectSystem/XSharpProjectTreePropertiesProvider.cs b/src/VisualStudio/ProjectSystem/XSharpProjectTreePropertiesProvider.cs new file mode 100644 index 0000000000..836165db5b --- /dev/null +++ b/src/VisualStudio/ProjectSystem/XSharpProjectTreePropertiesProvider.cs @@ -0,0 +1,97 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// + +using System.Collections.Immutable; +using System.ComponentModel.Composition; + +using Microsoft.VisualStudio.Imaging; +using Microsoft.VisualStudio.Imaging.Interop; +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.ProjectSystem.Tree; + +namespace XSharp.VisualStudio.ProjectSystem +{ + /// + /// Provides custom icons and tree-node flags for files inside an XSharp project. + /// + /// This mirrors the icon mapping that the MPF XSharpFileNode performs via + /// MapExtensionsToMoniker, ensuring a consistent look across both project + /// system implementations. + /// + [Export(typeof(IProjectTreePropertiesProvider))] + [AppliesTo(XSharpProjectCapabilities.XSharp)] + [Order(100)] + internal sealed class XSharpProjectTreePropertiesProvider : IProjectTreePropertiesProvider + { + // Extension → moniker map built once and reused for every tree change. + private static readonly ImmutableDictionary s_extensionMonikers = + new Dictionary(System.StringComparer.OrdinalIgnoreCase) + { + // XSharp source files + { ".prg", KnownMonikers.Script }, + { ".xs", KnownMonikers.Script }, + { ".ppo", KnownMonikers.Script }, + { ".vh", KnownMonikers.Script }, + { ".xh", KnownMonikers.Script }, + { ".ch", KnownMonikers.Script }, + + // VO / XSharp binary editors + { ".xsfrm", KnownMonikers.FormInstance }, + { ".vnfrm", KnownMonikers.FormInstance }, + { ".xsmnu", KnownMonikers.MainMenuControl }, + { ".vnmnu", KnownMonikers.MainMenuControl }, + { ".xsdbs", KnownMonikers.Database }, + { ".vndbs", KnownMonikers.Database }, + { ".xsfs", KnownMonikers.ValidationRule }, + { ".vnfs", KnownMonikers.ValidationRule }, + { ".xsrep", KnownMonikers.Report }, + { ".vnrep", KnownMonikers.Report }, + { ".xssql", KnownMonikers.DatabaseScript }, + { ".vnsqs", KnownMonikers.DatabaseScript }, + + // WPF + { ".xaml", KnownMonikers.WPFFile }, + } + .ToImmutableDictionary(System.StringComparer.OrdinalIgnoreCase); + + /// + public void CalculatePropertyValues( + IProjectTreeCustomizationContext context, + IProjectTreePropertiesUpdate update) + { + // Only process file nodes that have a known extension. + if (!context.Node.Flags.Contains(ProjectTreeFlags.FileOnDisk)) + return; + + string extension = System.IO.Path.GetExtension(context.Node.FilePath); + if (string.IsNullOrEmpty(extension)) + return; + + if (s_extensionMonikers.TryGetValue(extension, out ImageMoniker moniker)) + { + update.Icon = moniker.ToProjectSystemType(); + update.ExpandedIcon = moniker.ToProjectSystemType(); + } + + // Mark XSharp source files as proper source files so features like + // "Go to definition" and code-model scanning are activated. + if (IsXSharpSource(extension)) + { + update.Flags = update.Flags + .Add(ProjectTreeFlags.SourceFile) + .Add(ProjectTreeFlags.FileSystemEntity); + } + } + + private static bool IsXSharpSource(string extension) => + extension.Equals(".prg", System.StringComparison.OrdinalIgnoreCase) || + extension.Equals(".xs", System.StringComparison.OrdinalIgnoreCase) || + extension.Equals(".ppo", System.StringComparison.OrdinalIgnoreCase) || + extension.Equals(".vh", System.StringComparison.OrdinalIgnoreCase) || + extension.Equals(".xh", System.StringComparison.OrdinalIgnoreCase) || + extension.Equals(".ch", System.StringComparison.OrdinalIgnoreCase); + } +} diff --git a/src/VisualStudio/ProjectSystem/XSharpProjectType.cs b/src/VisualStudio/ProjectSystem/XSharpProjectType.cs new file mode 100644 index 0000000000..38e4478331 --- /dev/null +++ b/src/VisualStudio/ProjectSystem/XSharpProjectType.cs @@ -0,0 +1,32 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// + +using System.ComponentModel.Composition; + +using Microsoft.VisualStudio.ProjectSystem; + +namespace XSharp.VisualStudio.ProjectSystem +{ + /// + /// Binds the XSharp project type (GUID aa6c8d78-22ff-423a-9c7c-5f2393824e04) + /// and the .xsproj file extension to this CPS-based project system. + /// + /// The causes CPS to hand any + /// .xsproj file that declares the XSharp project-type GUID to this + /// project system rather than the legacy MPF factory. For VS2019 support, + /// the registry key ProjectSystemPackage under the project-type root + /// is only written for VS2022+ installations (see XSharpCpsPackage.pkgdef). + /// + [Export] + [ExportProjectType( + projectTypeGuid: XSharpConstants.guidXSharpProjectFactoryString, + displayName: XSharpConstants.LanguageName, + projectFileExtension: XSharpConstants.ProjectExtension, + defaultProjectCapabilities: XSharpProjectCapabilities.XSharp)] + internal sealed class XSharpProjectType + { + } +} diff --git a/src/VisualStudio/ProjectSystem/XSharpTargetFrameworkProvider.cs b/src/VisualStudio/ProjectSystem/XSharpTargetFrameworkProvider.cs new file mode 100644 index 0000000000..1ce204d43e --- /dev/null +++ b/src/VisualStudio/ProjectSystem/XSharpTargetFrameworkProvider.cs @@ -0,0 +1,98 @@ +// +// Copyright (c) XSharp B.V. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. +// See License.txt in the project root for license information. +// + +using System.Collections.Immutable; +using System.ComponentModel.Composition; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.VisualStudio.ProjectSystem; +using Microsoft.VisualStudio.ProjectSystem.Build; + +namespace XSharp.VisualStudio.ProjectSystem +{ + /// + /// Hooks into CPS multi-targeting support for XSharp SDK-style projects. + /// + /// When a project file contains <TargetFrameworks>net48;net6.0-windows</TargetFrameworks> + /// CPS automatically creates one per framework. + /// This provider participates in that mechanism by advertising the list of + /// active target frameworks so that VS can show the framework picker and build + /// all targets on Rebuild. + /// + /// This capability is VS2022+ / CPS only. + /// The MPF-based VS2019 path does not support multi-targeting. + /// + [Export(typeof(IActiveConfiguredProjectsDimensionProvider))] + [AppliesTo(XSharpProjectCapabilities.XSharp)] + [ExportMetadata("DimensionName", ConfigurationGeneral.TargetFrameworkProperty)] + internal sealed class XSharpTargetFrameworkProvider + : IActiveConfiguredProjectsDimensionProvider + { + private readonly UnconfiguredProject _unconfiguredProject; + private readonly IProjectPropertiesProvider _propertiesProvider; + + [ImportingConstructor] + public XSharpTargetFrameworkProvider( + UnconfiguredProject unconfiguredProject, + [Import(ExportContractNames.ProjectPropertyProviders.ProjectFile)] + IProjectPropertiesProvider propertiesProvider) + { + _unconfiguredProject = unconfiguredProject; + _propertiesProvider = propertiesProvider; + } + + /// + /// + /// Returns the set of target frameworks declared in the project. + /// When only <TargetFramework> (singular) is set, a single entry is + /// returned. When <TargetFrameworks> (plural) is set, all entries + /// from the semicolon-separated list are returned. + /// + /// CPS uses this list to create one slice per + /// framework, enabling simultaneous multi-targeted builds. + /// + public async Task> GetDefaultValuesForDimensionsAsync( + CancellationToken cancellationToken) + { + IProjectProperties props = _propertiesProvider.GetCommonProperties(); + + // Prefer the plural TargetFrameworks property first. + string multiTarget = await props.GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.TargetFrameworks).ConfigureAwait(false); + + if (!string.IsNullOrWhiteSpace(multiTarget)) + { + var frameworks = multiTarget + .Split(';') + .Select(f => f.Trim()) + .Where(f => !string.IsNullOrEmpty(f)) + .ToImmutableHashSet(System.StringComparer.OrdinalIgnoreCase); + + if (frameworks.Count > 0) + return frameworks; + } + + // Fall back to the singular TargetFramework. + string singleTarget = await props.GetEvaluatedPropertyValueAsync( + XSharpProjectFileConstants.TargetFramework).ConfigureAwait(false); + + if (!string.IsNullOrWhiteSpace(singleTarget)) + return ImmutableHashSet.Create( + System.StringComparer.OrdinalIgnoreCase, singleTarget.Trim()); + + // No framework declared — return the default. + return ImmutableHashSet.Create( + System.StringComparer.OrdinalIgnoreCase, "net48"); + } + + /// + public string DimensionName => ConfigurationGeneral.TargetFrameworkProperty; + + /// + public bool ShouldBeVisibleInConfigurationUI => true; + } +}