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
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>

<PropertyGroup>
<Version>2.7.0</Version>
<Version>2.8.0</Version>
<TargetFramework>net48</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
Expand Down
94 changes: 51 additions & 43 deletions source/ProjectFilter/Commands/FilterProjectsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Microsoft.VisualStudio.Text.PatternMatching;
using ProjectFilter.Services;
using ProjectFilter.UI;
using ProjectFilter.UI.Utilities;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
Expand Down Expand Up @@ -31,70 +30,79 @@ protected async override Task ExecuteAsync(OleMenuCmdEventArgs e) {
}


private async Task<FilterOptions?> GetOptionsAsync() {
private static async Task<FilterOptions?> GetOptionsAsync() {
IHierarchyProvider hierarchyProvider;
TextFilterFactory textFilterFactory;
Func<Task<IEnumerable<IHierarchyNode>>> hierarchyFactory;
IExtensionSettings globalSettings;
ISolutionSettingsManager solutionSettingsManager;
SolutionSettings? solutionSettings;
FilterDialogViewModel vm;
FilterDialog dialog;
bool result;


await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

hierarchyProvider = await VS.GetRequiredServiceAsync<IHierarchyProvider, IHierarchyProvider>();
textFilterFactory = await CreateTextFilterFactoryAsync();
hierarchyFactory = async () => await hierarchyProvider.GetHierarchyAsync();
hierarchyFactory = async () => await hierarchyProvider.GetHierarchyAsync(true);

globalSettings = await VS.GetRequiredServiceAsync<IExtensionSettings, IExtensionSettings>();
await globalSettings.LoadAsync();

solutionSettingsManager = await VS.GetRequiredServiceAsync<ISolutionSettingsManager, ISolutionSettingsManager>();
solutionSettings = await solutionSettingsManager.GetSettingsAsync();

using (var vm = new FilterDialogViewModel(hierarchyFactory, Debouncer.Create, textFilterFactory, solutionSettings?.Nodes, Package.JoinableTaskFactory)) {
FilterDialog dialog;
bool result;


vm = new FilterDialogViewModel(hierarchyFactory, textFilterFactory, solutionSettings?.Nodes) {
// Initialize the settings with the settings that were last
// used for this solution. If this solution hasn't been
// filtered before, then we'll use the global settings.
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
};

result = dialog.ShowModal().GetValueOrDefault();

// Save the settings for this solution, even if filtering
// was cancelled. This saves the user from having to re-apply
// the settings the next time they open the dialog.
solutionSettings = new SolutionSettings {
LoadProjectDependencies = vm.LoadProjectDependencies,
UseRegularExpressions = vm.UseRegularExpressions,
ExpandLoadedProjects = vm.ExpandLoadedProjects,
HideSolutionFoldersWithoutLoadedProjects = vm.HideSolutionFoldersWithoutLoadedProjects
};
PopulateNodeSettings(vm.Items, solutionSettings.Nodes);

solutionSettingsManager.SetSettings(solutionSettings);

// Also save the settings to the global settings so that the same settings
// can be used for solutions that don't have their own settings yet.
globalSettings.LoadProjectDependencies = vm.LoadProjectDependencies;
globalSettings.UseRegularExpressions = vm.UseRegularExpressions;
globalSettings.ExpandLoadedProjects = vm.ExpandLoadedProjects;
globalSettings.HideSolutionFoldersWithoutLoadedProjects = vm.HideSolutionFoldersWithoutLoadedProjects;
await globalSettings.SaveAsync();

if (result) {
return vm.Result;
}
LoadProjectDependencies = solutionSettings?.LoadProjectDependencies ?? globalSettings.LoadProjectDependencies,
UseRegularExpressions = solutionSettings?.UseRegularExpressions ?? globalSettings.UseRegularExpressions,
ExpandLoadedProjects = solutionSettings?.ExpandLoadedProjects ?? globalSettings.ExpandLoadedProjects,
HideSolutionFoldersWithoutLoadedProjects =
solutionSettings?.HideSolutionFoldersWithoutLoadedProjects ??
globalSettings.HideSolutionFoldersWithoutLoadedProjects,
HideSolutionFoldersWithOnlyFiles =
solutionSettings?.HideSolutionFoldersWithOnlyFiles ??
globalSettings.HideSolutionFoldersWithOnlyFiles
};

dialog = new FilterDialog {
DataContext = vm
};

result = dialog.ShowModal().GetValueOrDefault();

// Save the settings for this solution, even if filtering
// was cancelled. This saves the user from having to re-apply
// the settings the next time they open the dialog.
solutionSettings = new SolutionSettings {
LoadProjectDependencies = vm.LoadProjectDependencies,
UseRegularExpressions = vm.UseRegularExpressions,
ExpandLoadedProjects = vm.ExpandLoadedProjects,
HideSolutionFoldersWithoutLoadedProjects = vm.HideSolutionFoldersWithoutLoadedProjects,
HideSolutionFoldersWithOnlyFiles = vm.HideSolutionFoldersWithOnlyFiles
};
PopulateNodeSettings(vm.Items, solutionSettings.Nodes);

solutionSettingsManager.SetSettings(solutionSettings);

// Also save the settings to the global settings so that the same settings
// can be used for solutions that don't have their own settings yet.
globalSettings.LoadProjectDependencies = solutionSettings.LoadProjectDependencies;
globalSettings.UseRegularExpressions = solutionSettings.UseRegularExpressions;
globalSettings.ExpandLoadedProjects = solutionSettings.ExpandLoadedProjects;
globalSettings.HideSolutionFoldersWithoutLoadedProjects =
solutionSettings.HideSolutionFoldersWithoutLoadedProjects ?? false;
globalSettings.HideSolutionFoldersWithOnlyFiles =
solutionSettings.HideSolutionFoldersWithOnlyFiles ?? false;

await globalSettings.SaveAsync();

if (result) {
return vm.Result;
}

return null;
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 @@ -14,6 +14,7 @@ public ExtensionSettings() {
UseRegularExpressions = false;
ExpandLoadedProjects = true;
HideSolutionFoldersWithoutLoadedProjects = false;
HideSolutionFoldersWithOnlyFiles = false;
}


Expand All @@ -28,4 +29,7 @@ public ExtensionSettings() {

public bool HideSolutionFoldersWithoutLoadedProjects { get; set; }


public bool HideSolutionFoldersWithOnlyFiles { get; set; }

}
10 changes: 7 additions & 3 deletions source/ProjectFilter/Services/FilterOptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;


namespace ProjectFilter.Services;
Expand All @@ -13,13 +12,15 @@ public FilterOptions(
IEnumerable<Guid> projectsToUnload,
bool loadProjectDependencies,
bool expandLoadedProjects,
bool hideSolutionFoldersWithoutLoadedProjects
bool hideSolutionFoldersWithoutLoadedProjects,
bool hideSolutionFoldersWithOnlyFiles
) {
ProjectsToLoad = [.. projectsToLoad];
ProjectsToUnload = [.. projectsToUnload];
LoadProjectDependencies = loadProjectDependencies;
ExpandLoadedProjects = expandLoadedProjects;
HideSolutionFoldersWithoutLoadedProjects = hideSolutionFoldersWithoutLoadedProjects;
HideSolutionFoldersWithOnlyFiles = hideSolutionFoldersWithOnlyFiles;
}


Expand All @@ -32,9 +33,12 @@ bool hideSolutionFoldersWithoutLoadedProjects
public bool LoadProjectDependencies { get; }


public bool ExpandLoadedProjects{ get; }
public bool ExpandLoadedProjects { get; }


public bool HideSolutionFoldersWithoutLoadedProjects { get; }


public bool HideSolutionFoldersWithOnlyFiles { get; }

}
18 changes: 11 additions & 7 deletions source/ProjectFilter/Services/FilterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,17 @@ 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();
if (options.HideSolutionFoldersWithoutLoadedProjects || options.HideSolutionFoldersWithOnlyFiles) {
// 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.
// So when we want to hide solution files without loaded projects, or hide solution
// folders that only contain files, then we need to hide those folders ourselves.
await solutionExplorer.HideSolutionFoldersAsync(
options.HideSolutionFoldersWithoutLoadedProjects,
options.HideSolutionFoldersWithOnlyFiles
);
}

// Expand the projects if we are supposed to. For some reason, Visual Studio seems to expand
Expand Down
8 changes: 5 additions & 3 deletions source/ProjectFilter/Services/HierarchyProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace ProjectFilter.Services;

public class HierarchyProvider : IHierarchyProvider {

public async Task<IEnumerable<IHierarchyNode>> GetHierarchyAsync() {
public async Task<IEnumerable<IHierarchyNode>> GetHierarchyAsync(bool omitEmptyFolders) {
List<(HierarchyNode Hierarchy, Guid Parent)> nodes;
Dictionary<Guid, HierarchyNode> mapping;
List<HierarchyNode> roots;
Expand Down Expand Up @@ -45,7 +45,9 @@ public async Task<IEnumerable<IHierarchyNode>> GetHierarchyAsync() {
}
}

RemoveEmptyFolders(roots);
if (omitEmptyFolders) {
RemoveEmptyFolders(roots);
}

return roots;
}
Expand Down Expand Up @@ -182,7 +184,7 @@ private static int CompareNodes(HierarchyNode x, HierarchyNode y) {
}


private void RemoveEmptyFolders(IList<HierarchyNode> nodes) {
private static void RemoveEmptyFolders(IList<HierarchyNode> nodes) {
for (int i = nodes.Count - 1; i >= 0; i--) {
HierarchyNode node;

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 @@ -20,6 +20,9 @@ public interface IExtensionSettings {
bool HideSolutionFoldersWithoutLoadedProjects { get; set; }


bool HideSolutionFoldersWithOnlyFiles { get; set; }


Task LoadAsync();


Expand Down
2 changes: 1 addition & 1 deletion source/ProjectFilter/Services/IHierarchyProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ namespace ProjectFilter.Services;
[Guid("7eaa8e03-e6fe-35c7-a27d-5415b28d58df")]
public interface IHierarchyProvider {

Task<IEnumerable<IHierarchyNode>> GetHierarchyAsync();
Task<IEnumerable<IHierarchyNode>> GetHierarchyAsync(bool omitEmptyFolders);

}
5 changes: 4 additions & 1 deletion source/ProjectFilter/Services/ISolutionExplorer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ public interface ISolutionExplorer {
Task HideUnloadedProjectsAsync();


Task HideSolutionFoldersWithoutLoadedProjectsAsync();
Task HideSolutionFoldersAsync(
bool hideSolutionFoldersWithoutLoadedProjects,
bool hideSolutionFoldersWithOnlyFiles
);


Task ExpandAsync(IEnumerable<Guid> projects);
Expand Down
Loading