Skip to content

Commit

Permalink
Added support for converting all projects in a solution, and converti…
Browse files Browse the repository at this point in the history
…ng multiple selected projects.

See #2
  • Loading branch information
icnocop committed Nov 1, 2024
1 parent b7a79ca commit ea01a15
Show file tree
Hide file tree
Showing 14 changed files with 362 additions and 66 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ Thank you! :)

# Change Log

## v1.0.1102.17 (November 2<sup>nd</sup>, 2024)
- Fixed [#2](https://github.com/icnocop/PackageReferenceVersionToAttribute/issues/2) - Added support for converting all projects in a solution, and converting multiple selected projects.

## v1.0.1101.15 (November 1<sup>st</sup>, 2024)
- Fixed [#1](https://github.com/icnocop/PackageReferenceVersionToAttribute/issues/1) - Fixed issue when converting a project with an XML namespace.

Expand Down
6 changes: 6 additions & 0 deletions src/DemoLibrary.sln → src/Demo.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ VisualStudioVersion = 17.11.35327.3
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DemoLibrary", "DemoLibrary\DemoLibrary.csproj", "{6EB23B91-905C-4E2B-9C6F-5F438960C0D1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DemoConsoleApp", "DemoConsoleApp\DemoConsoleApp.csproj", "{318114D0-D1E6-48A2-8BB6-96FDB43FB15E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -15,6 +17,10 @@ Global
{6EB23B91-905C-4E2B-9C6F-5F438960C0D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6EB23B91-905C-4E2B-9C6F-5F438960C0D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6EB23B91-905C-4E2B-9C6F-5F438960C0D1}.Release|Any CPU.Build.0 = Release|Any CPU
{318114D0-D1E6-48A2-8BB6-96FDB43FB15E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{318114D0-D1E6-48A2-8BB6-96FDB43FB15E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{318114D0-D1E6-48A2-8BB6-96FDB43FB15E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{318114D0-D1E6-48A2-8BB6-96FDB43FB15E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
10 changes: 10 additions & 0 deletions src/DemoConsoleApp/DemoConsoleApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
2 changes: 2 additions & 0 deletions src/DemoConsoleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="ConvertPackageReferenceVersionElementsToAttributesCommand.cs" company="Rami Abughazaleh">
// <copyright file="BaseCommand.cs" company="Rami Abughazaleh">
// Copyright (c) Rami Abughazaleh. All rights reserved.
// </copyright>

Expand All @@ -12,30 +12,29 @@ namespace PackageReferenceVersionToAttributeExtension
using System.Xml;
using System.Xml.Linq;
using Community.VisualStudio.Toolkit;
using Community.VisualStudio.Toolkit.DependencyInjection;
using Community.VisualStudio.Toolkit.DependencyInjection.Core;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using PackageReferenceVersionToAttributeExtension.Services;

/// <summary>
/// Convert PackageReference Version elements to attributes command.
/// Base command.
/// </summary>
[Command(
PackageGuids.guidPackageReferenceVersionToAttributeExtensionCmdSetString,
PackageIds.PackageReferenceVersionToAttributeCommand)]
internal sealed class ConvertPackageReferenceVersionElementsToAttributesCommand(
DIToolkitPackage package,
internal class BaseCommand(
LoggingService loggingService,
ProjectService projectService,
FileSystemService fileSystemService) : BaseDICommand(package)
FileSystemService fileSystemService)
{
private readonly LoggingService loggingService = loggingService;
private readonly ProjectService projectService = projectService;
private readonly FileSystemService fileSystemService = fileSystemService;

/// <inheritdoc/>
protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
/// <summary>
/// Converts all &lt;Version&gt; elements within &lt;PackageReference&gt; items in the project file
/// to attributes, ensuring that version information is consistently represented as an attribute.
/// This operation scans for &lt;PackageReference&gt; elements, identifies child &lt;Version&gt;
/// elements, and moves the version value to an attribute if found.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
internal async Task ConvertPackageReferenceVersionElementsToAttributesAsync()
{
try
{
Expand Down Expand Up @@ -67,6 +66,36 @@ protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
}
}

/// <summary>
/// Retrieves the projects currently selected in Solution Explorer.
/// This method identifies selected project nodes, as well as projects under a solution node if it is selected.
/// </summary>
/// <returns>A collection of distinct projects selected in Solution Explorer.</returns>
internal async Task<IEnumerable<Project>> GetSelectedProjectsAsync()
{
// Get the active items from Solution Explorer
var activeItems = await VS.Solutions.GetActiveItemsAsync();

// A HashSet to ensure unique projects are returned
var projects = new HashSet<Project>();

foreach (var item in activeItems)
{
if (item is Solution solution)
{
// Get all projects in the solution
projects.UnionWith(this.GetAllProjects(solution.Children));
}
else if (item is Project project)
{
// Directly add selected projects
projects.Add(project);
}
}

return projects;
}

private async Task ConvertPackageReferenceVersionElementsToAttributesAsync(IEnumerable<Community.VisualStudio.Toolkit.Project> projects)
{
foreach (var project in projects)
Expand Down Expand Up @@ -155,13 +184,34 @@ private async Task ConvertPackageReferenceVersionElementsToAttributesAsync(IEnum
}
}

private async Task<IEnumerable<Community.VisualStudio.Toolkit.Project>> GetSelectedProjectsAsync()
private IEnumerable<Project> GetAllProjects(
IEnumerable<SolutionItem> items)
{
// Get the active items from the Solution Explorer
var activeItems = await VS.Solutions.GetActiveItemsAsync();
var projects = new List<Project>();

foreach (var item in items)
{
this.AddProjectsRecursively(item, projects);
}

// Get the selected items of type Project
return activeItems.OfType<Community.VisualStudio.Toolkit.Project>();
return projects;
}

// Recursive method to gather projects from solution and solution folders
private void AddProjectsRecursively(SolutionItem solutionItem, List<Project> projects)
{
if (solutionItem is Project project)
{
projects.Add(project);
}
else if (solutionItem is SolutionFolder folder)
{
// Recursively add projects within each solution folder
foreach (var child in folder.Children)
{
this.AddProjectsRecursively(child, projects);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// <copyright file="MultipleProjectNodesCommand.cs" company="Rami Abughazaleh">
// Copyright (c) Rami Abughazaleh. All rights reserved.
// </copyright>

namespace PackageReferenceVersionToAttributeExtension
{
using System;
using System.Linq;
using System.Threading.Tasks;
using Community.VisualStudio.Toolkit;
using Community.VisualStudio.Toolkit.DependencyInjection;
using Community.VisualStudio.Toolkit.DependencyInjection.Core;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using PackageReferenceVersionToAttributeExtension.Services;

/// <summary>
/// Project node command.
/// </summary>
[Command(
PackageGuids.guidPackageReferenceVersionToAttributeExtensionCmdSetString,
PackageIds.PackageReferenceVersionToAttributeMultipleProjectNodesCommand)]
internal sealed class MultipleProjectNodesCommand(
DIToolkitPackage package,
LoggingService loggingService,
ProjectService projectService,
FileSystemService fileSystemService)
: BaseDICommand(package)
{
private readonly BaseCommand baseCommand = new(loggingService, projectService, fileSystemService);

/// <inheritdoc/>
protected override void BeforeQueryStatus(EventArgs e)
{
this.Package.JoinableTaskFactory.Run(async () =>
{
// Check the current selection in Solution Explorer
var selectedProjects = await this.baseCommand.GetSelectedProjectsAsync();

// Enable command if there are any C# projects selected
this.Command.Enabled = selectedProjects.Any(p => p.IsCapabilityMatch("CSharp"));
});
}

/// <inheritdoc/>
protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
{
await this.baseCommand.ConvertPackageReferenceVersionElementsToAttributesAsync();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// <copyright file="ProjectNodeCommand.cs" company="Rami Abughazaleh">
// Copyright (c) Rami Abughazaleh. All rights reserved.
// </copyright>

namespace PackageReferenceVersionToAttributeExtension
{
using System.Threading.Tasks;
using Community.VisualStudio.Toolkit;
using Community.VisualStudio.Toolkit.DependencyInjection;
using Community.VisualStudio.Toolkit.DependencyInjection.Core;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using PackageReferenceVersionToAttributeExtension.Services;

/// <summary>
/// Project node command.
/// </summary>
[Command(
PackageGuids.guidPackageReferenceVersionToAttributeExtensionCmdSetString,
PackageIds.PackageReferenceVersionToAttributeProjectNodeCommand)]
internal sealed class ProjectNodeCommand(
DIToolkitPackage package,
LoggingService loggingService,
ProjectService projectService,
FileSystemService fileSystemService)
: BaseDICommand(package)
{
private readonly BaseCommand baseCommand = new(loggingService, projectService, fileSystemService);

/// <inheritdoc/>
protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
{
await this.baseCommand.ConvertPackageReferenceVersionElementsToAttributesAsync();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// <copyright file="SolutionNodeCommand.cs" company="Rami Abughazaleh">
// Copyright (c) Rami Abughazaleh. All rights reserved.
// </copyright>

namespace PackageReferenceVersionToAttributeExtension
{
using System.Threading.Tasks;
using Community.VisualStudio.Toolkit;
using Community.VisualStudio.Toolkit.DependencyInjection;
using Community.VisualStudio.Toolkit.DependencyInjection.Core;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using PackageReferenceVersionToAttributeExtension.Services;

/// <summary>
/// Solution node command.
/// </summary>
[Command(
PackageGuids.guidPackageReferenceVersionToAttributeExtensionCmdSetString,
PackageIds.PackageReferenceVersionToAttributeSolutionNodeCommand)]
internal sealed class SolutionNodeCommand(
DIToolkitPackage package,
LoggingService loggingService,
ProjectService projectService,
FileSystemService fileSystemService)
: BaseDICommand(package)
{
private readonly BaseCommand baseCommand = new(loggingService, projectService, fileSystemService);

/// <inheritdoc/>
protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
{
await this.baseCommand.ConvertPackageReferenceVersionElementsToAttributesAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace PackageReferenceVersionToAttributeExtension
using EnvDTE;
using EnvDTE80;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using PackageReferenceVersionToAttributeExtension.Services;
using Task = System.Threading.Tasks.Task;
Expand All @@ -24,13 +25,21 @@ namespace PackageReferenceVersionToAttributeExtension
[InstalledProductRegistration(Vsix.Name, Vsix.Description, Vsix.Version)]
[ProvideMenuResource("Menus.ctmenu", 1)]
[Guid(PackageGuids.guidPackageReferenceVersionToAttributeExtensionString)]
[ProvideAutoLoad(VSConstants.UICONTEXT.NoSolution_string, PackageAutoLoadFlags.BackgroundLoad)]
[ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string, PackageAutoLoadFlags.BackgroundLoad)]
[ProvideUIContextRule(
contextGuid: PackageGuids.guidPackageReferenceVersionToAttributeExtensionUIRuleString,
contextGuid: PackageGuids.guidPackageReferenceVersionToAttributeExtensionProjectNodeUIRuleString,
name: "Csproj",
expression: "Csproj",
termNames: ["Csproj"],
termValues: ["ActiveProjectCapability:CSharp"])]
public sealed class PackageReferenceVersionToAttributeExtensionPackage : MicrosoftDIToolkitPackage<PackageReferenceVersionToAttributeExtensionPackage>
[ProvideUIContextRule(
contextGuid: PackageGuids.guidPackageReferenceVersionToAttributeExtensionSolutionNodeUIRuleString,
name: "Sln",
expression: "(SingleProject | MultipleProjects)",
termNames: ["SingleProject", "MultipleProjects"],
termValues: [VSConstants.UICONTEXT.SolutionHasSingleProject_string, VSConstants.UICONTEXT.SolutionHasMultipleProjects_string])]
public sealed class Package : MicrosoftDIToolkitPackage<Package>
{
/// <inheritdoc/>
protected override void InitializeServices(IServiceCollection services)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@
<DocumentationFile>bin\Release\PackageReferenceVersionToAttributeExtension.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="Commands\BaseCommand.cs" />
<Compile Include="Commands\MultipleProjectNodesCommand.cs" />
<Compile Include="Commands\SolutionNodeCommand.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Commands\ConvertPackageReferenceVersionElementsToAttributesCommand.cs" />
<Compile Include="PackageReferenceVersionToAttributeExtensionPackage.cs" />
<Compile Include="Commands\ProjectNodeCommand.cs" />
<Compile Include="Package.cs" />
<Compile Include="Services\FileSystemService.cs" />
<Compile Include="Services\LoggingService.cs" />
<Compile Include="Services\ProjectService.cs" />
Expand Down
Binary file modified src/PackageReferenceVersionToAttributeExtension/Resources/Icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 11 additions & 4 deletions src/PackageReferenceVersionToAttributeExtension/VSCommandTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,22 @@ internal sealed partial class PackageGuids
public const string guidPackageReferenceVersionToAttributeExtensionCmdSetString = "0c128779-7aa4-463a-82f5-8e6eb5cc0412";
public static Guid guidPackageReferenceVersionToAttributeExtensionCmdSet = new Guid(guidPackageReferenceVersionToAttributeExtensionCmdSetString);

public const string guidPackageReferenceVersionToAttributeExtensionUIRuleString = "e309983d-aa3b-4faa-a8db-96923c2c0d90";
public static Guid guidPackageReferenceVersionToAttributeExtensionUIRule = new Guid(guidPackageReferenceVersionToAttributeExtensionUIRuleString);
public const string guidPackageReferenceVersionToAttributeExtensionProjectNodeUIRuleString = "e309983d-aa3b-4faa-a8db-96923c2c0d90";
public static Guid guidPackageReferenceVersionToAttributeExtensionProjectNodeUIRule = new Guid(guidPackageReferenceVersionToAttributeExtensionProjectNodeUIRuleString);

public const string guidPackageReferenceVersionToAttributeExtensionSolutionNodeUIRuleString = "e56530ff-9e50-4309-b551-c273bd2aab84";
public static Guid guidPackageReferenceVersionToAttributeExtensionSolutionNodeUIRule = new Guid(guidPackageReferenceVersionToAttributeExtensionSolutionNodeUIRuleString);
}
/// <summary>
/// Helper class that encapsulates all CommandIDs uses across VS Package.
/// </summary>
internal sealed partial class PackageIds
{
public const int PackageReferenceVersionToAttributeMenuGroup = 0x0001;
public const int PackageReferenceVersionToAttributeCommand = 0x0100;
public const int ProjectNodeGroup = 0x0001;
public const int MultipleProjectNodesGroup = 0x0002;
public const int SolutionNodeGroup = 0x0003;
public const int PackageReferenceVersionToAttributeProjectNodeCommand = 0x0100;
public const int PackageReferenceVersionToAttributeMultipleProjectNodesCommand = 0x0101;
public const int PackageReferenceVersionToAttributeSolutionNodeCommand = 0x0102;
}
}
Loading

0 comments on commit ea01a15

Please sign in to comment.