[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
This commit is contained in:
Avneet Kaur
2020-10-26 15:14:33 -07:00
committed by GitHub
parent 3906896947
commit ca1e5d111a
31 changed files with 432 additions and 97 deletions

View File

@@ -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 },
};

View File

@@ -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),
};

View File

@@ -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),
};

View File

@@ -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<string>(splittedSearchString), string.Empty);
// Act
string program = null, programArguments = null;

View File

@@ -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))
{

View File

@@ -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]))
{

View File

@@ -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);

View File

@@ -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);

View File

@@ -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<string> { metadata.ActionKeyword };
metadata.SetActionKeywords(metadata.GetActionKeywords() ?? new List<string> { metadata.ActionKeyword });
// for plugin still use old ActionKeyword
metadata.ActionKeyword = metadata.ActionKeywords?[0];
metadata.ActionKeyword = metadata.GetActionKeywords()?[0];
}
catch (Exception e)
{

View File

@@ -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);
}
/// <summary>
@@ -258,7 +258,6 @@ namespace Wox.Core.Plugin
}
public static IEnumerable<PluginPair> GetPluginsForInterface<T>()
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);
}
/// <summary>
@@ -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)

View File

@@ -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<string>(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<string>(terms), string.Empty);
pluginQueryPair.Add(globalPlugin, query);
}
}

View File

@@ -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,
};
}

View File

@@ -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);
}
}
}

View File

@@ -6,11 +6,7 @@ using System.Collections.Generic;
namespace Wox.Plugin
{
public interface IFeatures
{
}
public interface IContextMenu : IFeatures
public interface IContextMenu
{
List<ContextMenuResult> LoadContextMenus(Result selectedResult);
}
@@ -18,14 +14,14 @@ namespace Wox.Plugin
/// <summary>
/// Represent plugins that support internationalization
/// </summary>
public interface IPluginI18n : IFeatures
public interface IPluginI18n
{
string GetTranslatedPluginTitle();
string GetTranslatedPluginDescription();
}
public interface IResultUpdated : IFeatures
public interface IResultUpdated
{
event ResultUpdatedEventHandler ResultsUpdated;
}

View File

@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace Wox.Plugin
{
public interface IDelayedExecutionPlugin : IFeatures
public interface IDelayedExecutionPlugin
{
List<Result> Query(Query query, bool delayedExecution);
}

View File

@@ -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"
}
]
}
]
}

View File

@@ -14,6 +14,13 @@ namespace Wox.Plugin
{
private string _pluginDirectory;
private List<string> _actionKeywords;
public PluginMetadata(List<string> 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<string> ActionKeywords { get; set; }
public List<string> GetActionKeywords()
{
return _actionKeywords;
}
public void SetActionKeywords(List<string> value)
{
_actionKeywords = value;
}
public string IcoPath { get; set; }

View File

@@ -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;
}
}

View File

