From 3bdb5305baae1cf403fb608e8f86bdf25528bb41 Mon Sep 17 00:00:00 2001 From: Michael Jolley Date: Fri, 26 Sep 2025 11:15:34 -0500 Subject: [PATCH] CmdPal: Linq clean-up (All Apps) (#41551) Beginning the process of removing System.Linq from CmdPal. This PR removes it from the All Apps extension. --- .../AllAppsCommandProvider.cs | 48 ++++-- .../Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs | 104 +++++++------ .../ext/Microsoft.CmdPal.Ext.Apps/AppCache.cs | 6 +- .../Microsoft.CmdPal.Ext.Apps/IAppCache.cs | 1 - .../JsonSerializationContext.cs | 4 - .../Programs/PackageManagerWrapper.cs | 15 +- .../Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs | 93 ++++++++---- .../Programs/UWPApplication.cs | 43 ++++-- .../Programs/Win32Program.cs | 143 +++++++++++++++--- .../State/PinStateChangedEventArgs.cs | 4 - .../State/PinnedAppsManager.cs | 4 +- .../Storage/ListRepository`1.cs | 21 ++- .../Storage/PackageRepository.cs | 9 +- .../Storage/Win32ProgramFileSystemWatchers.cs | 12 +- .../Utils/ShellCommand.cs | 3 - .../Microsoft.CmdPal.Ext.Apps/Utils/Theme.cs | 6 - .../Utils/ThemeHelper.cs | 29 ++-- 17 files changed, 380 insertions(+), 165 deletions(-) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsCommandProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsCommandProvider.cs index 3329456960..b5ce5522bb 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsCommandProvider.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsCommandProvider.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Linq; +using System.Collections.Generic; using Microsoft.CmdPal.Ext.Apps.Properties; using Microsoft.CmdPal.Ext.Apps.State; using Microsoft.CommandPalette.Extensions; @@ -71,26 +71,48 @@ public partial class AllAppsCommandProvider : CommandProvider { var items = _page.GetItems(); - // We're going to do this search in two directions: - // First, is this name a substring of any app... - var nameMatches = items.Where(i => i.Title.Contains(displayName)); + var nameMatches = new List(); + ICommandItem? bestAppMatch = null; + var bestLength = -1; - // ... Then, does any app have this name as a substring ... - // Only get one of these - "Terminal Preview" contains both "Terminal" and "Terminal Preview", so just take the best one - var appMatches = items.Where(i => displayName.Contains(i.Title)).OrderByDescending(i => i.Title.Length).Take(1); + foreach (var item in items) + { + if (item.Title is null) + { + continue; + } + + // We're going to do this search in two directions: + // First, is this name a substring of any app... + if (item.Title.Contains(displayName)) + { + nameMatches.Add(item); + } + + // ... Then, does any app have this name as a substring ... + // Only get one of these - "Terminal Preview" contains both "Terminal" and "Terminal Preview", so just take the best one + if (displayName.Contains(item.Title)) + { + if (item.Title.Length > bestLength) + { + bestLength = item.Title.Length; + bestAppMatch = item; + } + } + } // ... Now, combine those two - var both = nameMatches.Concat(appMatches); + List both = bestAppMatch is null ? nameMatches : [.. nameMatches, bestAppMatch]; - if (both.Count() == 1) + if (both.Count == 1) { - return both.First(); + return both[0]; } - else if (nameMatches.Count() == 1 && appMatches.Count() == 1) + else if (nameMatches.Count == 1 && bestAppMatch is not null) { - if (nameMatches.First() == appMatches.First()) + if (nameMatches[0] == bestAppMatch) { - return nameMatches.First(); + return nameMatches[0]; } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs index 68b77ce728..2a264f70c2 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Threading; using System.Threading.Tasks; using ManagedCommon; @@ -62,7 +61,9 @@ public sealed partial class AllAppsPage : ListPage { // Build or update the list if needed BuildListItems(); - return pinnedApps.Concat(unpinnedApps).ToArray(); + + AppListItem[] allApps = [.. pinnedApps, .. unpinnedApps]; + return allApps; } private void BuildListItems() @@ -93,16 +94,25 @@ public sealed partial class AllAppsPage : ListPage private AppItem[] GetAllApps() { - var uwpResults = _appCache.UWPs - .Where((application) => application.Enabled) - .Select(app => app.ToAppItem()); + List allApps = new(); - var win32Results = _appCache.Win32s - .Where((application) => application.Enabled && application.Valid) - .Select(app => app.ToAppItem()); + foreach (var uwpApp in _appCache.UWPs) + { + if (uwpApp.Enabled) + { + allApps.Add(uwpApp.ToAppItem()); + } + } - var allApps = uwpResults.Concat(win32Results).ToArray(); - return allApps; + foreach (var win32App in _appCache.Win32s) + { + if (win32App.Enabled && win32App.Valid) + { + allApps.Add(win32App.ToAppItem()); + } + } + + return [.. allApps]; } internal (AppItem[] AllApps, AppListItem[] PinnedItems, AppListItem[] UnpinnedItems) GetPrograms() @@ -118,9 +128,7 @@ public sealed partial class AllAppsPage : ListPage if (isPinned) { - appListItem.Tags = appListItem.Tags - .Concat([new Tag() { Icon = Icons.PinIcon }]) - .ToArray(); + appListItem.Tags = [.. appListItem.Tags, new Tag() { Icon = Icons.PinIcon }]; pinned.Add(appListItem); } else @@ -129,15 +137,14 @@ public sealed partial class AllAppsPage : ListPage } } + pinned.Sort((a, b) => string.Compare(a.Title, b.Title, StringComparison.Ordinal)); + unpinned.Sort((a, b) => string.Compare(a.Title, b.Title, StringComparison.Ordinal)); + return ( - allApps - .ToArray(), - pinned - .OrderBy(app => app.Title) - .ToArray(), - unpinned - .OrderBy(app => app.Title) - .ToArray()); + allApps, + pinned.ToArray(), + unpinned.ToArray() + ); } private void OnPinStateChanged(object? sender, PinStateChangedEventArgs e) @@ -147,44 +154,55 @@ public sealed partial class AllAppsPage : ListPage * So, instead, we'll just compare pinned items to move existing * items between the two lists. */ - var existingAppItem = allApps.FirstOrDefault(f => f.AppIdentifier == e.AppIdentifier); + AppItem? existingAppItem = null; + + foreach (var app in allApps) + { + if (app.AppIdentifier == e.AppIdentifier) + { + existingAppItem = app; + break; + } + } if (existingAppItem is not null) { var appListItem = new AppListItem(existingAppItem, true, e.IsPinned); + var newPinned = new List(pinnedApps); + var newUnpinned = new List(unpinnedApps); + if (e.IsPinned) { - // Remove it from the unpinned apps array - this.unpinnedApps = this.unpinnedApps - .Where(app => app.AppIdentifier != existingAppItem.AppIdentifier) - .OrderBy(app => app.Title) - .ToArray(); - - var newPinned = this.pinnedApps.ToList(); newPinned.Add(appListItem); - this.pinnedApps = newPinned - .OrderBy(app => app.Title) - .ToArray(); + foreach (var app in newUnpinned) + { + if (app.AppIdentifier == e.AppIdentifier) + { + newUnpinned.Remove(app); + break; + } + } } else { - // Remove it from the pinned apps array - this.pinnedApps = this.pinnedApps - .Where(app => app.AppIdentifier != existingAppItem.AppIdentifier) - .OrderBy(app => app.Title) - .ToArray(); - - var newUnpinned = this.unpinnedApps.ToList(); newUnpinned.Add(appListItem); - this.unpinnedApps = newUnpinned - .OrderBy(app => app.Title) - .ToArray(); + foreach (var app in newPinned) + { + if (app.AppIdentifier == e.AppIdentifier) + { + newPinned.Remove(app); + break; + } + } } - RaiseItemsChanged(0); + pinnedApps = newPinned.ToArray(); + unpinnedApps = newUnpinned.ToArray(); } + + RaiseItemsChanged(0); } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCache.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCache.cs index 48beaec1ff..f2476dae61 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCache.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCache.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Microsoft.CmdPal.Ext.Apps.Programs; using Microsoft.CmdPal.Ext.Apps.Storage; @@ -31,7 +30,10 @@ public sealed partial class AppCache : IAppCache, IDisposable public AppCache() { _win32ProgramRepositoryHelper = new Win32ProgramFileSystemWatchers(); - _win32ProgramRepository = new Win32ProgramRepository(_win32ProgramRepositoryHelper.FileSystemWatchers.Cast().ToList(), AllAppsSettings.Instance, _win32ProgramRepositoryHelper.PathsToWatch); + + var watchers = new List(_win32ProgramRepositoryHelper.FileSystemWatchers); + + _win32ProgramRepository = new Win32ProgramRepository(watchers, AllAppsSettings.Instance, _win32ProgramRepositoryHelper.PathsToWatch); _packageRepository = new PackageRepository(new PackageCatalogWrapper()); diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/IAppCache.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/IAppCache.cs index 0a84230a88..b6e2b94ef5 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/IAppCache.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/IAppCache.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; using Microsoft.CmdPal.Ext.Apps.Programs; namespace Microsoft.CmdPal.Ext.Apps; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/JsonSerializationContext.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/JsonSerializationContext.cs index 5a11d9c135..0db868222c 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/JsonSerializationContext.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/JsonSerializationContext.cs @@ -2,12 +2,8 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Text.Json.Serialization; -using System.Threading.Tasks; using Microsoft.CmdPal.Ext.Apps.State; namespace Microsoft.CmdPal.Ext.Apps; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/PackageManagerWrapper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/PackageManagerWrapper.cs index 38337c4462..79a7ee14fc 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/PackageManagerWrapper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/PackageManagerWrapper.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Linq; using System.Security.Principal; using Windows.Management.Deployment; @@ -26,9 +25,19 @@ public class PackageManagerWrapper : IPackageManager { var pkgs = _packageManager.FindPackagesForUser(user.Value); - return pkgs.Select(PackageWrapper.GetWrapperFromPackage).Where(package => package is not null); + ICollection packages = []; + + foreach (var package in pkgs) + { + if (package is not null) + { + packages.Add(PackageWrapper.GetWrapperFromPackage(package)); + } + } + + return packages; } - return Enumerable.Empty(); + return []; } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs index 01a518f057..faca2c2b39 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO.Abstractions; -using System.Linq; using System.Threading.Tasks; using System.Xml.Linq; using ManagedCommon; @@ -72,18 +71,20 @@ public partial class UWP PInvoke.SHCreateStreamOnFileEx(path, STGMREAD, noAttribute, false, null, &stream).ThrowOnFailure(); using var streamHandle = new SafeComHandle((IntPtr)stream); - Apps = AppxPackageHelper.GetAppsFromManifest(stream).Select(appInManifest => + var appsInManifest = AppxPackageHelper.GetAppsFromManifest(stream); + + foreach (var appInManifest in appsInManifest) { using var appHandle = new SafeComHandle(appInManifest); - return new UWPApplication((IAppxManifestApplication*)appInManifest, this); - }).Where(a => - { - var valid = - !string.IsNullOrEmpty(a.UserModelId) && - !string.IsNullOrEmpty(a.DisplayName) && - a.AppListEntry != "none"; - return valid; - }).ToList(); + var uwpApp = new UWPApplication((IAppxManifestApplication*)appInManifest, this); + + if (!string.IsNullOrEmpty(uwpApp.UserModelId) && + !string.IsNullOrEmpty(uwpApp.DisplayName) && + uwpApp.AppListEntry != "none") + { + Apps.Add(uwpApp); + } + } } catch (Exception ex) { @@ -93,19 +94,31 @@ public partial class UWP } } - // http://www.hanselman.com/blog/GetNamespacesFromAnXMLDocumentWithXPathDocumentAndLINQToXML.aspx private static string[] XmlNamespaces(string path) { var z = XDocument.Load(path); if (z.Root is not null) { - var namespaces = z.Root.Attributes(). - Where(a => a.IsNamespaceDeclaration). - GroupBy( - a => a.Name.Namespace == XNamespace.None ? string.Empty : a.Name.LocalName, - a => XNamespace.Get(a.Value)).Select( - g => g.First().ToString()).ToArray(); - return namespaces; + var namespaces = new HashSet(); + + var attributes = z.Root.Attributes(); + foreach (var attribute in attributes) + { + if (attribute.IsNamespaceDeclaration) + { + // Extract namespace + var key = attribute.Name.Namespace == XNamespace.None ? string.Empty : attribute.Name.LocalName; + XNamespace ns = XNamespace.Get(attribute.Value); + var nsString = ns.ToString(); + + // Use HashSet to check for duplicates + namespaces.Add(nsString); + } + } + + var uniqueNamespaces = new string[namespaces.Count]; + namespaces.CopyTo(uniqueNamespaces); + return uniqueNamespaces; } else { @@ -115,10 +128,13 @@ public partial class UWP private void InitPackageVersion(string[] namespaces) { - foreach (var n in _versionFromNamespace.Keys.Where(namespaces.Contains)) + foreach (var n in _versionFromNamespace.Keys) { - Version = _versionFromNamespace[n]; - return; + if (Array.IndexOf(namespaces, n) >= 0) + { + Version = _versionFromNamespace[n]; + return; + } } Version = PackageVersion.Unknown; @@ -137,7 +153,18 @@ public partial class UWP foreach (var app in u.Apps) { - if (AllAppsSettings.Instance.DisabledProgramSources.All(x => x.UniqueIdentifier != app.UniqueIdentifier)) + var isDisabled = false; + + foreach (var disabled in AllAppsSettings.Instance.DisabledProgramSources) + { + if (disabled.UniqueIdentifier == app.UniqueIdentifier) + { + isDisabled = true; + break; + } + } + + if (!isDisabled) { appsBag.Add(app); } @@ -154,20 +181,28 @@ public partial class UWP private static IEnumerable CurrentUserPackages() { - return PackageManagerWrapper.FindPackagesForCurrentUser().Where(p => + var currentUsersPackages = PackageManagerWrapper.FindPackagesForCurrentUser(); + ICollection packagesToReturn = []; + + foreach (var pkg in currentUsersPackages) { try { - var f = p.IsFramework; - var path = p.InstalledLocation; - return !f && !string.IsNullOrEmpty(path); + var f = pkg.IsFramework; + var path = pkg.InstalledLocation; + + if (!f && !string.IsNullOrEmpty(path)) + { + packagesToReturn.Add(pkg); + } } catch (Exception ex) { Logger.LogError(ex.Message); - return false; } - }); + } + + return packagesToReturn; } public override string ToString() diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs index 6d16bb7bde..7485716f1b 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO.Abstractions; -using System.Linq; using System.Xml; using ManagedCommon; using Microsoft.CmdPal.Ext.Apps.Commands; @@ -348,20 +347,22 @@ public class UWPApplication : IUWPApplication // // FirstOrDefault would result in us using the 1x scaled icon // always, which is usually too small for our needs. - var selectedIconPath = paths.LastOrDefault(File.Exists); - if (!string.IsNullOrEmpty(selectedIconPath)) + for (var i = paths.Count - 1; i >= 0; i--) { - LogoPath = selectedIconPath; - if (highContrast) + if (File.Exists(paths[i])) { - LogoType = LogoType.HighContrast; - } - else - { - LogoType = LogoType.Colored; - } + LogoPath = paths[i]; + if (highContrast) + { + LogoType = LogoType.HighContrast; + } + else + { + LogoType = LogoType.Colored; + } - return true; + return true; + } } } @@ -402,7 +403,23 @@ public class UWPApplication : IUWPApplication } } - var selectedIconPath = paths.OrderBy(x => Math.Abs(pathFactorPairs.GetValueOrDefault(x) - appIconSize)).FirstOrDefault(File.Exists); + // Sort paths by distance to desired app icon size + var selectedIconPath = string.Empty; + var closestDistance = int.MaxValue; + + foreach (var p in paths) + { + if (File.Exists(p) && pathFactorPairs.TryGetValue(p, out var factor)) + { + var distance = Math.Abs(factor - appIconSize); + if (distance < closestDistance) + { + closestDistance = distance; + selectedIconPath = p; + } + } + } + if (!string.IsNullOrEmpty(selectedIconPath)) { LogoPath = selectedIconPath; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs index a8615342de..a45f05cdf3 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs @@ -7,7 +7,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.IO.Abstractions; -using System.Linq; using System.Security; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -616,9 +615,24 @@ public class Win32Program : IProgram } private static IEnumerable CustomProgramPaths(IEnumerable sources, IList suffixes) - => sources?.Where(programSource => Directory.Exists(programSource.Location) && programSource.Enabled) - .SelectMany(programSource => ProgramPaths(programSource.Location, suffixes)) - .ToList() ?? Enumerable.Empty(); + { + if (sources is not null) + { + var paths = new List(); + + foreach (var programSource in sources) + { + if (Directory.Exists(programSource.Location) && programSource.Enabled) + { + paths.AddRange(ProgramPaths(programSource.Location, suffixes)); + } + } + + return paths; + } + + return []; + } // Function to obtain the list of applications, the locations of which have been added to the env variable PATH private static List PathEnvironmentProgramPaths(IList suffixes) @@ -647,9 +661,15 @@ public class Win32Program : IProgram } private static List IndexPath(IList suffixes, List indexLocations) - => indexLocations - .SelectMany(indexLocation => ProgramPaths(indexLocation, suffixes)) - .ToList(); + { + var paths = new List(); + foreach (var indexLocation in indexLocations) + { + paths.AddRange(ProgramPaths(indexLocation, suffixes)); + } + + return paths; + } private static List StartMenuProgramPaths(IList suffixes) { @@ -691,17 +711,51 @@ public class Win32Program : IProgram } } - return paths - .Where(path => suffixes.Any(suffix => path.EndsWith(suffix, StringComparison.InvariantCultureIgnoreCase))) - .Select(ExpandEnvironmentVariables) - .Where(path => path is not null) - .ToList(); + var returnedPaths = new List(); + foreach (var path in paths) + { + var matchesSuffix = false; + foreach (var suffix in suffixes) + { + if (path.EndsWith(suffix, StringComparison.InvariantCultureIgnoreCase)) + { + matchesSuffix = true; + break; + } + } + + if (matchesSuffix) + { + var expandedPath = ExpandEnvironmentVariables(path); + if (expandedPath is not null) + { + returnedPaths.Add(expandedPath); + } + } + } + + return returnedPaths; } private static IEnumerable GetPathsFromRegistry(RegistryKey root) - => root - .GetSubKeyNames() - .Select(x => GetPathFromRegistrySubkey(root, x)); + { + var result = new List(); + + // Get all subkey names + var subKeyNames = root.GetSubKeyNames(); + + // Process each subkey to extract the path + foreach (var subkeyName in subKeyNames) + { + var path = GetPathFromRegistrySubkey(root, subkeyName); + if (!string.IsNullOrEmpty(path)) + { + result.Add(path); + } + } + + return result; + } private static string GetPathFromRegistrySubkey(RegistryKey root, string subkey) { @@ -765,7 +819,28 @@ public class Win32Program : IProgram } public static List DeduplicatePrograms(IEnumerable programs) - => new HashSet(programs, Win32ProgramEqualityComparer.Default).ToList(); + { + // Create a HashSet with the custom equality comparer to automatically deduplicate programs + var uniquePrograms = new HashSet(Win32ProgramEqualityComparer.Default); + + // Filter out invalid programs and add valid ones to the HashSet + foreach (var program in programs) + { + if (program?.Valid == true) + { + uniquePrograms.Add(program); + } + } + + // Convert the HashSet to a List for return + var result = new List(uniquePrograms.Count); + foreach (var program in uniquePrograms) + { + result.Add(program); + } + + return result; + } private static Win32Program GetProgramFromPath(string path) { @@ -881,8 +956,22 @@ public class Win32Program : IProgram foreach (var path in source.GetPaths()) { - if (disabledProgramsList.All(x => x.UniqueIdentifier != path) && - !ExecutableApplicationExtensions.Contains(Extension(path))) + if (ExecutableApplicationExtensions.Contains(Extension(path))) + { + continue; + } + + var isDisabled = false; + foreach (var disabledProgram in disabledProgramsList) + { + if (disabledProgram.UniqueIdentifier == path) + { + isDisabled = true; + break; + } + } + + if (!isDisabled) { pathBag.Add(path); } @@ -902,7 +991,17 @@ public class Win32Program : IProgram foreach (var path in source.GetPaths()) { - if (disabledProgramsList.All(x => x.UniqueIdentifier != path)) + var isDisabled = false; + foreach (var disabledProgram in disabledProgramsList) + { + if (disabledProgram.UniqueIdentifier == path) + { + isDisabled = true; + break; + } + } + + if (!isDisabled) { runCommandPathBag.Add(path); } @@ -931,10 +1030,8 @@ public class Win32Program : IProgram } }); - var programs = programsList.ToList(); - var runCommandPrograms = runCommandProgramsList.ToList(); - - return DeduplicatePrograms(programs.Concat(runCommandPrograms).Where(program => program?.Valid == true)); + List allPrograms = [.. programsList, .. runCommandProgramsList]; + return DeduplicatePrograms(allPrograms); } catch (Exception e) { diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/State/PinStateChangedEventArgs.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/State/PinStateChangedEventArgs.cs index 75cbbde56d..7590ba3ad9 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/State/PinStateChangedEventArgs.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/State/PinStateChangedEventArgs.cs @@ -3,10 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Microsoft.CmdPal.Ext.Apps.State; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/State/PinnedAppsManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/State/PinnedAppsManager.cs index 4540caf78d..0fdc0a934c 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/State/PinnedAppsManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/State/PinnedAppsManager.cs @@ -3,9 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using ManagedCommon; using Microsoft.CommandPalette.Extensions.Toolkit; @@ -31,7 +29,7 @@ public sealed class PinnedAppsManager public bool IsAppPinned(string appIdentifier) { - return _pinnedApps.PinnedAppIdentifiers.Contains(appIdentifier, StringComparer.OrdinalIgnoreCase); + return _pinnedApps.PinnedAppIdentifiers.IndexOf(appIdentifier) >= 0; } public void PinApp(string appIdentifier) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs index b5f92db020..4c12ac0fc7 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs @@ -6,7 +6,6 @@ using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; using ManagedCommon; namespace Microsoft.CmdPal.Ext.Apps.Storage; @@ -20,7 +19,16 @@ public class ListRepository : IRepository, IEnumerable { public IList Items { - get { return _items.Values.ToList(); } + get + { + var items = new List(_items.Count); + foreach (var item in _items.Values) + { + items.Add(item); + } + + return items; + } } private ConcurrentDictionary _items = new ConcurrentDictionary(); @@ -34,9 +42,16 @@ public class ListRepository : IRepository, IEnumerable // enforce that internal representation try { + var result = new ConcurrentDictionary(); + + foreach (var item in list) + { #pragma warning disable CS8602 // Dereference of a possibly null reference. - _items = new ConcurrentDictionary(list.ToDictionary(i => i.GetHashCode())); + result.TryAdd(item.GetHashCode(), item); #pragma warning restore CS8602 // Dereference of a possibly null reference. + } + + _items = result; } catch (ArgumentException ex) { diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/PackageRepository.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/PackageRepository.cs index 2c53a649b9..e9708899c8 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/PackageRepository.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/PackageRepository.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Linq; using ManagedCommon; using Microsoft.CmdPal.Ext.Apps.Programs; using Microsoft.CmdPal.Ext.Apps.Utils; @@ -104,10 +103,14 @@ internal sealed partial class PackageRepository : ListRepository a.Package.Equals(uwp)).ToArray(); - foreach (var app in apps) + foreach (var app in Items) { + if (!app.Package.Equals(uwp)) + { + continue; + } + Remove(app); _isDirty = true; } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramFileSystemWatchers.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramFileSystemWatchers.cs index 3e992cc807..b17568ea73 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramFileSystemWatchers.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramFileSystemWatchers.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using ManagedCommon; namespace Microsoft.CmdPal.Ext.Apps.Storage; @@ -55,7 +54,16 @@ internal sealed partial class Win32ProgramFileSystemWatchers : IDisposable } } - return paths.Except(invalidPaths).ToArray(); + var validPaths = new List(); + foreach (var path in paths) + { + if (!invalidPaths.Contains(path)) + { + validPaths.Add(path); + } + } + + return validPaths.ToArray(); } public void Dispose() diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellCommand.cs index d63afb2180..dbe7a694aa 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellCommand.cs @@ -2,10 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Diagnostics; -using System.Text; -using System.Threading; namespace Microsoft.CmdPal.Ext.Apps.Utils; diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Theme.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Theme.cs index f668fe7bf6..04fecb8379 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Theme.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Theme.cs @@ -2,12 +2,6 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Microsoft.CmdPal.Ext.Apps.Utils; public enum Theme diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ThemeHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ThemeHelper.cs index 005f1962f6..243e0e9a91 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ThemeHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ThemeHelper.cs @@ -4,7 +4,6 @@ using System; using System.Globalization; -using System.Linq; using Microsoft.Win32; @@ -56,15 +55,25 @@ public static class ThemeHelper return Theme.Light; // Default to light theme if missing } - var theme = themePath.Split('\\').Last().Split('.').First().ToLowerInvariant(); - - return theme switch + var splitThemePath = themePath.Split('\\'); + if (splitThemePath.Length > 0) { - "hc1" => Theme.HighContrastOne, - "hc2" => Theme.HighContrastTwo, - "hcwhite" => Theme.HighContrastWhite, - "hcblack" => Theme.HighContrastBlack, - _ => Theme.Light, - }; + var lastSegment = splitThemePath[splitThemePath.Length - 1]; + var splitSegment = lastSegment.Split('.'); + if (splitSegment.Length > 0) + { + var themeVariant = splitSegment[0].ToLowerInvariant(); + return themeVariant switch + { + "hc1" => Theme.HighContrastOne, + "hc2" => Theme.HighContrastTwo, + "hcwhite" => Theme.HighContrastWhite, + "hcblack" => Theme.HighContrastBlack, + _ => Theme.Light, + }; + } + } + + return Theme.Light; } }