Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PT Run] Find applications using the PATH env variable #4418

Merged
merged 17 commits into from
Jun 23, 2020
Merged
Show file tree
Hide file tree
Changes from 15 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
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32-Anwendung</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Weblink-Anwendung</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web-Anwendung</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>

</ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,5 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32 application</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Internet shortcut application</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web application</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>
</ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32 application</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Internet shortcut application</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web application</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>

</ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32 application</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Internet shortcut application</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web application</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>

</ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32 application</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Internet shortcut application</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web application</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>

</ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32 application</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Internet shortcut application</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web application</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>

</ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@
<system:String x:Key="powertoys_run_plugin_program_win32_application">Win32 application</system:String>
<system:String x:Key="powertoys_run_plugin_program_internet_shortcut_application">Internet shortcut application</system:String>
<system:String x:Key="powertoys_run_plugin_program_web_application">Web application</system:String>
<system:String x:Key="powertoys_run_plugin_program_run_command">Run command</system:String>

</ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ private enum ApplicationTypes
{
WEB_APPLICATION = 0,
INTERNET_SHORTCUT_APPLICATION = 1,
WIN32_APPLICATION = 2
WIN32_APPLICATION = 2,
RUN_COMMAND = 3
}

private int Score(string query)
Expand Down Expand Up @@ -115,12 +116,27 @@ public string SetSubtitle(uint AppType, IPublicAPI api)
{
return api.GetTranslation("powertoys_run_plugin_program_web_application");
}
else if(AppType == (uint)ApplicationTypes.RUN_COMMAND)
{
return api.GetTranslation("powertoys_run_plugin_program_run_command");
}
else
{
return String.Empty;
}
}

public bool FilterPartialMatchForRunCommands(string query)
dsrivastavv marked this conversation as resolved.
Show resolved Hide resolved
alekhyareddy28 marked this conversation as resolved.
Show resolved Hide resolved
{
if (AppType == (uint)ApplicationTypes.RUN_COMMAND
&& !query.Equals(Name, StringComparison.OrdinalIgnoreCase))
{
return true;
}

return false;
}

public Result Result(string query, IPublicAPI api)
{
var score = Score(query);
Expand All @@ -143,6 +159,12 @@ public Result Result(string query, IPublicAPI api)
}
}

// NOTE: This is to display run commands only when there is an exact match, like in start menu
if(FilterPartialMatchForRunCommands(query))
{
return null;
}

var result = new Result
{
SubTitle = SetSubtitle(AppType, api),
Expand Down Expand Up @@ -435,7 +457,7 @@ private static Win32 ExeProgram(string path)
}
}

