Skip to content

Commit

Permalink
Launcher hot fixes (#4003)
Browse files Browse the repository at this point in the history
* Add resource only when it is not already present (#3842)

(cherry picked from commit d2dfbff)

* Take space into consideration while calculating the first matched index (#3874)

(cherry picked from commit a8d67a1)

* Fix for File explorer not showing up and multiple notepads (#3969)

* reverted the dedup code, file explorer shows up but so do duplicates

* Fixed file explorer and dedup

* Formatting

* Added tests for all the cases

* Formatting

* Tests

* take name and exe into consideration while calculating hash

* unique primes while calculating hash code

(cherry picked from commit 8cddd59)
  • Loading branch information
alekhyareddy28 authored Jun 3, 2020
1 parent bbbbf09 commit 2ba99d1
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,10 @@ internal string ResourceFromPri(string packageFullName, string resourceReference
{
parsed = prefix + "//" + key;
}
else if (key.Contains("resources", StringComparison.OrdinalIgnoreCase))
{
parsed = prefix + key;
}
else
{
parsed = prefix + "///resources/" + key;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,46 @@ private static Win32 GetProgramFromPath(string path)
return entry;
}

public class removeDuplicatesComparer : IEqualityComparer<Win32>
{
public bool Equals(Win32 app1, Win32 app2)
{

if(!string.IsNullOrEmpty(app1.Name) && !string.IsNullOrEmpty(app2.Name)
&& !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)
&& app1.FullPath.Equals(app2.FullPath, StringComparison.OrdinalIgnoreCase);
}
return false;
}

// Ref : https://stackoverflow.com/questions/2730865/how-do-i-calculate-a-good-hash-code-for-a-list-of-strings
public int GetHashCode(Win32 obj)
{
int namePrime = 13;
int executablePrime = 17;
int fullPathPrime = 31;

int result = 1;
result = result * namePrime + obj.Name.GetHashCode();
result = result * executablePrime + obj.ExecutableName.GetHashCode();
result = result * fullPathPrime + obj.FullPath.GetHashCode();

return result;
}
}

// Deduplication code
public static Func<ParallelQuery<Win32>, Win32[]> DeduplicatePrograms = (programs) =>
{
var uniqueExePrograms = programs.Where(x => !string.IsNullOrEmpty(x.LnkResolvedPath) || Extension(x.FullPath) != ExeExtension);
var uniquePrograms = uniqueExePrograms.Distinct(new removeDuplicatesComparer());
return uniquePrograms.ToArray();
};

public static Win32[] All(Settings settings)
{
try
Expand All @@ -464,15 +504,7 @@ public static Win32[] All(Settings settings)
programs = programs.Concat(startMenu);
}

var programsWithoutLnk = programs.Where(x => string.IsNullOrEmpty(x.LnkResolvedPath));
var programsAsList = programs.ToList();

foreach(var app in programsWithoutLnk)
{
programsAsList.RemoveAll(x => (x.FullPath == app.FullPath) && string.IsNullOrEmpty(x.LnkResolvedPath));
}

return programsAsList.ToArray();
return DeduplicatePrograms(programs);
}
#if DEBUG //This is to make developer aware of any unhandled exception and add in handling.
catch (Exception e)
Expand Down
27 changes: 26 additions & 1 deletion src/modules/launcher/Wox.Infrastructure/StringMatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,18 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
bool allSubstringsContainedInCompareString = true;

var indexList = new List<int>();
List<int> spaceIndices = new List<int>();

for (var compareStringIndex = 0; compareStringIndex < fullStringToCompareWithoutCase.Length; compareStringIndex++)
{

// To maintain a list of indices which correspond to spaces in the string to compare
// To populate the list only for the first query substring
if (fullStringToCompareWithoutCase[compareStringIndex].Equals(' ') && currentQuerySubstringIndex == 0)
{
spaceIndices.Add(compareStringIndex);
}

if (fullStringToCompareWithoutCase[compareStringIndex] != currentQuerySubstring[currentQuerySubstringCharacterIndex])
{
matchFoundInPreviousLoop = false;
Expand Down Expand Up @@ -147,14 +156,30 @@ public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption
// proceed to calculate score if every char or substring without whitespaces matched
if (allQuerySubstringsMatched)
{
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex, lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
var nearestSpaceIndex = CalculateClosestSpaceIndex(spaceIndices, firstMatchIndex);
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);

return new MatchResult(true, UserSettingSearchPrecision, indexList, score);
}

return new MatchResult (false, UserSettingSearchPrecision);
}

// To get the index of the closest space which preceeds the first matching index
private int CalculateClosestSpaceIndex(List<int> spaceIndices, int firstMatchIndex)
{
if(spaceIndices.Count == 0)
{
return -1;
}
else
{
int? ind = spaceIndices.OrderBy(item => (firstMatchIndex - item)).Where(item => firstMatchIndex > item).FirstOrDefault();
int closestSpaceIndex = ind ?? -1;
return closestSpaceIndex;
}
}

private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQuerySubstringCharacterIndex,
string fullStringToCompareWithoutCase, string currentQuerySubstring)
{
Expand Down
17 changes: 15 additions & 2 deletions src/modules/launcher/Wox.Test/FuzzyMatcherTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,6 @@ public void WhenGivenDesiredPrecisionThenShouldReturnAllResultsGreaterOrEqual(
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sql", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sql serv", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sqlserv", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("sql servman", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("sql serv man", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sql studio", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("mic", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
Expand Down Expand Up @@ -224,5 +222,20 @@ public void WhenGivenQueryShouldReturnResultsContainingAllQuerySubstrings(
$"Raw Score: {matchResult.RawScore}{Environment.NewLine}" +
$"Precision Score: {(int)expectedPrecisionScore}");
}

[TestCase("Windows Terminal", "Windows_Terminal", "term")]
[TestCase("Windows Terminal", "WindowsTerminal", "term")]
public void FuzzyMatchingScore_ShouldBeHigher_WhenPreceedingCharacterIsSpace(string firstCompareStr, string secondCompareStr, string query)
{
// Arrange
var matcher = new StringMatcher();

// Act
var firstScore = matcher.FuzzyMatch(query, firstCompareStr).Score;
var secondScore = matcher.FuzzyMatch(query, secondCompareStr).Score;

// Assert
Assert.IsTrue(firstScore > secondScore);
}
}
}
143 changes: 143 additions & 0 deletions src/modules/launcher/Wox.Test/Plugins/ProgramPluginTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NUnit.Framework;
using Wox.Infrastructure;
using Wox.Plugin;
using Microsoft.Plugin.Program.Programs;
using Moq;
using System.IO;

namespace Wox.Test.Plugins
{
[TestFixture]
public class ProgramPluginTest
{
Win32 notepad_appdata = new Win32
{
Name = "Notepad",
ExecutableName = "notepad.exe",
FullPath = "c:\\windows\\system32\\notepad.exe",
LnkResolvedPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk"
};

Win32 notepad_users = new Win32
{
Name = "Notepad",
ExecutableName = "notepad.exe",
FullPath = "c:\\windows\\system32\\notepad.exe",
LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\notepad.lnk"
};

Win32 azure_command_prompt = new Win32
{
Name = "Microsoft Azure Command Prompt - v2.9",
ExecutableName = "cmd.exe",
FullPath = "c:\\windows\\system32\\cmd.exe",
LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\microsoft azure\\microsoft azure sdk for .net\\v2.9\\microsoft azure command prompt - v2.9.lnk"
};

Win32 visual_studio_command_prompt = new Win32
{
Name = "x64 Native Tools Command Prompt for VS 2019",
ExecutableName = "cmd.exe",
FullPath = "c:\\windows\\system32\\cmd.exe",
LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\visual studio 2019\\visual studio tools\\vc\\x64 native tools command prompt for vs 2019.lnk"
};

Win32 command_prompt = new Win32
{
Name = "Command Prompt",
ExecutableName = "cmd.exe",
FullPath = "c:\\windows\\system32\\cmd.exe",
LnkResolvedPath ="c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\system tools\\command prompt.lnk"
};

Win32 file_explorer = new Win32
{
Name = "File Explorer",
ExecutableName = "File Explorer.lnk",
FullPath = "c:\\users\\powertoys\\appdata\\roaming\\microsoft\\windows\\start menu\\programs\\system tools\\file explorer.lnk",
LnkResolvedPath = null
};

Win32 wordpad = new Win32
{
Name = "Wordpad",
ExecutableName = "wordpad.exe",
FullPath = "c:\\program files\\windows nt\\accessories\\wordpad.exe",
LnkResolvedPath = "c:\\programdata\\microsoft\\windows\\start menu\\programs\\accessories\\wordpad.lnk"
};

Win32 wordpad_duplicate = new Win32
{
Name = "WORDPAD",
ExecutableName = "WORDPAD.EXE",
FullPath = "c:\\program files\\windows nt\\accessories\\wordpad.exe",
LnkResolvedPath = null
};


[Test]
public void DedupFunction_whenCalled_mustRemoveDuplicateNotepads()
{
// Arrange
List<Win32> prgms = new List<Win32>();
prgms.Add(notepad_appdata);
prgms.Add(notepad_users);

// Act
Win32[] apps = Win32.DeduplicatePrograms(prgms.AsParallel());

// Assert
Assert.AreEqual(apps.Length, 1);
}

[Test]
public void DedupFunction_whenCalled_mustNotRemovelnkWhichdoesNotHaveExe()
{
// Arrange
List<Win32> prgms = new List<Win32>();
prgms.Add(file_explorer);

// Act
Win32[] apps = Win32.DeduplicatePrograms(prgms.AsParallel());

// Assert
Assert.AreEqual(apps.Length, 1);
}

[Test]
public void DedupFunction_mustRemoveDuplicates_forExeExtensionsWithoutLnkResolvedPath()
{
// Arrange
List<Win32> prgms = new List<Win32>();
prgms.Add(wordpad);
prgms.Add(wordpad_duplicate);

// Act
Win32[] apps = Win32.DeduplicatePrograms(prgms.AsParallel());

// Assert
Assert.AreEqual(apps.Length, 1);
Assert.IsTrue(!string.IsNullOrEmpty(apps[0].LnkResolvedPath));
}

[Test]
public void DedupFunction_mustNotRemovePrograms_withSameExeNameAndFullPath()
{
// Arrange
List<Win32> prgms = new List<Win32>();
prgms.Add(azure_command_prompt);
prgms.Add(visual_studio_command_prompt);
prgms.Add(command_prompt);

// Act
Win32[] apps = Win32.DeduplicatePrograms(prgms.AsParallel());

// Assert
Assert.AreEqual(apps.Length, 3);
}
}
}
1 change: 1 addition & 0 deletions src/modules/launcher/Wox.Test/Wox.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

<ItemGroup>
<ProjectReference Include="..\Plugins\Microsoft.Plugin.Indexer\Microsoft.Plugin.Indexer.csproj" />
<ProjectReference Include="..\Plugins\Microsoft.Plugin.Program\Microsoft.Plugin.Program.csproj" />
<ProjectReference Include="..\Wox.Core\Wox.Core.csproj" />
<ProjectReference Include="..\Wox.Infrastructure\Wox.Infrastructure.csproj" />
<ProjectReference Include="..\Wox.Plugin\Wox.Plugin.csproj" />
Expand Down

0 comments on commit 2ba99d1

Please sign in to comment.