diff --git a/Flow.Launcher.Infrastructure/StringMatcher.cs b/Flow.Launcher.Infrastructure/StringMatcher.cs index 3ffa9f7b1..46165a849 100644 --- a/Flow.Launcher.Infrastructure/StringMatcher.cs +++ b/Flow.Launcher.Infrastructure/StringMatcher.cs @@ -202,7 +202,11 @@ namespace Flow.Launcher.Infrastructure if (allQuerySubstringsMatched) { var nearestSpaceIndex = CalculateClosestSpaceIndex(spaceIndices, firstMatchIndex); - var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, + + // firstMatchIndex - nearestSpaceIndex - 1 is to set the firstIndex as the index of the first matched char + // preceded by a space e.g. 'world' matching 'hello world' firstIndex would be 0 not 6 + // giving more weight than 'we or donald' by allowing the distance calculation to treat the starting position at after the space. + var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, spaceIndices, lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString); var resultList = indexList.Select(x => translationMapping?.MapToOriginalIndex(x) ?? x).Distinct().ToList(); @@ -296,7 +300,7 @@ namespace Flow.Launcher.Infrastructure return currentQuerySubstringIndex >= querySubstringsLength; } - private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen, + private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, List spaceIndices, int matchLen, bool allSubstringsContainedInCompareString) { // A match found near the beginning of a string is scored more than a match found near the end @@ -304,6 +308,14 @@ namespace Flow.Launcher.Infrastructure // while the score is lower if they are more spread out var score = 100 * (query.Length + 1) / ((1 + firstIndex) + (matchLen + 1)); + // Give more weight to a match that is closer to the start of the string. + // if the first matched char is immediately before space and all strings are contained in the compare string e.g. 'world' matching 'hello world' + // and 'world hello', because both have 'world' immediately preceded by space, their firstIndex will be 0 when distance is calculated, + // to prevent them scoring the same, we adjust the score by deducting the number of spaces it has from the start of the string, so 'world hello' + // will score slightly higher than 'hello world' because 'hello world' has one additional space. + if (firstIndex == 0 && allSubstringsContainedInCompareString) + score -= spaceIndices.Count; + // A match with less characters assigning more weights if (stringToCompare.Length - query.Length < 5) { diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index 7633e34a7..a1d3b83ab 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -37,7 +37,11 @@ namespace Flow.Launcher.Plugin /// user's clipboard when Ctrl + C is pressed on a result. If the text is a file/directory path /// flow will copy the actual file/folder instead of just the path text. /// - public string CopyText { get; set; } = string.Empty; + public string CopyText + { + get => string.IsNullOrEmpty(_copyText) ? SubTitle : _copyText; + set => _copyText = value; + } /// /// This holds the text which can be provided by plugin to help Flow autocomplete text @@ -81,6 +85,7 @@ namespace Flow.Launcher.Plugin /// Delegate to Get Image Source /// public IconDelegate Icon; + private string _copyText = string.Empty; /// /// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons) diff --git a/Flow.Launcher.Test/FuzzyMatcherTest.cs b/Flow.Launcher.Test/FuzzyMatcherTest.cs index bbddcbd2a..46c848c7a 100644 --- a/Flow.Launcher.Test/FuzzyMatcherTest.cs +++ b/Flow.Launcher.Test/FuzzyMatcherTest.cs @@ -129,14 +129,20 @@ namespace Flow.Launcher.Test } } + + /// + /// These are standard match scenarios + /// The intention of this test is provide a bench mark for how much the score has increased from a change. + /// Usually the increase in scoring should not be drastic, increase of less than 10 is acceptable. + /// [TestCase(Chrome, Chrome, 157)] - [TestCase(Chrome, LastIsChrome, 147)] + [TestCase(Chrome, LastIsChrome, 145)] [TestCase("chro", HelpCureHopeRaiseOnMindEntityChrome, 50)] [TestCase("chr", HelpCureHopeRaiseOnMindEntityChrome, 30)] [TestCase(Chrome, UninstallOrChangeProgramsOnYourComputer, 21)] [TestCase(Chrome, CandyCrushSagaFromKing, 0)] - [TestCase("sql", MicrosoftSqlServerManagementStudio, 110)] - [TestCase("sql manag", MicrosoftSqlServerManagementStudio, 121)] //double spacing intended + [TestCase("sql", MicrosoftSqlServerManagementStudio, 109)] + [TestCase("sql manag", MicrosoftSqlServerManagementStudio, 120)] //double spacing intended public void WhenGivenQueryString_ThenShouldReturn_TheDesiredScoring( string queryString, string compareString, int expectedScore) { @@ -275,7 +281,40 @@ namespace Flow.Launcher.Test $"Query: \"{queryString}\"{Environment.NewLine} " + $"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" + $"Should be greater than{Environment.NewLine}" + - $"CompareString2: \"{compareString2}\", Score: {compareString1Result.Score}{Environment.NewLine}"); + $"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}"); + } + + [TestCase("red", "red colour", "metro red")] + [TestCase("red", "this red colour", "this colour red")] + [TestCase("red", "this red colour", "this colour is very red")] + [TestCase("red", "this red colour", "this colour is surprisingly super awesome red and cool")] + [TestCase("red", "this colour is surprisingly super red very and cool", "this colour is surprisingly super very red and cool")] + public void WhenGivenTwoStrings_Scoring_ShouldGiveMoreWeightToTheStringCloserToIndexZero( + string queryString, string compareString1, string compareString2) + { + // When + var matcher = new StringMatcher { UserSettingSearchPrecision = SearchPrecisionScore.Regular }; + + // Given + var compareString1Result = matcher.FuzzyMatch(queryString, compareString1); + var compareString2Result = matcher.FuzzyMatch(queryString, compareString2); + + Debug.WriteLine(""); + Debug.WriteLine("###############################################"); + Debug.WriteLine($"QueryString: \"{queryString}\"{Environment.NewLine}"); + Debug.WriteLine( + $"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}"); + Debug.WriteLine( + $"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}"); + Debug.WriteLine("###############################################"); + Debug.WriteLine(""); + + // Should + Assert.True(compareString1Result.Score > compareString2Result.Score, + $"Query: \"{queryString}\"{Environment.NewLine} " + + $"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" + + $"Should be greater than{Environment.NewLine}" + + $"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}"); } [TestCase("vim", "Vim", "ignoreDescription", "ignore.exe", "Vim Diff", "ignoreDescription", "ignore.exe")] diff --git a/Flow.Launcher/MainWindow.xaml b/Flow.Launcher/MainWindow.xaml index 6f3915076..922f8b933 100644 --- a/Flow.Launcher/MainWindow.xaml +++ b/Flow.Launcher/MainWindow.xaml @@ -300,7 +300,7 @@ + PreviewMouseLeftButtonUp="OnPreviewMouseButtonDown" /> diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 630daf42e..6a65a38f2 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.ComponentModel; using System.Threading.Tasks; using System.Windows; @@ -20,6 +20,11 @@ using Flow.Launcher.Infrastructure; using System.Windows.Media; using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Plugin.SharedCommands; +using System.Text; +using DataObject = System.Windows.DataObject; +using System.Diagnostics; +using Microsoft.AspNetCore.Http; +using System.IO; using System.Windows.Threading; using System.Windows.Data; diff --git a/Flow.Launcher/ResultListBox.xaml b/Flow.Launcher/ResultListBox.xaml index 5c497b925..1ece390d1 100644 --- a/Flow.Launcher/ResultListBox.xaml +++ b/Flow.Launcher/ResultListBox.xaml @@ -25,12 +25,15 @@ VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Standard" Visibility="{Binding Visbility}" - mc:Ignorable="d"> + mc:Ignorable="d" + PreviewMouseMove="ResultList_MouseMove" + PreviewMouseLeftButtonDown="ResultList_PreviewMouseLeftButtonDown"> -