From ca1e5d111aabbfbc043c4a1d9ad41adb221188dc Mon Sep 17 00:00:00 2001 From: Avneet Kaur <72103212+avneet-kr@users.noreply.github.com> Date: Mon, 26 Oct 2020 15:14:33 -0700 Subject: [PATCH] [fxcop] Fixes for Wox.Plugin (1of3) (#7457) * Added CultureInfo (CA1307: Specify StringComparison for clarity / CA1304: Specify CultureInfo) * Check arguments and throw ArgumentNullException (CA1062: Validate arguments of public methods) * Changed url parameter from string to System.Uri and added null checks (CA1054: URI parameters should not be strings) * Rethrow exception without specifying the exception explicitly (CA2200: Rethrow to preserve stack details) * Changed from Collection property to methods for PluginMetadata::ActionKeywords (CA2227: Collection properties should be read only) * Changed from Collection property to methods for Result::GetTitleHighlightData (CA2227: Collection properties should be read only) * Made Collection property read-only and added parameter in constructor for Result::SubTitleHighlightData (CA2227: Collection properties should be read only) * Made Collection property read only and added parameter in constructor for ResultUpdatedEventArgs::Results (CA2227: Collection properties should be read only) * CA1507: Use nameof in place of string * Removed initialization for ThemeManager::_disposed (CA1805: Do not initialize unnecessarily) * Changed Query::Terms array property to ReadOnlyCollection and added private set (CA1819: Properties should not return arrays) * CA1060: Move P/Invokes to NativeMethods class * CA1806: Do not ignore method results * CA2101: Specify marshaling for P/Invoke string arguments * Removed unnecessary empty interface IFeatures (CA1040: Avoid empty interfaces) - Removed IFeatures interface and references - Renamed IFeatures.cs to IContextMenu.cs according to guidelines * Added comments for CultureInfo (CA1307: Specify StringComparison for clarity / CA1304: Specify CultureInfo) * Added localization for Wox.Plugin and localized strings in FilesFolders.cs --- .../Sources/Result/FileItemResult.cs | 3 +- .../Sources/Result/FolderItemResult.cs | 3 +- .../UserFolderResult.cs | 3 +- .../ProgramArgumentParserTests.cs | 3 +- .../DoubleDashProgramArgumentParser.cs | 4 +- .../InferedProgramArgumentParser.cs | 4 +- .../Programs/UWPApplication.cs | 2 +- .../Programs/Win32Program.cs | 2 +- .../launcher/Wox.Core/Plugin/PluginConfig.cs | 4 +- .../launcher/Wox.Core/Plugin/PluginManager.cs | 11 +- .../launcher/Wox.Core/Plugin/QueryBuilder.cs | 5 +- .../UserSettings/PluginSettings.cs | 4 +- .../launcher/Wox.Plugin/AllowedLanguage.cs | 13 +- .../{IFeatures.cs => IContextMenu.cs} | 10 +- .../Wox.Plugin/IDelayedExecutionPlugin.cs | 2 +- .../launcher/Wox.Plugin/LocProject.json | 14 ++ .../launcher/Wox.Plugin/PluginMetadata.cs | 17 ++- src/modules/launcher/Wox.Plugin/PluginPair.cs | 8 +- .../Properties/Resources.Designer.cs | 90 ++++++++++++ .../Wox.Plugin/Properties/Resources.resx | 132 ++++++++++++++++++ src/modules/launcher/Wox.Plugin/Query.cs | 7 +- src/modules/launcher/Wox.Plugin/Result.cs | 60 +++++--- .../Wox.Plugin/ResultUpdatedEventArgs.cs | 7 +- .../Wox.Plugin/SharedCommands/FilesFolders.cs | 19 ++- .../SharedCommands/NativeMethods.cs | 23 +++ .../Wox.Plugin/SharedCommands/SearchWeb.cs | 24 +++- .../Wox.Plugin/SharedCommands/ShellCommand.cs | 21 ++- .../launcher/Wox.Plugin/ThemeManager.cs | 2 +- .../launcher/Wox.Plugin/ToolTipData.cs | 2 +- .../launcher/Wox.Plugin/Wox.Plugin.csproj | 13 ++ .../launcher/Wox.Test/QueryBuilderTest.cs | 17 +-- 31 files changed, 432 insertions(+), 97 deletions(-) rename src/modules/launcher/Wox.Plugin/{IFeatures.cs => IContextMenu.cs} (79%) create mode 100644 src/modules/launcher/Wox.Plugin/LocProject.json create mode 100644 src/modules/launcher/Wox.Plugin/Properties/Resources.Designer.cs create mode 100644 src/modules/launcher/Wox.Plugin/Properties/Resources.resx create mode 100644 src/modules/launcher/Wox.Plugin/SharedCommands/NativeMethods.cs diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FileItemResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FileItemResult.cs index 67ce726808..785ce63cca 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FileItemResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FileItemResult.cs @@ -21,12 +21,11 @@ namespace Microsoft.Plugin.Folder.Sources.Result public Wox.Plugin.Result Create(IPublicAPI contextApi) { - var result = new Wox.Plugin.Result + var result = new Wox.Plugin.Result(StringMatcher.FuzzySearch(Search, Path.GetFileName(FilePath)).MatchData) { Title = Title, SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_file_result_subtitle, FilePath), IcoPath = FilePath, - TitleHighlightData = StringMatcher.FuzzySearch(Search, Path.GetFileName(FilePath)).MatchData, Action = c => ShellAction.Execute(FilePath, contextApi), ContextData = new SearchResult { Type = ResultType.File, FullPath = FilePath }, }; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FolderItemResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FolderItemResult.cs index dd295f9413..45ee48ee50 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FolderItemResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Sources/Result/FolderItemResult.cs @@ -33,13 +33,12 @@ namespace Microsoft.Plugin.Folder.Sources.Result public Wox.Plugin.Result Create(IPublicAPI contextApi) { - return new Wox.Plugin.Result + return new Wox.Plugin.Result(StringMatcher.FuzzySearch(Search, Title).MatchData) { Title = Title, IcoPath = Path, SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Subtitle), QueryTextDisplay = Path, - TitleHighlightData = StringMatcher.FuzzySearch(Search, Title).MatchData, ContextData = new SearchResult { Type = ResultType.Folder, FullPath = Path }, Action = c => ShellAction.Execute(Path, contextApi), }; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderResult.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderResult.cs index f8bb9ce1eb..5c63f5a53e 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderResult.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/UserFolderResult.cs @@ -24,13 +24,12 @@ namespace Microsoft.Plugin.Folder public Result Create(IPublicAPI contextApi) { - return new Result + return new Result(StringMatcher.FuzzySearch(Search, Title).MatchData) { Title = Title, IcoPath = Path, SubTitle = string.Format(CultureInfo.CurrentCulture, Properties.Resources.wox_plugin_folder_select_folder_result_subtitle, Subtitle), QueryTextDisplay = Path, - TitleHighlightData = StringMatcher.FuzzySearch(Search, Title).MatchData, ContextData = new SearchResult { Type = ResultType.Folder, FullPath = Path }, Action = c => _shellAction.Execute(Path, contextApi), }; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/ProgramArgumentParser/ProgramArgumentParserTests.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/ProgramArgumentParser/ProgramArgumentParserTests.cs index c233217f48..06179a493b 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/ProgramArgumentParser/ProgramArgumentParserTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/ProgramArgumentParser/ProgramArgumentParserTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using Microsoft.Plugin.Program.ProgramArgumentParser; +using Mono.Collections.Generic; using NUnit.Framework; using Wox.Plugin; @@ -34,7 +35,7 @@ namespace Microsoft.Plugin.Program.UnitTests.ProgramArgumentParser // basic version of the Quey parser which can be found at Wox.Core.Plugin.QueryBuilder but did not want to create a project reference var splittedSearchString = inputQuery?.Split(Query.TermSeparator, System.StringSplitOptions.RemoveEmptyEntries); var cleanQuery = string.Join(Query.TermSeparator, splittedSearchString); - var query = new Query(cleanQuery, cleanQuery, splittedSearchString, string.Empty); + var query = new Query(cleanQuery, cleanQuery, new ReadOnlyCollection(splittedSearchString), string.Empty); // Act string program = null, programArguments = null; diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramArgumentParser/DoubleDashProgramArgumentParser.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramArgumentParser/DoubleDashProgramArgumentParser.cs index 7b3728623c..d9c3cab952 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramArgumentParser/DoubleDashProgramArgumentParser.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramArgumentParser/DoubleDashProgramArgumentParser.cs @@ -19,9 +19,9 @@ namespace Microsoft.Plugin.Program if (!string.IsNullOrEmpty(query?.Search)) { // First Argument is always (part of) the program, 2nd term is possibly a Program Argument - if (query.Terms.Length > 1) + if (query.Terms.Count > 1) { - for (var i = 1; i < query.Terms.Length; i++) + for (var i = 1; i < query.Terms.Count; i++) { if (!string.Equals(query.Terms[i], DoubleDash, StringComparison.Ordinal)) { diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramArgumentParser/InferedProgramArgumentParser.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramArgumentParser/InferedProgramArgumentParser.cs index 884a4771c4..b3aa16eb6f 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramArgumentParser/InferedProgramArgumentParser.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/ProgramArgumentParser/InferedProgramArgumentParser.cs @@ -19,9 +19,9 @@ namespace Microsoft.Plugin.Program if (!string.IsNullOrEmpty(query?.Search)) { // First Argument is always (part of) the program, 2nd term is possibly a Program Argument - if (query.Terms.Length > 1) + if (query.Terms.Count > 1) { - for (var i = 1; i < query.Terms.Length; i++) + for (var i = 1; i < query.Terms.Count; i++) { if (!ArgumentPrefixRegex.IsMatch(query.Terms[i])) { diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs index a148854ea4..9ac90bfc64 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/UWPApplication.cs @@ -109,7 +109,7 @@ namespace Microsoft.Plugin.Program.Programs // To set the title to always be the displayname of the packaged application result.Title = DisplayName; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, Name).MatchData; + result.SetTitleHighlightData(StringMatcher.FuzzySearch(query, Name).MatchData); var toolTipTitle = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_name, result.Title); var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_path, Package.Location); diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs index ca984da762..6c5c49fb55 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Programs/Win32Program.cs @@ -233,7 +233,7 @@ namespace Microsoft.Plugin.Program.Programs // To set the title for the result to always be the name of the application result.Title = Name; - result.TitleHighlightData = StringMatcher.FuzzySearch(query, Name).MatchData; + result.SetTitleHighlightData(StringMatcher.FuzzySearch(query, Name).MatchData); var toolTipTitle = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_name, result.Title); var toolTipText = string.Format(CultureInfo.CurrentCulture, "{0}: {1}", Properties.Resources.powertoys_run_plugin_program_file_path, FullPath); diff --git a/src/modules/launcher/Wox.Core/Plugin/PluginConfig.cs b/src/modules/launcher/Wox.Core/Plugin/PluginConfig.cs index 4c677ed9ec..ba2b284bb7 100644 --- a/src/modules/launcher/Wox.Core/Plugin/PluginConfig.cs +++ b/src/modules/launcher/Wox.Core/Plugin/PluginConfig.cs @@ -78,10 +78,10 @@ namespace Wox.Core.Plugin metadata.PluginDirectory = pluginDirectory; // for plugins which doesn't has ActionKeywords key - metadata.ActionKeywords = metadata.ActionKeywords ?? new List { metadata.ActionKeyword }; + metadata.SetActionKeywords(metadata.GetActionKeywords() ?? new List { metadata.ActionKeyword }); // for plugin still use old ActionKeyword - metadata.ActionKeyword = metadata.ActionKeywords?[0]; + metadata.ActionKeyword = metadata.GetActionKeywords()?[0]; } catch (Exception e) { diff --git a/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs b/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs index 687f5460f5..6da5acb8e1 100644 --- a/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs +++ b/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs @@ -125,7 +125,7 @@ namespace Wox.Core.Plugin } // Plugins may have multiple ActionKeywords, eg. WebSearch - plugin.Metadata.ActionKeywords.Where(x => x != Query.GlobalPluginWildcardSign) + plugin.Metadata.GetActionKeywords().Where(x => x != Query.GlobalPluginWildcardSign) .ToList() .ForEach(x => NonGlobalPlugins[x] = plugin); } @@ -244,7 +244,7 @@ namespace Wox.Core.Plugin private static bool IsGlobalPlugin(PluginMetadata metadata) { - return metadata.ActionKeywords.Contains(Query.GlobalPluginWildcardSign); + return metadata.GetActionKeywords().Contains(Query.GlobalPluginWildcardSign); } /// @@ -258,7 +258,6 @@ namespace Wox.Core.Plugin } public static IEnumerable GetPluginsForInterface() - where T : IFeatures { return AllPlugins.Where(p => p.Plugin is T); } @@ -320,7 +319,7 @@ namespace Wox.Core.Plugin NonGlobalPlugins[newActionKeyword] = plugin; } - plugin.Metadata.ActionKeywords.Add(newActionKeyword); + plugin.Metadata.GetActionKeywords().Add(newActionKeyword); } /// @@ -332,7 +331,7 @@ namespace Wox.Core.Plugin var plugin = GetPluginForId(id); if (oldActionkeyword == Query.GlobalPluginWildcardSign && // Plugins may have multiple ActionKeywords that are global, eg. WebSearch - plugin.Metadata.ActionKeywords + plugin.Metadata.GetActionKeywords() .Where(x => x == Query.GlobalPluginWildcardSign) .ToList() .Count == 1) @@ -345,7 +344,7 @@ namespace Wox.Core.Plugin NonGlobalPlugins.Remove(oldActionkeyword); } - plugin.Metadata.ActionKeywords.Remove(oldActionkeyword); + plugin.Metadata.GetActionKeywords().Remove(oldActionkeyword); } public static void ReplaceActionKeyword(string id, string oldActionKeyword, string newActionKeyword) diff --git a/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs b/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs index 6db5def1c5..90e86f67b9 100644 --- a/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs +++ b/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Mono.Collections.Generic; using Wox.Plugin; namespace Wox.Core.Plugin @@ -63,7 +64,7 @@ namespace Wox.Core.Plugin } // A new query is constructed for each plugin as they have different action keywords - var query = new Query(rawQuery, search, terms, pluginActionKeyword); + var query = new Query(rawQuery, search, new ReadOnlyCollection(terms), pluginActionKeyword); pluginQueryPair.TryAdd(pluginPair, query); } @@ -80,7 +81,7 @@ namespace Wox.Core.Plugin { if (!pluginQueryPair.ContainsKey(globalPlugin)) { - var query = new Query(rawQuery, rawQuery, terms, string.Empty); + var query = new Query(rawQuery, rawQuery, new ReadOnlyCollection(terms), string.Empty); pluginQueryPair.Add(globalPlugin, query); } } diff --git a/src/modules/launcher/Wox.Infrastructure/UserSettings/PluginSettings.cs b/src/modules/launcher/Wox.Infrastructure/UserSettings/PluginSettings.cs index d975aa6f79..196af9ff49 100644 --- a/src/modules/launcher/Wox.Infrastructure/UserSettings/PluginSettings.cs +++ b/src/modules/launcher/Wox.Infrastructure/UserSettings/PluginSettings.cs @@ -20,7 +20,7 @@ namespace Wox.Infrastructure.UserSettings var settings = Plugins[metadata.ID]; if (settings.ActionKeywords?.Count > 0) { - metadata.ActionKeywords = settings.ActionKeywords; + metadata.SetActionKeywords(settings.ActionKeywords); metadata.ActionKeyword = settings.ActionKeywords[0]; } @@ -32,7 +32,7 @@ namespace Wox.Infrastructure.UserSettings { ID = metadata.ID, Name = metadata.Name, - ActionKeywords = metadata.ActionKeywords, + ActionKeywords = metadata.GetActionKeywords(), Disabled = metadata.Disabled, }; } diff --git a/src/modules/launcher/Wox.Plugin/AllowedLanguage.cs b/src/modules/launcher/Wox.Plugin/AllowedLanguage.cs index fcd9d44303..8922b89064 100644 --- a/src/modules/launcher/Wox.Plugin/AllowedLanguage.cs +++ b/src/modules/launcher/Wox.Plugin/AllowedLanguage.cs @@ -2,6 +2,9 @@ // 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.Globalization; + namespace Wox.Plugin { public static class AllowedLanguage @@ -18,8 +21,14 @@ namespace Wox.Plugin public static bool IsAllowed(string language) { - return language.ToUpper() == CSharp.ToUpper() - || language.ToUpper() == Executable.ToUpper(); + if (language == null) + { + throw new ArgumentNullException(nameof(language)); + } + + // Using InvariantCulture since this is a command line arg + return language.ToUpper(CultureInfo.InvariantCulture) == CSharp.ToUpper(CultureInfo.InvariantCulture) + || language.ToUpper(CultureInfo.InvariantCulture) == Executable.ToUpper(CultureInfo.InvariantCulture); } } } diff --git a/src/modules/launcher/Wox.Plugin/IFeatures.cs b/src/modules/launcher/Wox.Plugin/IContextMenu.cs similarity index 79% rename from src/modules/launcher/Wox.Plugin/IFeatures.cs rename to src/modules/launcher/Wox.Plugin/IContextMenu.cs index d4b5b66cdc..78ccead1d2 100644 --- a/src/modules/launcher/Wox.Plugin/IFeatures.cs +++ b/src/modules/launcher/Wox.Plugin/IContextMenu.cs @@ -6,11 +6,7 @@ using System.Collections.Generic; namespace Wox.Plugin { - public interface IFeatures - { - } - - public interface IContextMenu : IFeatures + public interface IContextMenu { List LoadContextMenus(Result selectedResult); } @@ -18,14 +14,14 @@ namespace Wox.Plugin /// /// Represent plugins that support internationalization /// - public interface IPluginI18n : IFeatures + public interface IPluginI18n { string GetTranslatedPluginTitle(); string GetTranslatedPluginDescription(); } - public interface IResultUpdated : IFeatures + public interface IResultUpdated { event ResultUpdatedEventHandler ResultsUpdated; } diff --git a/src/modules/launcher/Wox.Plugin/IDelayedExecutionPlugin.cs b/src/modules/launcher/Wox.Plugin/IDelayedExecutionPlugin.cs index 2075258863..5254643f24 100644 --- a/src/modules/launcher/Wox.Plugin/IDelayedExecutionPlugin.cs +++ b/src/modules/launcher/Wox.Plugin/IDelayedExecutionPlugin.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; namespace Wox.Plugin { - public interface IDelayedExecutionPlugin : IFeatures + public interface IDelayedExecutionPlugin { List Query(Query query, bool delayedExecution); } diff --git a/src/modules/launcher/Wox.Plugin/LocProject.json b/src/modules/launcher/Wox.Plugin/LocProject.json new file mode 100644 index 0000000000..1c5684f6f3 --- /dev/null +++ b/src/modules/launcher/Wox.Plugin/LocProject.json @@ -0,0 +1,14 @@ +{ + "Projects": [ + { + "LanguageSet": "Azure_Languages", + "LocItems": [ + { + "SourceFile": "src\\modules\\launcher\\Wox.Plugin\\Properties\\Resources.resx", + "CopyOption": "LangIDOnName", + "OutputPath": "src\\modules\\launcher\\Wox.Plugin\\Properties" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/modules/launcher/Wox.Plugin/PluginMetadata.cs b/src/modules/launcher/Wox.Plugin/PluginMetadata.cs index 5f805c1426..657dfe26ba 100644 --- a/src/modules/launcher/Wox.Plugin/PluginMetadata.cs +++ b/src/modules/launcher/Wox.Plugin/PluginMetadata.cs @@ -14,6 +14,13 @@ namespace Wox.Plugin { private string _pluginDirectory; + private List _actionKeywords; + + public PluginMetadata(List actionKeywords = null) + { + _actionKeywords = actionKeywords; + } + public string ID { get; set; } public string Name { get; set; } @@ -51,7 +58,15 @@ namespace Wox.Plugin public string ActionKeyword { get; set; } - public List ActionKeywords { get; set; } + public List GetActionKeywords() + { + return _actionKeywords; + } + + public void SetActionKeywords(List value) + { + _actionKeywords = value; + } public string IcoPath { get; set; } diff --git a/src/modules/launcher/Wox.Plugin/PluginPair.cs b/src/modules/launcher/Wox.Plugin/PluginPair.cs index c853ecc1f6..6bb94815b0 100644 --- a/src/modules/launcher/Wox.Plugin/PluginPair.cs +++ b/src/modules/launcher/Wox.Plugin/PluginPair.cs @@ -2,6 +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; + namespace Wox.Plugin { public class PluginPair @@ -19,7 +21,8 @@ namespace Wox.Plugin { if (obj is PluginPair r) { - return string.Equals(r.Metadata.ID, Metadata.ID); + // Using Ordinal since this is used internally + return string.Equals(r.Metadata.ID, Metadata.ID, StringComparison.Ordinal); } else { @@ -29,7 +32,8 @@ namespace Wox.Plugin public override int GetHashCode() { - var hashcode = Metadata.ID?.GetHashCode() ?? 0; + // Using Ordinal since this is used internally + var hashcode = Metadata.ID?.GetHashCode(StringComparison.Ordinal) ?? 0; return hashcode; } } diff --git a/src/modules/launcher/Wox.Plugin/Properties/Resources.Designer.cs b/src/modules/launcher/Wox.Plugin/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..ca18b9aee7 --- /dev/null +++ b/src/modules/launcher/Wox.Plugin/Properties/Resources.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Wox.Plugin.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Wox.Plugin.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Copying path {0} has failed, it will now be deleted for consistency. + /// + public static string filesfolder_copy_failed { + get { + return ResourceManager.GetString("filesfolder_copy_failed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not able to delete folder {0}, please go to the location and manually delete it. + /// + public static string filesfolder_removefolder_failed { + get { + return ResourceManager.GetString("filesfolder_removefolder_failed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unable to verify folders and files between {0} and {1}. + /// + public static string filesfolder_verifybothfolderfilesequal_failed { + get { + return ResourceManager.GetString("filesfolder_verifybothfolderfilesequal_failed", resourceCulture); + } + } + } +} diff --git a/src/modules/launcher/Wox.Plugin/Properties/Resources.resx b/src/modules/launcher/Wox.Plugin/Properties/Resources.resx new file mode 100644 index 0000000000..c5d21a23d5 --- /dev/null +++ b/src/modules/launcher/Wox.Plugin/Properties/Resources.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Copying path {0} has failed, it will now be deleted for consistency + parameter: targetPath + + + Not able to delete folder {0}, please go to the location and manually delete it + parameter: path + + + Unable to verify folders and files between {0} and {1} + paramaters: fromPath, toPath + + \ No newline at end of file diff --git a/src/modules/launcher/Wox.Plugin/Query.cs b/src/modules/launcher/Wox.Plugin/Query.cs index af6adb8e13..dc7f4e8606 100644 --- a/src/modules/launcher/Wox.Plugin/Query.cs +++ b/src/modules/launcher/Wox.Plugin/Query.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Mono.Collections.Generic; namespace Wox.Plugin { @@ -18,7 +19,7 @@ namespace Wox.Plugin /// Initializes a new instance of the class. /// to allow unit tests for plug ins /// - public Query(string rawQuery, string search, string[] terms, string actionKeyword = "") + public Query(string rawQuery, string search, ReadOnlyCollection terms, string actionKeyword = "") { Search = search; RawQuery = rawQuery; @@ -41,9 +42,9 @@ namespace Wox.Plugin public string Search { get; internal set; } /// - /// Gets or sets the raw query splited into a string array. + /// Gets the raw query splited into a string array. /// - public string[] Terms { get; set; } + public ReadOnlyCollection Terms { get; private set; } /// /// Query can be splited into multiple terms by whitespace diff --git a/src/modules/launcher/Wox.Plugin/Result.cs b/src/modules/launcher/Wox.Plugin/Result.cs index 6e4ecd06b2..2bcae0d53d 100644 --- a/src/modules/launcher/Wox.Plugin/Result.cs +++ b/src/modules/launcher/Wox.Plugin/Result.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Windows; using System.Windows.Media; @@ -16,6 +17,7 @@ namespace Wox.Plugin private ToolTipData _toolTipData; private string _pluginDirectory; private string _icoPath; + private IList _titleHighlightData; public string Title { @@ -26,7 +28,13 @@ namespace Wox.Plugin set { - _title = value.Replace("\n", " "); + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + // Using Ordinal since this is used internally + _title = value.Replace("\n", " ", StringComparison.Ordinal); } } @@ -90,15 +98,32 @@ namespace Wox.Plugin public int Score { get; set; } - /// - /// Gets or sets a list of indexes for the characters to be highlighted in Title - /// - public IList TitleHighlightData { get; set; } + public Result(IList titleHighlightData = null, IList subTitleHighlightData = null) + { + _titleHighlightData = titleHighlightData; + SubTitleHighlightData = subTitleHighlightData; + } /// - /// Gets or sets a list of indexes for the characters to be highlighted in SubTitle + /// Gets a list of indexes for the characters to be highlighted in Title /// - public IList SubTitleHighlightData { get; set; } + public IList GetTitleHighlightData() + { + return _titleHighlightData; + } + + /// + /// Sets a list of indexes for the characters to be highlighted in Title + /// + public void SetTitleHighlightData(IList value) + { + _titleHighlightData = value; + } + + /// + /// Gets a list of indexes for the characters to be highlighted in SubTitle + /// + public IList SubTitleHighlightData { get; private set; } /// /// Gets or sets only results that originQuery match with current query will be displayed in the panel @@ -129,10 +154,11 @@ namespace Wox.Plugin { var r = obj as Result; - var equality = string.Equals(r?.Title, Title) && - string.Equals(r?.SubTitle, SubTitle) && - string.Equals(r?.IcoPath, IcoPath) && - TitleHighlightData == r.TitleHighlightData && + // Using Ordinal since this is used internally + var equality = string.Equals(r?.Title, Title, StringComparison.Ordinal) && + string.Equals(r?.SubTitle, SubTitle, StringComparison.Ordinal) && + string.Equals(r?.IcoPath, IcoPath, StringComparison.Ordinal) && + GetTitleHighlightData() == r.GetTitleHighlightData() && SubTitleHighlightData == r.SubTitleHighlightData; return equality; @@ -140,18 +166,16 @@ namespace Wox.Plugin public override int GetHashCode() { - var hashcode = (Title?.GetHashCode() ?? 0) ^ - (SubTitle?.GetHashCode() ?? 0); + // Using Ordinal since this is used internally + var hashcode = (Title?.GetHashCode(StringComparison.Ordinal) ?? 0) ^ + (SubTitle?.GetHashCode(StringComparison.Ordinal) ?? 0); return hashcode; } public override string ToString() { - return string.Format("{0} : {1}", Title, SubTitle); - } - - public Result() - { + // Using CurrentCulture since this is user facing + return string.Format(CultureInfo.CurrentCulture, "{0} : {1}", Title, SubTitle); } /// diff --git a/src/modules/launcher/Wox.Plugin/ResultUpdatedEventArgs.cs b/src/modules/launcher/Wox.Plugin/ResultUpdatedEventArgs.cs index 3afd2ffcbc..ec66322032 100644 --- a/src/modules/launcher/Wox.Plugin/ResultUpdatedEventArgs.cs +++ b/src/modules/launcher/Wox.Plugin/ResultUpdatedEventArgs.cs @@ -9,7 +9,12 @@ namespace Wox.Plugin { public class ResultUpdatedEventArgs : EventArgs { - public List Results { get; set; } + public List Results { get; private set; } + + public ResultUpdatedEventArgs(List results = null) + { + Results = results; + } public Query Query { get; set; } } diff --git a/src/modules/launcher/Wox.Plugin/SharedCommands/FilesFolders.cs b/src/modules/launcher/Wox.Plugin/SharedCommands/FilesFolders.cs index 5e59093b8c..82158e8b10 100644 --- a/src/modules/launcher/Wox.Plugin/SharedCommands/FilesFolders.cs +++ b/src/modules/launcher/Wox.Plugin/SharedCommands/FilesFolders.cs @@ -3,9 +3,11 @@ // See the LICENSE file in the project root for more information. using System; +using System.Globalization; using System.IO; using System.Reflection; using Wox.Plugin.Logger; +using Wox.Plugin.Properties; namespace Wox.Plugin.SharedCommands { @@ -56,9 +58,10 @@ namespace Wox.Plugin.SharedCommands string error = $"Copying path {targetPath} has failed"; Log.Exception(error, e, MethodBase.GetCurrentMethod().DeclaringType); #if DEBUG - throw e; + throw; #else - System.Windows.MessageBox.Show(string.Format("Copying path {0} has failed, it will now be deleted for consistency", targetPath)); + // Using CurrentCulture since this is user facing + System.Windows.MessageBox.Show(string.Format(CultureInfo.CurrentCulture, Resources.filesfolder_copy_failed, targetPath)); RemoveFolder(targetPath); #endif } @@ -91,9 +94,10 @@ namespace Wox.Plugin.SharedCommands string error = $"Unable to verify folders and files between {fromPath} and {toPath}"; Log.Exception(error, e, MethodBase.GetCurrentMethod().DeclaringType); #if DEBUG - throw e; + throw; #else - System.Windows.MessageBox.Show(string.Format(error)); + // Using CurrentCulture since this is user facing + System.Windows.MessageBox.Show(string.Format(CultureInfo.CurrentCulture, Resources.filesfolder_verifybothfolderfilesequal_failed, fromPath, toPath)); return false; #endif } @@ -113,12 +117,13 @@ namespace Wox.Plugin.SharedCommands catch (Exception e) #pragma warning restore CS0168 // Variable is declared but never used { - string error = $"Not able to delete folder {path}, please go to the location and manually delete it"; + string error = $"Not able to delete folder {path}"; Log.Exception(error, e, MethodBase.GetCurrentMethod().DeclaringType); #if DEBUG - throw e; + throw; #else - System.Windows.MessageBox.Show(string.Format(error)); + // Using CurrentCulture since this is user facing + System.Windows.MessageBox.Show(string.Format(CultureInfo.CurrentCulture, Resources.filesfolder_removefolder_failed, path)); #endif } } diff --git a/src/modules/launcher/Wox.Plugin/SharedCommands/NativeMethods.cs b/src/modules/launcher/Wox.Plugin/SharedCommands/NativeMethods.cs new file mode 100644 index 0000000000..d03f1cc252 --- /dev/null +++ b/src/modules/launcher/Wox.Plugin/SharedCommands/NativeMethods.cs @@ -0,0 +1,23 @@ +// 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.Runtime.InteropServices; +using System.Text; +using static Wox.Plugin.SharedCommands.ShellCommand; + +namespace Wox.Plugin.SharedCommands +{ + internal static class NativeMethods + { + [DllImport("user32.dll")] + public static extern bool EnumThreadWindows(uint threadId, EnumThreadDelegate lpfn, IntPtr lParam); + + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + public static extern int GetWindowText(IntPtr hwnd, StringBuilder lpString, int nMaxCount); + + [DllImport("user32.dll")] + public static extern int GetWindowTextLength(IntPtr hwnd); + } +} diff --git a/src/modules/launcher/Wox.Plugin/SharedCommands/SearchWeb.cs b/src/modules/launcher/Wox.Plugin/SharedCommands/SearchWeb.cs index 6ecd1c57b0..224c7a830d 100644 --- a/src/modules/launcher/Wox.Plugin/SharedCommands/SearchWeb.cs +++ b/src/modules/launcher/Wox.Plugin/SharedCommands/SearchWeb.cs @@ -15,8 +15,13 @@ namespace Wox.Plugin.SharedCommands /// Opens search in a new browser. If no browser path is passed in then Chrome is used. /// Leave browser path blank to use Chrome. /// - public static void NewBrowserWindow(this string url, string browserPath) + public static void NewBrowserWindow(this Uri url, string browserPath) { + if (url == null) + { + throw new ArgumentNullException(nameof(url)); + } + var browserExecutableName = browserPath? .Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None) .Last(); @@ -24,7 +29,7 @@ namespace Wox.Plugin.SharedCommands var browser = string.IsNullOrEmpty(browserExecutableName) ? "chrome" : browserPath; // Internet Explorer will open url in new browser window, and does not take the --new-window parameter - var browserArguments = browserExecutableName == "iexplore.exe" ? url : "--new-window " + url; + var browserArguments = browserExecutableName == "iexplore.exe" ? url.AbsoluteUri : "--new-window " + url.AbsoluteUri; try { @@ -34,7 +39,7 @@ namespace Wox.Plugin.SharedCommands { var psi = new ProcessStartInfo { - FileName = url, + FileName = url.AbsoluteUri, UseShellExecute = true, }; Process.Start(psi); @@ -44,24 +49,29 @@ namespace Wox.Plugin.SharedCommands /// /// Opens search as a tab in the default browser chosen in Windows settings. /// - public static void NewTabInBrowser(this string url, string browserPath) + public static void NewTabInBrowser(this Uri url, string browserPath) { + if (url == null) + { + throw new ArgumentNullException(nameof(url)); + } + try { if (!string.IsNullOrEmpty(browserPath)) { - Process.Start(browserPath, url); + Process.Start(browserPath, url.AbsoluteUri); } else { - Process.Start(url); + Process.Start(url.AbsoluteUri); } } // This error may be thrown for Process.Start(browserPath, url) catch (System.ComponentModel.Win32Exception) { - Process.Start(url); + Process.Start(url.AbsoluteUri); } } } diff --git a/src/modules/launcher/Wox.Plugin/SharedCommands/ShellCommand.cs b/src/modules/launcher/Wox.Plugin/SharedCommands/ShellCommand.cs index 8d95741812..97414b9036 100644 --- a/src/modules/launcher/Wox.Plugin/SharedCommands/ShellCommand.cs +++ b/src/modules/launcher/Wox.Plugin/SharedCommands/ShellCommand.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; -using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -14,19 +13,15 @@ namespace Wox.Plugin.SharedCommands { public delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam); - [DllImport("user32.dll")] - private static extern bool EnumThreadWindows(uint threadId, EnumThreadDelegate lpfn, IntPtr lParam); - - [DllImport("user32.dll")] - private static extern int GetWindowText(IntPtr hwnd, StringBuilder lpString, int nMaxCount); - - [DllImport("user32.dll")] - private static extern int GetWindowTextLength(IntPtr hwnd); - private static bool containsSecurityWindow; public static Process RunAsDifferentUser(ProcessStartInfo processStartInfo) { + if (processStartInfo == null) + { + throw new ArgumentNullException(nameof(processStartInfo)); + } + processStartInfo.Verb = "RunAsUser"; var process = Process.Start(processStartInfo); @@ -55,7 +50,7 @@ namespace Wox.Plugin.SharedCommands ProcessThreadCollection ptc = Process.GetCurrentProcess().Threads; for (int i = 0; i < ptc.Count; i++) { - EnumThreadWindows((uint)ptc[i].Id, CheckSecurityThread, IntPtr.Zero); + NativeMethods.EnumThreadWindows((uint)ptc[i].Id, CheckSecurityThread, IntPtr.Zero); } } @@ -71,8 +66,8 @@ namespace Wox.Plugin.SharedCommands private static string GetWindowTitle(IntPtr hwnd) { - StringBuilder sb = new StringBuilder(GetWindowTextLength(hwnd) + 1); - GetWindowText(hwnd, sb, sb.Capacity); + StringBuilder sb = new StringBuilder(NativeMethods.GetWindowTextLength(hwnd) + 1); + _ = NativeMethods.GetWindowText(hwnd, sb, sb.Capacity); return sb.ToString(); } diff --git a/src/modules/launcher/Wox.Plugin/ThemeManager.cs b/src/modules/launcher/Wox.Plugin/ThemeManager.cs index ee45ae06d9..55b3cdcc06 100644 --- a/src/modules/launcher/Wox.Plugin/ThemeManager.cs +++ b/src/modules/launcher/Wox.Plugin/ThemeManager.cs @@ -22,7 +22,7 @@ namespace Wox.Plugin private const string HighContrastWhiteTheme = "HighContrast.Accent5"; private Theme currentTheme; - private bool _disposed = false; + private bool _disposed; public event ThemeChangedHandler ThemeChanged; diff --git a/src/modules/launcher/Wox.Plugin/ToolTipData.cs b/src/modules/launcher/Wox.Plugin/ToolTipData.cs index 9224f5adbb..c558613686 100644 --- a/src/modules/launcher/Wox.Plugin/ToolTipData.cs +++ b/src/modules/launcher/Wox.Plugin/ToolTipData.cs @@ -16,7 +16,7 @@ namespace Wox.Plugin { if (string.IsNullOrEmpty(title)) { - throw new ArgumentException("title cannot be null or empty", "title"); + throw new ArgumentException("title cannot be null or empty", nameof(title)); } Title = title; diff --git a/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj b/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj index 94c616503c..6a6be6bf91 100644 --- a/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj +++ b/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj @@ -96,4 +96,17 @@ all + + + True + True + Resources.resx + + + + + PublicResXFileCodeGenerator + Resources.Designer.cs + + \ No newline at end of file diff --git a/src/modules/launcher/Wox.Test/QueryBuilderTest.cs b/src/modules/launcher/Wox.Test/QueryBuilderTest.cs index f7cbae3ed7..2b99aea2bd 100644 --- a/src/modules/launcher/Wox.Test/QueryBuilderTest.cs +++ b/src/modules/launcher/Wox.Test/QueryBuilderTest.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Mono.Collections.Generic; using NUnit.Framework; using Wox.Core.Plugin; using Wox.Plugin; @@ -26,7 +27,7 @@ namespace Wox.Test // Arrange var nonGlobalPlugins = new Dictionary { - { ">", new PluginPair { Metadata = new PluginMetadata { ActionKeywords = new List { ">" } } } }, + { ">", new PluginPair { Metadata = new PluginMetadata(new List { ">" } ) } }, }; string searchQuery = "> file.txt file2 file3"; @@ -43,7 +44,7 @@ namespace Wox.Test // Arrange var nonGlobalPlugins = new Dictionary { - { ">", new PluginPair { Metadata = new PluginMetadata { ActionKeywords = new List { ">" }, Disabled = true } } }, + { ">", new PluginPair { Metadata = new PluginMetadata(new List { ">" }) { Disabled = true } } }, }; string searchQuery = "> file.txt file2 file3"; @@ -72,7 +73,7 @@ namespace Wox.Test { // Arrange string searchQuery = "> query"; - var firstPlugin = new PluginPair { Metadata = new PluginMetadata { ActionKeywords = new List { ">" } } }; + var firstPlugin = new PluginPair { Metadata = new PluginMetadata(new List { ">" } ) }; var secondPlugin = new PluginPair { Metadata = new PluginMetadata { ActionKeyword = ">" } }; var nonGlobalPluginWithActionKeywords = new Dictionary @@ -85,7 +86,7 @@ namespace Wox.Test { ">", secondPlugin }, }; string[] terms = { ">", "query" }; - Query expectedQuery = new Query("> query", "query", terms, ">"); + Query expectedQuery = new Query("> query", "query", new ReadOnlyCollection(terms), ">"); // Act var queriesForPluginsWithActionKeywords = QueryBuilder.Build(ref searchQuery, nonGlobalPluginWithActionKeywords); @@ -103,7 +104,7 @@ namespace Wox.Test public void QueryBuilderShouldGenerateCorrectQueriesForPluginsWithMultipleActionKeywords() { // Arrange - var plugin = new PluginPair { Metadata = new PluginMetadata { ActionKeywords = new List { "a", "b" } } }; + var plugin = new PluginPair { Metadata = new PluginMetadata(new List { "a", "b" } ) }; var nonGlobalPlugins = new Dictionary { { "a", plugin }, @@ -129,7 +130,7 @@ namespace Wox.Test public void QueryBuildShouldGenerateSameSearchQueryWithOrWithoutSpaceAfterActionKeyword() { // Arrange - var plugin = new PluginPair { Metadata = new PluginMetadata { ActionKeywords = new List { "a" } } }; + var plugin = new PluginPair { Metadata = new PluginMetadata(new List { "a" } ) }; var nonGlobalPlugins = new Dictionary { { "a", plugin }, @@ -198,8 +199,8 @@ namespace Wox.Test // Assert // Using Ordinal since this is used internally - Assert.IsTrue(firstQuery.Terms[0].Equals("cd", StringComparison.Ordinal) && firstQuery.Terms[1].Equals("efgh", StringComparison.Ordinal) && firstQuery.Terms.Length == 2); - Assert.IsTrue(secondQuery.Terms[0].Equals("efgh", StringComparison.Ordinal) && secondQuery.Terms.Length == 1); + Assert.IsTrue(firstQuery.Terms[0].Equals("cd", StringComparison.Ordinal) && firstQuery.Terms[1].Equals("efgh", StringComparison.Ordinal) && firstQuery.Terms.Count == 2); + Assert.IsTrue(secondQuery.Terms[0].Equals("efgh", StringComparison.Ordinal) && secondQuery.Terms.Count == 1); } } }