Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,21 @@ dotnet_naming_style.begins_with__.capitalization = camel_case
dotnet_diagnostic.CA1014.severity = none
dotnet_diagnostic.CA2007.severity = none
dotnet_diagnostic.IDE0070.severity = none

# IDE0028: Simplify collection initialization
dotnet_diagnostic.IDE0028.severity = warning

# IDE0290: Use primary constructor
dotnet_diagnostic.IDE0290.severity = none

# IDE0300: Simplify collection initialization
dotnet_diagnostic.IDE0300.severity = warning

# IDE0301: Simplify collection initialization
dotnet_diagnostic.IDE0301.severity = warning

# IDE0303: Simplify collection initialization
dotnet_diagnostic.IDE0303.severity = warning

# IDE0305: Simplify collection initialization
dotnet_diagnostic.IDE0305.severity = warning
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ on:

jobs:
build:
runs-on: windows-2022
runs-on: windows-2025-vs2026

env:
Configuration: Release
DeployExtension: false

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6

- name: Build
run: dotnet build
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/publish-manually.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,24 @@ on:

jobs:
publish:
runs-on: windows-2022
runs-on: windows-2025-vs2026

env:
Configuration: Release
DeployExtension: false

steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
ref: ${{ github.event.inputs.version }}

- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
uses: microsoft/setup-msbuild@v3

- name: Publish
run: msbuild source/ProjectFilter/ProjectFilter.csproj /t:PublishToMarketplace /Restore /v:Minimal /p:BuildBeforePublish=true /p:PersonalAccessToken=${{ secrets.PUBLISHER_TOKEN }}
env:
# This workflow should be used when the `VsixPublisher` from the `Microsoft.VSSDK.BuildTools`
# package fails, so we'll use the `VsixPublisher` that is installed with Visual Studio.
VsixPublisher: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VSSDK\\VisualStudioIntegration\\Tools\\Bin\\VsixPublisher.exe"
VsixPublisher: "C:\\Program Files\\Microsoft Visual Studio\\18\\Enterprise\\VSSDK\\VisualStudioIntegration\\Tools\\Bin\\VsixPublisher.exe"
6 changes: 3 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ on:

jobs:
publish:
runs-on: windows-2022
runs-on: windows-2025-vs2026

env:
Configuration: Release
DeployExtension: false

steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v7

- name: Setup MSBuild
uses: actions/setup-dotnet@v5
uses: actions/setup-dotnet@v6

- name: Publish
working-directory: source/ProjectFilter
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 2.7.0 (2026-09-06)

- ✨ New option to hide folders that only contain unloaded projects and miscellaneous files.
-
# 2.6.1 (2025-08-19)

- 🔧 Dependency updates.
Expand Down
5 changes: 3 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<Project>

<PropertyGroup>
<Version>2.6.1</Version>
<Version>2.7.0</Version>
<TargetFramework>net48</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>10.0</LangVersion>
<LangVersion>latest</LangVersion>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>

