mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-15 03:07:56 +01:00
* create plugin * Update plugin code * fix deps * last changes * unix * new results and small changes * Update settings name * make spellcheck happy * new time/date formats * add comment * code cleanup, installer, signing pipeline * fix unix result * UnitTests * spell fix * Update tests, Timestamp query feature * new formats * last changes * last changes * unit tests and fixes * cjhanges and fixes * fix installer * fix settings class init * context menu * fix tests * add settings tests * update/fix DateTimeResult tests * small improvements * update pipeline * enable analyzer * fixes and improvements * spell fix * dev docs * doc fixes * spell fix * last changes * changes and fixes * fixes and test updates * improvements * last changes * try to fix tests * remove obsolete code * add info to test log * fix search * tag fix * tests * change tests * update dev docs * fix spelling * fix culture for ui strings * improvements based on feedback * improve global search * improve text * docs improvement * add settings note * fix and update tests * fix spelling
183 lines
7.8 KiB
C#
183 lines
7.8 KiB
C#
// Copyright (c) Microsoft Corporation
|
|
// 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.RegularExpressions;
|
|
using System.Windows;
|
|
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
|
|
using Wox.Infrastructure;
|
|
using Wox.Plugin;
|
|
|
|
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
|
{
|
|
/// <summary>
|
|
/// SearchController: Class tot hold the search method that filter available date time formats
|
|
/// Extra class to simplify code in <see cref="Main"/> class
|
|
/// </summary>
|
|
internal static class SearchController
|
|
{
|
|
/// <summary>
|
|
/// Var that holds the delimiter between format and date
|
|
/// </summary>
|
|
private const string InputDelimiter = "::";
|
|
|
|
/// <summary>
|
|
/// A list of conjunctions that we ignore on search
|
|
/// </summary>
|
|
private static readonly string[] _conjunctionList = Resources.Microsoft_plugin_timedate_Search_ConjunctionList.Split("; ");
|
|
|
|
/// <summary>
|
|
/// Searches for results
|
|
/// </summary>
|
|
/// <param name="query">Search query object</param>
|
|
/// <returns>List of Wox <see cref="Result"/>s.</returns>
|
|
internal static List<Result> ExecuteSearch(Query query, string iconTheme)
|
|
{
|
|
List<AvailableResult> availableFormats = new List<AvailableResult>();
|
|
List<Result> results = new List<Result>();
|
|
bool isKeywordSearch = !string.IsNullOrEmpty(query.ActionKeyword);
|
|
bool isEmptySearchInput = string.IsNullOrEmpty(query.Search);
|
|
string searchTerm = query.Search;
|
|
|
|
// Empty search without keyword => return no results
|
|
if (!isKeywordSearch && isEmptySearchInput)
|
|
{
|
|
return results;
|
|
}
|
|
|
|
// Conjunction search without keyword => return no results
|
|
// (This improves the results on global queries.)
|
|
if (!isKeywordSearch && _conjunctionList.Any(x => x.Equals(searchTerm, StringComparison.CurrentCultureIgnoreCase)))
|
|
{
|
|
return results;
|
|
}
|
|
|
|
// Switch search type
|
|
if (isEmptySearchInput)
|
|
{
|
|
// Return all results for system time/date on empty keyword search
|
|
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch));
|
|
}
|
|
else if (Regex.IsMatch(searchTerm, @".+" + Regex.Escape(InputDelimiter) + @".+"))
|
|
{
|
|
// Search for specified format with specified time/date value
|
|
var userInput = searchTerm.Split(InputDelimiter);
|
|
if (TimeAndDateHelper.ParseStringAsDateTime(userInput[1], out DateTime timestamp))
|
|
{
|
|
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch, null, null, timestamp));
|
|
searchTerm = userInput[0];
|
|
}
|
|
}
|
|
else if (TimeAndDateHelper.ParseStringAsDateTime(searchTerm, out DateTime timestamp))
|
|
{
|
|
// Return all formats for specified time/date value
|
|
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch, null, null, timestamp));
|
|
searchTerm = string.Empty;
|
|
}
|
|
else
|
|
{
|
|
// Search for specified format with system time/date
|
|
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch));
|
|
}
|
|
|
|
// Check searchTerm after getting results to select type of result list
|
|
if (string.IsNullOrEmpty(searchTerm))
|
|
{
|
|
// Generate list with all results
|
|
foreach (var f in availableFormats)
|
|
{
|
|
results.Add(new Result
|
|
{
|
|
Title = f.Value,
|
|
SubTitle = $"{f.Label} - {Resources.Microsoft_plugin_timedate_SubTitleNote}",
|
|
ToolTipData = ResultHelper.GetSearchTagToolTip(f, out Visibility v),
|
|
ToolTipVisibility = v,
|
|
IcoPath = f.GetIconPath(iconTheme),
|
|
Action = _ => ResultHelper.CopyToClipBoard(f.Value),
|
|
ContextData = f,
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Generate filtered list of results
|
|
foreach (var f in availableFormats)
|
|
{
|
|
var resultMatchScore = GetMatchScore(searchTerm, f.Label, f.AlternativeSearchTag, !isKeywordSearch);
|
|
|
|
if (resultMatchScore > 0)
|
|
{
|
|
results.Add(new Result
|
|
{
|
|
Title = f.Value,
|
|
SubTitle = $"{f.Label} - {Resources.Microsoft_plugin_timedate_SubTitleNote}",
|
|
ToolTipData = ResultHelper.GetSearchTagToolTip(f, out Visibility v),
|
|
ToolTipVisibility = v,
|
|
IcoPath = f.GetIconPath(iconTheme),
|
|
Action = _ => ResultHelper.CopyToClipBoard(f.Value),
|
|
Score = resultMatchScore,
|
|
ContextData = f,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// If search term is only a number that can't be parsed return an error message
|
|
if (!isEmptySearchInput && results.Count == 0 && searchTerm.Any(char.IsNumber) && Regex.IsMatch(searchTerm, @"\w+\d+$") &&
|
|
!searchTerm.Contains(InputDelimiter) && !searchTerm.Any(char.IsWhiteSpace) && !searchTerm.Any(char.IsPunctuation))
|
|
{
|
|
// Without plugin key word show only if message is not hidden by setting
|
|
if (isKeywordSearch || !TimeDateSettings.Instance.HideNumberMessageOnGlobalQuery)
|
|
{
|
|
results.Add(ResultHelper.CreateNumberErrorResult(iconTheme));
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks the format for a match with the user query and returns the score.
|
|
/// </summary>
|
|
/// <param name="query">The user query.</param>
|
|
/// <param name="label">The label of the format.</param>
|
|
/// <param name="tags">The search tag list as string.</param>
|
|
/// <param name="isGlobalSearch">Is this a global search?</param>
|
|
/// <returns>The score for the result.</returns>
|
|
private static int GetMatchScore(string query, string label, string tags, bool isGlobalSearch)
|
|
{
|
|
// The query is global and the first word don't match any word in the label or tags => Return score of zero
|
|
if (isGlobalSearch)
|
|
{
|
|
char[] chars = new char[] { ' ', ',', ';', '(', ')' };
|
|
string queryFirstWord = query.Split(chars)[0];
|
|
string[] words = $"{label} {tags}".Split(chars);
|
|
|
|
if (!words.Any(x => x.Trim().Equals(queryFirstWord, StringComparison.CurrentCultureIgnoreCase)))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Get match for label (or for tags if label score is <1)
|
|
int score = StringMatcher.FuzzySearch(query, label).Score;
|
|
if (score < 1)
|
|
{
|
|
foreach (string t in tags.Split(";"))
|
|
{
|
|
var tagScore = StringMatcher.FuzzySearch(query, t.Trim()).Score / 2;
|
|
if (tagScore > score)
|
|
{
|
|
score = tagScore / 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
return score;
|
|
}
|
|
}
|
|
}
|