private static IEnumerable<string> ProgramPaths(string directory, string[] suffixes)
private static IEnumerable<string> ProgramPaths(string directory, string[] suffixes, bool recursiveSearch = true)
{
if (!Directory.Exists(directory))
{
Expand Down Expand Up @@ -472,6 +494,12 @@ private static IEnumerable<string> ProgramPaths(string directory, string[] suffi

try
{
// If the search is set to be non-recursive, then do not enqueue the child directories.
if(!recursiveSearch)
{
continue;
}

foreach (var childDirectory in Directory.EnumerateDirectories(currentDirectory, "*", SearchOption.TopDirectoryOnly))
{
folderQueue.Enqueue(childDirectory);
Expand Down Expand Up @@ -522,6 +550,44 @@ private static ParallelQuery<Win32> UnregisteredPrograms(List<Settings.ProgramSo
return programs1.Concat(programs2).Concat(programs3);
}

// Function to obtain the list of applications, the locations of which have been added to the env variable PATH
private static ParallelQuery<Win32> PathEnvironmentPrograms(string[] suffixes)
{

// To get all the locations stored in the PATH env variable
var pathEnvVariable = Environment.GetEnvironmentVariable("PATH");
string[] searchPaths = pathEnvVariable.Split(Path.PathSeparator);
IEnumerable<String> toFilterAllPaths = new List<String>();
bool isRecursiveSearch = true;

foreach(string path in searchPaths)
{
if(path.Length > 0)
{
// to expand any environment variables present in the path
string directory = Environment.ExpandEnvironmentVariables(path);
var paths = ProgramPaths(directory, suffixes, !isRecursiveSearch);
toFilterAllPaths = toFilterAllPaths.Concat(paths);
}
}

var allPaths = toFilterAllPaths
.Distinct()
.ToArray();

var programs1 = allPaths.AsParallel().Where(p => Extension(p).Equals(ShortcutExtension, StringComparison.OrdinalIgnoreCase)).Select(LnkProgram);
var programs2 = allPaths.AsParallel().Where(p => Extension(p).Equals(ApplicationReferenceExtension, StringComparison.OrdinalIgnoreCase)).Select(Win32Program);
var programs3 = allPaths.AsParallel().Where(p => Extension(p).Equals(InternetShortcutExtension, StringComparison.OrdinalIgnoreCase)).Select(InternetShortcutProgram);
var programs4 = allPaths.AsParallel().Where(p => Extension(p).Equals(ExeExtension, StringComparison.OrdinalIgnoreCase)).Select(ExeProgram);

var allPrograms = programs1.Concat(programs2).Where(p => p.Valid)
.Concat(programs3).Where(p => p.Valid)
.Concat(programs4).Where(p => p.Valid)
.Select( p => { p.AppType = (uint)ApplicationTypes.RUN_COMMAND; return p; });

return allPrograms;
}

private static ParallelQuery<Win32> StartMenuPrograms(string[] suffixes)
{
var disabledProgramsList = Main._settings.DisabledProgramSources;
Expand Down Expand Up @@ -636,8 +702,8 @@ public bool Equals(Win32 app1, Win32 app2)
&& !string.IsNullOrEmpty(app1.ExecutableName) && !string.IsNullOrEmpty(app2.ExecutableName)
&& !string.IsNullOrEmpty(app1.FullPath) && !string.IsNullOrEmpty(app2.FullPath))
{
return app1.Name.Equals(app2.Name, StringComparison.OrdinalIgnoreCase)
&& app1.ExecutableName.Equals(app2.ExecutableName, StringComparison.OrdinalIgnoreCase)
return app1.Name.Equals(app2.Name, StringComparison.OrdinalIgnoreCase)
&& app1.ExecutableName.Equals(app2.ExecutableName, StringComparison.OrdinalIgnoreCase)
&& app1.FullPath.Equals(app2.FullPath, StringComparison.OrdinalIgnoreCase);
}
return false;
Expand All @@ -662,7 +728,7 @@ public int GetHashCode(Win32 obj)
// Deduplication code
public static Func<ParallelQuery<Win32>, Win32[]> DeduplicatePrograms = (programs) =>
{
var uniqueExePrograms = programs.Where(x => !string.IsNullOrEmpty(x.LnkResolvedPath) || Extension(x.FullPath) != ExeExtension);
var uniqueExePrograms = programs.Where(x => !(string.IsNullOrEmpty(x.LnkResolvedPath) && (Extension(x.FullPath) == ExeExtension) && !(x.AppType == (uint)ApplicationTypes.RUN_COMMAND)));
var uniquePrograms = uniqueExePrograms.Distinct(new removeDuplicatesComparer());
return uniquePrograms.ToArray();
};
Expand All @@ -688,6 +754,12 @@ public static Win32[] All(Settings settings)
programs = programs.Concat(startMenu);
}

if (settings.EnablePathEnvironmentVariableSource)
{
var appPathEnvironment = PathEnvironmentPrograms(settings.ProgramSuffixes);
programs = programs.Concat(appPathEnvironment);
}

return DeduplicatePrograms(programs);
}
#if DEBUG //This is to make developer aware of any unhandled exception and add in handling.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class Settings

public bool EnableRegistrySource { get; set; } = true;

public bool EnablePathEnvironmentVariableSource { get; set; } = true;

internal const char SuffixSeparator = ';';

/// <summary>
Expand Down
40 changes: 40 additions & 0 deletions src/modules/launcher/Wox.Test/Plugins/ProgramPluginTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,24 @@ public class ProgramPluginTest
LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\test proxy.lnk"
};

Win32 cmd_run_command = new Win32
{
Name = "cmd",
ExecutableName = "cmd.exe",
FullPath = "c:\\windows\\system32\\cmd.exe",
LnkResolvedPath = null,
AppType = 3 // Run command
};

Win32 cmder_run_command = new Win32
{
Name = "Cmder",
ExecutableName = "Cmder.exe",
FullPath = "c:\\tools\\cmder\\cmder.exe",
LnkResolvedPath = null,
AppType = 3 // Run command
};

[Test]
public void DedupFunction_whenCalled_mustRemoveDuplicateNotepads()
{
Expand Down Expand Up @@ -260,5 +278,27 @@ public bool PinnedWebPages_ShouldNotBeFiltered_WhenSearchingForThem(string query
// unreachable code
return true;
}

[TestCase("Command Prompt")]
[TestCase("cmd")]
[TestCase("cmd.exe")]
[TestCase("ignoreQueryText")]
public void Win32Applications_ShouldNotBeFiltered_WhenFilteringRunCommands(string query)
{
// Even if there is an exact match in the name or exe name, win32 applications should never be filtered
Assert.IsFalse(command_prompt.FilterPartialMatchForRunCommands(query));
}

[TestCase("cmd")]
[TestCase("Cmd")]
[TestCase("CMD")]
public void RunCommands_ShouldNotBeFiltered_OnExactMatch(string query)
{
// Partial matches should be filtered
Assert.IsTrue(cmder_run_command.FilterPartialMatchForRunCommands(query));

// If query matches the name, it should not be filtered (case-insensitive)
Assert.IsFalse(cmd_run_command.FilterPartialMatchForRunCommands(query));
}
}
}