@@ -0,0 +1,90 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Wox.Plugin.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 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() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[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;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Copying path {0} has failed, it will now be deleted for consistency.
/// </summary>
public static string filesfolder_copy_failed {
get {
return ResourceManager.GetString("filesfolder_copy_failed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Not able to delete folder {0}, please go to the location and manually delete it.
/// </summary>
public static string filesfolder_removefolder_failed {
get {
return ResourceManager.GetString("filesfolder_removefolder_failed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unable to verify folders and files between {0} and {1}.
/// </summary>
public static string filesfolder_verifybothfolderfilesequal_failed {
get {
return ResourceManager.GetString("filesfolder_verifybothfolderfilesequal_failed", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="filesfolder_copy_failed" xml:space="preserve">
<value>Copying path {0} has failed, it will now be deleted for consistency</value>
<comment>parameter: targetPath</comment>
</data>
<data name="filesfolder_removefolder_failed" xml:space="preserve">
<value>Not able to delete folder {0}, please go to the location and manually delete it</value>
<comment>parameter: path</comment>
</data>
<data name="filesfolder_verifybothfolderfilesequal_failed" xml:space="preserve">
<value>Unable to verify folders and files between {0} and {1}</value>
<comment>paramaters: fromPath, toPath</comment>
</data>
</root>

View File

@@ -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 <see cref="Query"/> class.
/// to allow unit tests for plug ins
/// </summary>
public Query(string rawQuery, string search, string[] terms, string actionKeyword = "")
public Query(string rawQuery, string search, ReadOnlyCollection<string> terms, string actionKeyword = "")
{
Search = search;
RawQuery = rawQuery;
@@ -41,9 +42,9 @@ namespace Wox.Plugin
public string Search { get; internal set; }
/// <summary>
/// Gets or sets the raw query splited into a string array.
/// Gets the raw query splited into a string array.
/// </summary>
public string[] Terms { get; set; }
public ReadOnlyCollection<string> Terms { get; private set; }
/// <summary>
/// Query can be splited into multiple terms by whitespace

View File

@@ -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<int> _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; }
/// <summary>
/// Gets or sets a list of indexes for the characters to be highlighted in Title
/// </summary>
public IList<int> TitleHighlightData { get; set; }
public Result(IList<int> titleHighlightData = null, IList<int> subTitleHighlightData = null)
{
_titleHighlightData = titleHighlightData;
SubTitleHighlightData = subTitleHighlightData;
}
/// <summary>
/// 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
/// </summary>
public IList<int> SubTitleHighlightData { get; set; }
public IList<int> GetTitleHighlightData()
{
return _titleHighlightData;
}
/// <summary>
/// Sets a list of indexes for the characters to be highlighted in Title
/// </summary>
public void SetTitleHighlightData(IList<int> value)
{
_titleHighlightData = value;
}
/// <summary>
/// Gets a list of indexes for the characters to be highlighted in SubTitle
/// </summary>
public IList<int> SubTitleHighlightData { get; private set; }
/// <summary>
/// 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);
}
/// <summary>

View File

@@ -9,7 +9,12 @@ namespace Wox.Plugin
{
public class ResultUpdatedEventArgs : EventArgs
{
public List<Result> Results { get; set; }
public List<Result> Results { get; private set; }
public ResultUpdatedEventArgs(List<Result> results = null)
{
Results = results;
}
public Query Query { get; set; }
}

View File

@@ -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
}
}

View File

@@ -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);
}
}

View File

@@ -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.
/// </summary>
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
/// <summary>
/// Opens search as a tab in the default browser chosen in Windows settings.
/// </summary>
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);
}
}
}

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -96,4 +96,17 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -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<string, PluginPair>
{
{ ">", new PluginPair { Metadata = new PluginMetadata { ActionKeywords = new List<string> { ">" } } } },
{ ">", new PluginPair { Metadata = new PluginMetadata(new List<string> { ">" } ) } },
};
string searchQuery = "> file.txt file2 file3";
@@ -43,7 +44,7 @@ namespace Wox.Test
// Arrange
var nonGlobalPlugins = new Dictionary<string, PluginPair>
{
{ ">", new PluginPair { Metadata = new PluginMetadata { ActionKeywords = new List<string> { ">" }, Disabled = true } } },
{ ">", new PluginPair { Metadata = new PluginMetadata(new List<string> { ">" }) { 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<string> { ">" } } };
var firstPlugin = new PluginPair { Metadata = new PluginMetadata(new List<string> { ">" } ) };
var secondPlugin = new PluginPair { Metadata = new PluginMetadata { ActionKeyword = ">" } };
var nonGlobalPluginWithActionKeywords = new Dictionary<string, PluginPair>
@@ -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<string>(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<string> { "a", "b" } } };
var plugin = new PluginPair { Metadata = new PluginMetadata(new List<string> { "a", "b" } ) };
var nonGlobalPlugins = new Dictionary<string, PluginPair>
{
{ "a", plugin },
@@ -129,7 +130,7 @@ namespace Wox.Test
public void QueryBuildShouldGenerateSameSearchQueryWithOrWithoutSpaceAfterActionKeyword()
{
// Arrange
var plugin = new PluginPair { Metadata = new PluginMetadata { ActionKeywords = new List<string> { "a" } } };
var plugin = new PluginPair { Metadata = new PluginMetadata(new List<string> { "a" } ) };
var nonGlobalPlugins = new Dictionary<string, PluginPair>
{
{ "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);
}
}
}