</Project>
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"sdk": {
"version": "10.0.203"
"version": "10.0.400"
}
}
7 changes: 5 additions & 2 deletions sample/Small/Small.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.31911.260
# Visual Studio Version 18
VisualStudioVersion = 18.10.12120.281
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "source", "source", "{8BB688F8-2B48-4692-9DFC-A199435AB13D}"
EndProject
Expand All @@ -17,6 +17,9 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beta", "source\Beta\Beta.csproj", "{00020190-70EA-4CC3-B3C1-CBD9720BD7A7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gamma", "Gamma", "{2715BEFA-EFBC-4AF8-966F-2395B8035457}"
ProjectSection(SolutionItems) = preProject
source\Gamma\Directory.Build.props = source\Gamma\Directory.Build.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gamma.Abstractions", "source\Gamma\Gamma.Abstractions\Gamma.Abstractions.csproj", "{3F39F3CF-2E4E-4CB0-AFF1-A11658E24EF1}"
EndProject
Expand Down
4 changes: 4 additions & 0 deletions sample/Small/source/Gamma/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))"
Condition="'' != $([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
</Project>
5 changes: 4 additions & 1 deletion source/ProjectFilter/Commands/FilterProjectsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ protected async override Task ExecuteAsync(OleMenuCmdEventArgs e) {
vm.LoadProjectDependencies = solutionSettings?.LoadProjectDependencies ?? globalSettings.LoadProjectDependencies;
vm.UseRegularExpressions = solutionSettings?.UseRegularExpressions ?? globalSettings.UseRegularExpressions;
vm.ExpandLoadedProjects = solutionSettings?.ExpandLoadedProjects ?? globalSettings.ExpandLoadedProjects;
vm.HideSolutionFoldersWithoutLoadedProjects = solutionSettings?.HideSolutionFoldersWithoutLoadedProjects ?? globalSettings.HideSolutionFoldersWithoutLoadedProjects;

dialog = new FilterDialog {
DataContext = vm
Expand All @@ -76,7 +77,8 @@ protected async override Task ExecuteAsync(OleMenuCmdEventArgs e) {
solutionSettings = new SolutionSettings {
LoadProjectDependencies = vm.LoadProjectDependencies,
UseRegularExpressions = vm.UseRegularExpressions,
ExpandLoadedProjects = vm.ExpandLoadedProjects
ExpandLoadedProjects = vm.ExpandLoadedProjects,
HideSolutionFoldersWithoutLoadedProjects = vm.HideSolutionFoldersWithoutLoadedProjects
};
PopulateNodeSettings(vm.Items, solutionSettings.Nodes);

Expand All @@ -87,6 +89,7 @@ protected async override Task ExecuteAsync(OleMenuCmdEventArgs e) {
globalSettings.LoadProjectDependencies = vm.LoadProjectDependencies;
globalSettings.UseRegularExpressions = vm.UseRegularExpressions;
globalSettings.ExpandLoadedProjects = vm.ExpandLoadedProjects;
globalSettings.HideSolutionFoldersWithoutLoadedProjects = vm.HideSolutionFoldersWithoutLoadedProjects;
await globalSettings.SaveAsync();

if (result) {
Expand Down
4 changes: 4 additions & 0 deletions source/ProjectFilter/Services/ExtensionSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public ExtensionSettings() {
LoadProjectDependencies = true;
UseRegularExpressions = false;
ExpandLoadedProjects = true;
HideSolutionFoldersWithoutLoadedProjects = false;
}


Expand All @@ -24,4 +25,7 @@ public ExtensionSettings() {

public bool ExpandLoadedProjects { get; set; }


public bool HideSolutionFoldersWithoutLoadedProjects { get; set; }

}
11 changes: 8 additions & 3 deletions source/ProjectFilter/Services/FilterOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ public FilterOptions(
IEnumerable<Guid> projectsToLoad,
IEnumerable<Guid> projectsToUnload,
bool loadProjectDependencies,
bool expandLoadedProjects
bool expandLoadedProjects,
bool hideSolutionFoldersWithoutLoadedProjects
) {
ProjectsToLoad = projectsToLoad.ToList();
ProjectsToUnload = projectsToUnload.ToList();
ProjectsToLoad = [.. projectsToLoad];
ProjectsToUnload = [.. projectsToUnload];
LoadProjectDependencies = loadProjectDependencies;
ExpandLoadedProjects = expandLoadedProjects;
HideSolutionFoldersWithoutLoadedProjects = hideSolutionFoldersWithoutLoadedProjects;
}


Expand All @@ -32,4 +34,7 @@ bool expandLoadedProjects

public bool ExpandLoadedProjects{ get; }


public bool HideSolutionFoldersWithoutLoadedProjects { get; }

}
12 changes: 6 additions & 6 deletions source/ProjectFilter/Services/FilterService.State.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ public State(IWaitDialog waitDialog, ThreadedWaitDialogProgressData initialProgr
_currentProgress = initialProgress;
Solution = solution;

_projectsToLoad = new HashSet<Guid>();
_loadedProjects = new HashSet<Guid>();
_projectsToLoad = [];
_loadedProjects = [];

_projectsToUnload = new HashSet<Guid>();
_unloadedProjects = new HashSet<Guid>();
_projectsToUnload = [];
_unloadedProjects = [];

ProjectsVisitedWhileLoading = new HashSet<Guid>();
ProjectsVisitedWhileLoading = [];
RequiresProjectDependencyCalculation = true;
}

Expand Down Expand Up @@ -109,7 +109,7 @@ private void UpdateProgressSteps() {


public IEnumerable<Guid> GetLoadedProjects() {
return _loadedProjects.ToList();
return [.. _loadedProjects];
}


Expand Down
19 changes: 14 additions & 5 deletions source/ProjectFilter/Services/FilterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ public async Task ApplyAsync(FilterOptions options) {
expandedFolders = new HashSet<Guid>();
}

projectsToUnload = options.ProjectsToUnload.ToList();
projectsToLoad = options.ProjectsToLoad.ToList();
projectsToUnload = [.. options.ProjectsToUnload];
projectsToLoad = [.. options.ProjectsToLoad];

// Work out which projects actually need to be unloaded
// so that we can calculate an accurate progress. If a
Expand Down Expand Up @@ -121,6 +121,15 @@ public async Task ApplyAsync(FilterOptions options) {
// remain hidden because the user cancelled half way through.
await solutionExplorer.HideUnloadedProjectsAsync();

// Hiding unloaded projects will hide the solution folders that those hidden
// projects are in as long as there is nothing else in those folders. If all
// of the projects in a solution folder have been unloaded, but the folder contains
// other solution item (like a readme file or editorconfig file), the folder
// will not be hidden. We have an option that allows those folders to be hidden.
if (options.HideSolutionFoldersWithoutLoadedProjects) {
await solutionExplorer.HideSolutionFoldersWithoutLoadedProjectsAsync();
}

// Expand the projects if we are supposed to. For some reason, Visual Studio seems to expand
// the projects anyway, so if we are not supposed to expand them, then we will collapse them.
if (options.ExpandLoadedProjects) {
Expand Down Expand Up @@ -262,7 +271,7 @@ private static async Task<IEnumerable<Guid>> GetProjectDependenciesAsync(Guid id

solutionBuildManager = (IVsSolutionBuildManager2)await VS.Services.GetSolutionBuildManagerAsync();

output = new List<Guid>();
output = [];

if (state.Solution.TryGetHierarchy(identifier, out IVsHierarchy hierarchy)) {
IVsHierarchy[] dependencies;
Expand All @@ -282,7 +291,7 @@ private static async Task<IEnumerable<Guid>> GetProjectDependenciesAsync(Guid id
state.RequiresProjectDependencyCalculation = false;
}

dependencies = Array.Empty<IVsHierarchy>();
dependencies = [];

// First we need to ask for the dependencies without specifying an
// array so that we can find out how many dependencies there are.
Expand Down Expand Up @@ -364,7 +373,7 @@ private static async Task<IEnumerable<Guid>> GetProjectsToCollapseAsync(ISolutio


// All of the projects that were loaded should be collapsed.
projects = state.GetLoadedProjects().ToHashSet();
projects = [.. state.GetLoadedProjects()];

// Any solution folders that are now expanded
// and were not originally expanded should also be collapsed.
Expand Down
2 changes: 1 addition & 1 deletion source/ProjectFilter/Services/HierarchyNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ ImageMoniker expandedIcon
Name = name;
CollapsedIcon = collapsedIcon;
ExpandedIcon = expandedIcon;
_children = new List<HierarchyNode>();
_children = [];

IsFolder = false;
IsLoaded = false;
Expand Down
6 changes: 3 additions & 3 deletions source/ProjectFilter/Services/HierarchyProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public async Task<IEnumerable<IHierarchyNode>> GetHierarchyAsync() {


nodes = await GetNodesAsync();
mapping = new Dictionary<Guid, HierarchyNode>();
mapping = [];

foreach (var item in nodes) {
mapping[item.Hierarchy.Identifier] = item.Hierarchy;
Expand All @@ -34,7 +34,7 @@ public async Task<IEnumerable<IHierarchyNode>> GetHierarchyAsync() {
// to sort each collection of children individually.
nodes.Sort((x, y) => CompareNodes(x.Hierarchy, y.Hierarchy));

roots = new List<HierarchyNode>();
roots = [];

// Add child nodes to the parents and find the root nodes.
foreach (var node in nodes) {
Expand Down Expand Up @@ -64,7 +64,7 @@ public async Task<IEnumerable<IHierarchyNode>> GetHierarchyAsync() {
imageService = (IVsImageService2)await VS.Services.GetImageServiceAsync();
hierarchyItemManager = await VS.GetMefServiceAsync<IVsHierarchyItemManager>();

output = new List<(HierarchyNode Hierarchy, Guid Parent)>();
output = [];

foreach (var hierarchy in solution.GetAllProjectHierarchies(ProjectStateFilter.All)) {
if (!TryGetIdentifier(solution, hierarchy, out Guid identifier)) {
Expand Down
3 changes: 3 additions & 0 deletions source/ProjectFilter/Services/IExtensionSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public interface IExtensionSettings {
bool ExpandLoadedProjects { get; set; }


bool HideSolutionFoldersWithoutLoadedProjects { get; set; }


Task LoadAsync();


Expand Down
3 changes: 3 additions & 0 deletions source/ProjectFilter/Services/ISolutionExplorer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public interface ISolutionExplorer {
Task HideUnloadedProjectsAsync();


Task HideSolutionFoldersWithoutLoadedProjectsAsync();


Task ExpandAsync(IEnumerable<Guid> projects);


Expand Down
20 changes: 20 additions & 0 deletions source/ProjectFilter/Services/ISolutionExplorerNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Collections.Generic;


namespace ProjectFilter.Services;


public interface ISolutionExplorerNode {

bool IsFolder { get; }


bool IsProject { get; }


bool IsLoaded { get; }


IReadOnlyList<ISolutionExplorerNode> Children { get; }

}
Loading