Merge branch 'master' into dotnet45

This commit is contained in:
bao-qian
2015-11-12 22:02:40 +00:00
60 changed files with 1717 additions and 671 deletions

View File

@@ -21,22 +21,17 @@ namespace Wox.Plugin.CMD
public List<Result> Query(Query query) public List<Result> Query(Query query)
{ {
List<Result> results = new List<Result>(); List<Result> results = new List<Result>();
List<Result> pushedResults = new List<Result>();
string cmd = query.Search; string cmd = query.Search;
if (string.IsNullOrEmpty(cmd)) if (string.IsNullOrEmpty(cmd))
{ {
return GetAllHistoryCmds(); return ResultsFromlHistory();
} }
else else
{ {
var queryCmd = GetCurrentCmd(cmd); var queryCmd = GetCurrentCmd(cmd);
context.API.PushResults(query, context.CurrentPluginMetadata, new List<Result>() { queryCmd }); results.Add(queryCmd);
pushedResults.Add(queryCmd);
var history = GetHistoryCmds(cmd, queryCmd); var history = GetHistoryCmds(cmd, queryCmd);
context.API.PushResults(query, context.CurrentPluginMetadata, history); results.AddRange(history);
pushedResults.AddRange(history);
try try
{ {
@@ -57,7 +52,11 @@ namespace Wox.Plugin.CMD
if (basedir != null) if (basedir != null)
{ {
List<string> autocomplete = Directory.GetFileSystemEntries(basedir).Select(o => dir + Path.GetFileName(o)).Where(o => o.StartsWith(cmd, StringComparison.OrdinalIgnoreCase) && !results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase)) && !pushedResults.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase))).ToList(); var autocomplete = Directory.GetFileSystemEntries(basedir).
Select(o => dir + Path.GetFileName(o)).
Where(o => o.StartsWith(cmd, StringComparison.OrdinalIgnoreCase) &&
!results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase)) &&
!results.Any(p => o.Equals(p.Title, StringComparison.OrdinalIgnoreCase))).ToList();
autocomplete.Sort(); autocomplete.Sort();
results.AddRange(autocomplete.ConvertAll(m => new Result() results.AddRange(autocomplete.ConvertAll(m => new Result()
{ {
@@ -94,7 +93,7 @@ namespace Wox.Plugin.CMD
var ret = new Result var ret = new Result
{ {
Title = m.Key, Title = m.Key,
SubTitle = string.Format(context.API.GetTranslation("wox_plugin_cmd_cmd_has_been_executed_times"), m.Value), SubTitle = string.Format(context.API.GetTranslation("wox_plugin_cmd_cmd_has_been_executed_times"), m.Value),
IcoPath = "Images/cmd.png", IcoPath = "Images/cmd.png",
Action = (c) => Action = (c) =>
{ {
@@ -125,13 +124,13 @@ namespace Wox.Plugin.CMD
return result; return result;
} }
private List<Result> GetAllHistoryCmds() private List<Result> ResultsFromlHistory()
{ {
IEnumerable<Result> history = CMDStorage.Instance.CMDHistory.OrderByDescending(o => o.Value) IEnumerable<Result> history = CMDStorage.Instance.CMDHistory.OrderByDescending(o => o.Value)
.Select(m => new Result .Select(m => new Result
{ {
Title = m.Key, Title = m.Key,
SubTitle = string.Format(context.API.GetTranslation("wox_plugin_cmd_cmd_has_been_executed_times"), m.Value), SubTitle = string.Format(context.API.GetTranslation("wox_plugin_cmd_cmd_has_been_executed_times"), m.Value),
IcoPath = "Images/cmd.png", IcoPath = "Images/cmd.png",
Action = (c) => Action = (c) =>
{ {
@@ -177,7 +176,8 @@ namespace Wox.Plugin.CMD
private void OnWinRPressed() private void OnWinRPressed()
{ {
context.API.ShowApp(); context.API.ShowApp();
context.API.ChangeQuery(">"); // todo don't hardcode action keywords.
context.API.ChangeQuery($">{Plugin.Query.TermSeperater}");
} }
public Control CreateSettingPanel() public Control CreateSettingPanel()

View File

@@ -227,8 +227,6 @@ namespace Wox.Plugin.Everything.Everything
yield break; yield break;
} }
Everything_SortResultsByPath();
const int bufferSize = 4096; const int bufferSize = 4096;
StringBuilder buffer = new StringBuilder(bufferSize); StringBuilder buffer = new StringBuilder(bufferSize);
for (int idx = 0; idx < Everything_GetNumResults(); ++idx) for (int idx = 0; idx < Everything_GetNumResults(); ++idx)

View File

@@ -47,12 +47,6 @@ namespace Wox.Plugin.Everything
try try
{ {
var searchList = api.Search(keyword, maxCount: ContextMenuStorage.Instance.MaxSearchCount).ToList(); var searchList = api.Search(keyword, maxCount: ContextMenuStorage.Instance.MaxSearchCount).ToList();
var fuzzyMather = FuzzyMatcher.Create(keyword);
searchList.Sort(
(x, y) =>
fuzzyMather.Evaluate(Path.GetFileName(y.FullPath)).Score -
fuzzyMather.Evaluate(Path.GetFileName(x.FullPath)).Score);
foreach (var s in searchList) foreach (var s in searchList)
{ {
var path = s.FullPath; var path = s.FullPath;

View File

@@ -2,7 +2,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using Wox.Infrastructure.Logger;
namespace Wox.Plugin.Program namespace Wox.Plugin.Program
{ {
internal class FileChangeWatcher internal class FileChangeWatcher
@@ -15,7 +15,7 @@ namespace Wox.Plugin.Program
if (watchedPath.Contains(path)) return; if (watchedPath.Contains(path)) return;
if (!Directory.Exists(path)) if (!Directory.Exists(path))
{ {
Debug.WriteLine(string.Format("FileChangeWatcher: {0} doesn't exist", path)); Log.Warn($"FileChangeWatcher: {path} doesn't exist");
return; return;
} }

View File

@@ -38,7 +38,7 @@ namespace Wox.Plugin.Program.ProgramSources
using (var key = root.OpenSubKey(item)) using (var key = root.OpenSubKey(item))
{ {
string path = key.GetValue("") as string; string path = key.GetValue("") as string;
if (path == null) continue; if (string.IsNullOrEmpty(path)) continue;
// fix path like this ""\"C:\\folder\\executable.exe\""" // fix path like this ""\"C:\\folder\\executable.exe\"""
const int begin = 0; const int begin = 0;
@@ -57,7 +57,7 @@ namespace Wox.Plugin.Program.ProgramSources
} }
catch (Exception e) catch (Exception e)
{ {
Log.Error(e.StackTrace); Log.Error(e);
} }
} }
} }

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Wox.Infrastructure.Exception;
using Wox.Infrastructure.Logger; using Wox.Infrastructure.Logger;
namespace Wox.Plugin.Program.ProgramSources namespace Wox.Plugin.Program.ProgramSources
@@ -70,7 +71,8 @@ namespace Wox.Plugin.Program.ProgramSources
} }
catch (Exception e) catch (Exception e)
{ {
Log.Warn(string.Format("GetAppFromDirectory failed: {0} - {1}", path, e.Message)); var woxPluginException = new WoxPluginException("Program", $"GetAppFromDirectory failed: {path}", e);
Log.Error(woxPluginException);
} }
} }

View File

@@ -8,6 +8,7 @@ using System.Windows;
using IWshRuntimeLibrary; using IWshRuntimeLibrary;
using Wox.Infrastructure; using Wox.Infrastructure;
using Wox.Plugin.Program.ProgramSources; using Wox.Plugin.Program.ProgramSources;
using Wox.Infrastructure.Logger;
using Stopwatch = Wox.Infrastructure.Stopwatch; using Stopwatch = Wox.Infrastructure.Stopwatch;
namespace Wox.Plugin.Program namespace Wox.Plugin.Program
@@ -17,7 +18,7 @@ namespace Wox.Plugin.Program
private static object lockObject = new object(); private static object lockObject = new object();
private static List<Program> programs = new List<Program>(); private static List<Program> programs = new List<Program>();
private static List<IProgramSource> sources = new List<IProgramSource>(); private static List<IProgramSource> sources = new List<IProgramSource>();
private static Dictionary<string, Type> SourceTypes = new Dictionary<string, Type>() { private static Dictionary<string, Type> SourceTypes = new Dictionary<string, Type>() {
{"FileSystemProgramSource", typeof(FileSystemProgramSource)}, {"FileSystemProgramSource", typeof(FileSystemProgramSource)},
{"CommonStartMenuProgramSource", typeof(CommonStartMenuProgramSource)}, {"CommonStartMenuProgramSource", typeof(CommonStartMenuProgramSource)},
{"UserStartMenuProgramSource", typeof(UserStartMenuProgramSource)}, {"UserStartMenuProgramSource", typeof(UserStartMenuProgramSource)},
@@ -27,26 +28,26 @@ namespace Wox.Plugin.Program
public List<Result> Query(Query query) public List<Result> Query(Query query)
{ {
var fuzzyMather = FuzzyMatcher.Create(query.Search);
List<Program> returnList = programs.Where(o => MatchProgram(o, fuzzyMather)).ToList();
returnList.ForEach(ScoreFilter);
returnList = returnList.OrderByDescending(o => o.Score).ToList();
return returnList.Select(c => new Result() var fuzzyMather = FuzzyMatcher.Create(query.Search);
{ var results = programs.Where(o => MatchProgram(o, fuzzyMather)).
Title = c.Title, Select(ScoreFilter).
SubTitle = c.ExecutePath, OrderByDescending(o => o.Score)
IcoPath = c.IcoPath, .Select(c => new Result()
Score = c.Score, {
ContextData = c, Title = c.Title,
Action = (e) => SubTitle = c.ExecutePath,
{ IcoPath = c.IcoPath,
context.API.HideApp(); Score = c.Score,
context.API.ShellRun(c.ExecutePath); ContextData = c,
return true; Action = (e) =>
} {
}).ToList(); context.API.HideApp();
context.API.ShellRun(c.ExecutePath);
return true;
}
}).ToList();
return results;
} }
static string ResolveShortcut(string filePath) static string ResolveShortcut(string filePath)
@@ -59,12 +60,10 @@ namespace Wox.Plugin.Program
private bool MatchProgram(Program program, FuzzyMatcher matcher) private bool MatchProgram(Program program, FuzzyMatcher matcher)
{ {
if ((program.Score = matcher.Evaluate(program.Title).Score) > 0) return true; var scores = new List<string> { program.Title, program.PinyinTitle, program.AbbrTitle, program.ExecuteName };
if ((program.Score = matcher.Evaluate(program.PinyinTitle).Score) > 0) return true; program.Score = scores.Select(s => matcher.Evaluate(s ?? string.Empty).Score).Max();
if (program.AbbrTitle != null && (program.Score = matcher.Evaluate(program.AbbrTitle).Score) > 0) return true; if (program.Score > 0) return true;
if (program.ExecuteName != null && (program.Score = matcher.Evaluate(program.ExecuteName).Score) > 0) return true; else return false;
return false;
} }
public void Init(PluginInitContext context) public void Init(PluginInitContext context)
@@ -75,7 +74,7 @@ namespace Wox.Plugin.Program
{ {
programs = ProgramCacheStorage.Instance.Programs; programs = ProgramCacheStorage.Instance.Programs;
}); });
Debug.WriteLine($"Preload {programs.Count} programs from cache"); Log.Info($"Preload {programs.Count} programs from cache");
Stopwatch.Debug("Program Index", IndexPrograms); Stopwatch.Debug("Program Index", IndexPrograms);
} }
@@ -98,7 +97,7 @@ namespace Wox.Plugin.Program
} }
sources.Clear(); sources.Clear();
foreach(var source in programSources.Where(o => o.Enabled)) foreach (var source in programSources.Where(o => o.Enabled))
{ {
Type sourceClass; Type sourceClass;
if (SourceTypes.TryGetValue(source.Type, out sourceClass)) if (SourceTypes.TryGetValue(source.Type, out sourceClass))
@@ -160,7 +159,7 @@ namespace Wox.Plugin.Program
return list; return list;
} }
private void ScoreFilter(Program p) private Program ScoreFilter(Program p)
{ {
p.Score += p.Source.BonusPoints; p.Score += p.Source.BonusPoints;
@@ -172,6 +171,7 @@ namespace Wox.Plugin.Program
if (p.Title.Contains("卸载") || p.Title.ToLower().Contains("uninstall")) if (p.Title.Contains("卸载") || p.Title.ToLower().Contains("uninstall"))
p.Score -= 20; p.Score -= 20;
return p;
} }
#region ISettingProvider Members #region ISettingProvider Members

View File

@@ -39,8 +39,11 @@ namespace Wox.Plugin.Sys
List<Result> results = new List<Result>(); List<Result> results = new List<Result>();
foreach (Result availableResult in availableResults) foreach (Result availableResult in availableResults)
{ {
if (StringMatcher.IsMatch(availableResult.Title, query.Search) || StringMatcher.IsMatch(availableResult.SubTitle, query.Search)) int titleScore = StringMatcher.Match(availableResult.Title, query.Search);
int subTitleScore = StringMatcher.Match(availableResult.SubTitle, query.Search);
if (titleScore > 0 || subTitleScore > 0)
{ {
availableResult.Score = titleScore > 0 ? titleScore : subTitleScore;
results.Add(availableResult); results.Add(availableResult);
} }
} }
@@ -55,92 +58,85 @@ namespace Wox.Plugin.Sys
private void LoadCommands() private void LoadCommands()
{ {
availableResults.AddRange(new Result[] { availableResults.AddRange(new Result[] {
new Result new Result
{ {
Title = "Shutdown", Title = "Shutdown",
SubTitle = context.API.GetTranslation("wox_plugin_sys_shutdown_computer"), SubTitle = context.API.GetTranslation("wox_plugin_sys_shutdown_computer"),
Score = 100, IcoPath = "Images\\exit.png",
IcoPath = "Images\\exit.png", Action = (c) =>
Action = (c) => {
{ if (MessageBox.Show("Are you sure you want to shut the computer down?","Shutdown Computer?",MessageBoxButtons.YesNo,MessageBoxIcon.Warning) == DialogResult.Yes) {
if (MessageBox.Show("Are you sure you want to shut the computer down?","Shutdown Computer?",MessageBoxButtons.YesNo,MessageBoxIcon.Warning) == DialogResult.Yes) { Process.Start("shutdown", "/s /t 0");
Process.Start("shutdown", "/s /t 0"); }
} return true;
return true; }
} },
}, new Result
new Result {
{ Title = "Log off",
Title = "Log off",
SubTitle = context.API.GetTranslation("wox_plugin_sys_log_off"), SubTitle = context.API.GetTranslation("wox_plugin_sys_log_off"),
Score = 100, IcoPath = "Images\\logoff.png",
IcoPath = "Images\\logoff.png", Action = (c) => ExitWindowsEx(EWX_LOGOFF, 0)
Action = (c) => ExitWindowsEx(EWX_LOGOFF, 0) },
}, new Result
new Result {
{ Title = "Lock",
Title = "Lock",
SubTitle = context.API.GetTranslation("wox_plugin_sys_lock"), SubTitle = context.API.GetTranslation("wox_plugin_sys_lock"),
Score = 100, IcoPath = "Images\\lock.png",
IcoPath = "Images\\lock.png", Action = (c) =>
Action = (c) => {
{ LockWorkStation();
LockWorkStation(); return true;
return true; }
} },
},
new Result new Result
{ {
Title = "Sleep", Title = "Sleep",
SubTitle = context.API.GetTranslation("wox_plugin_sys_sleep"), SubTitle = context.API.GetTranslation("wox_plugin_sys_sleep"),
Score = 100,
IcoPath = "Images\\sleep.png", IcoPath = "Images\\sleep.png",
Action = (c) => Application.SetSuspendState(PowerState.Suspend, false, false) Action = (c) => Application.SetSuspendState(PowerState.Suspend, false, false)
}, },
new Result new Result
{ {
Title = "Exit", Title = "Exit",
SubTitle = context.API.GetTranslation("wox_plugin_sys_exit"), SubTitle = context.API.GetTranslation("wox_plugin_sys_exit"),
Score = 110, IcoPath = "Images\\app.png",
IcoPath = "Images\\app.png", Action = (c) =>
Action = (c) => {
{ context.API.CloseApp();
context.API.CloseApp(); return true;
return true; }
} },
}, new Result
new Result {
{ Title = "Restart Wox",
Title = "Restart Wox",
SubTitle = context.API.GetTranslation("wox_plugin_sys_restart"), SubTitle = context.API.GetTranslation("wox_plugin_sys_restart"),
Score = 110, IcoPath = "Images\\restart.png",
IcoPath = "Images\\restart.png", Action = (c) =>
Action = (c) => {
{ ProcessStartInfo Info = new ProcessStartInfo();
ProcessStartInfo Info = new ProcessStartInfo(); Info.Arguments = "/C ping 127.0.0.1 -n 1 && \"" + Application.ExecutablePath + "\"";
Info.Arguments = "/C ping 127.0.0.1 -n 1 && \"" + Application.ExecutablePath + "\""; Info.WindowStyle = ProcessWindowStyle.Hidden;
Info.WindowStyle = ProcessWindowStyle.Hidden; Info.CreateNoWindow = true;
Info.CreateNoWindow = true; Info.FileName = "cmd.exe";
Info.FileName = "cmd.exe"; Process.Start(Info);
Process.Start(Info); context.API.CloseApp();
context.API.CloseApp(); return true;
return true; }
} },
}, new Result
new Result {
{ Title = "Settings",
Title = "Settings",
SubTitle = context.API.GetTranslation("wox_plugin_sys_setting"), SubTitle = context.API.GetTranslation("wox_plugin_sys_setting"),
Score = 100, IcoPath = "Images\\app.png",
IcoPath = "Images\\app.png", Action = (c) =>
Action = (c) => {
{ context.API.OpenSettingDialog();
context.API.OpenSettingDialog(); return true;
return true; }
} }
} });
});
} }
public string GetLanguagesFolder() public string GetLanguagesFolder()

View File

@@ -1,40 +0,0 @@
using System;
namespace Wox.Plugin.WebSearch
{
public static class EasyTimer
{
public static IDisposable SetInterval(Action method, int delayInMilliseconds)
{
System.Timers.Timer timer = new System.Timers.Timer(delayInMilliseconds);
timer.Elapsed += (source, e) =>
{
method();
};
timer.Enabled = true;
timer.Start();
// Returns a stop handle which can be used for stopping
// the timer, if required
return timer as IDisposable;
}
public static IDisposable SetTimeout(Action method, int delayInMilliseconds)
{
System.Timers.Timer timer = new System.Timers.Timer(delayInMilliseconds);
timer.Elapsed += (source, e) =>
{
method();
};
timer.AutoReset = false;
timer.Enabled = true;
timer.Start();
// Returns a stop handle which can be used for stopping
// the timer, if required
return timer as IDisposable;
}
}
}

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,996 @@
using System;
#pragma warning disable 1591
// ReSharper disable UnusedMember.Global
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global
// ReSharper disable IntroduceOptionalParameters.Global
// ReSharper disable MemberCanBeProtected.Global
// ReSharper disable InconsistentNaming
namespace Wox.Plugin.WebSearch.Annotations
{
/// <summary>
/// Indicates that the value of the marked element could be <c>null</c> sometimes,
/// so the check for <c>null</c> is necessary before its usage.
/// </summary>
/// <example><code>
/// [CanBeNull] object Test() => null;
///
/// void UseTest() {
/// var p = Test();
/// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'
/// }
/// </code></example>
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |
AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event)]
public sealed class CanBeNullAttribute : Attribute { }
/// <summary>
/// Indicates that the value of the marked element could never be <c>null</c>.
/// </summary>
/// <example><code>
/// [NotNull] object Foo() {
/// return null; // Warning: Possible 'null' assignment
/// }
/// </code></example>
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |
AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event)]
public sealed class NotNullAttribute : Attribute { }
/// <summary>
/// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task
/// and Lazy classes to indicate that the value of a collection item, of the Task.Result property
/// or of the Lazy.Value property can never be null.
/// </summary>
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |
AttributeTargets.Delegate | AttributeTargets.Field)]
public sealed class ItemNotNullAttribute : Attribute { }
/// <summary>
/// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task
/// and Lazy classes to indicate that the value of a collection item, of the Task.Result property
/// or of the Lazy.Value property can be null.
/// </summary>
[AttributeUsage(
AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property |
AttributeTargets.Delegate | AttributeTargets.Field)]
public sealed class ItemCanBeNullAttribute : Attribute { }
/// <summary>
/// Indicates that the marked method builds string by format pattern and (optional) arguments.
/// Parameter, which contains format string, should be given in constructor. The format string
/// should be in <see cref="string.Format(IFormatProvider,string,object[])"/>-like form.
/// </summary>
/// <example><code>
/// [StringFormatMethod("message")]
/// void ShowError(string message, params object[] args) { /* do something */ }
///
/// void Foo() {
/// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string
/// }
/// </code></example>
[AttributeUsage(
AttributeTargets.Constructor | AttributeTargets.Method |
AttributeTargets.Property | AttributeTargets.Delegate)]
public sealed class StringFormatMethodAttribute : Attribute
{
/// <param name="formatParameterName">
/// Specifies which parameter of an annotated method should be treated as format-string
/// </param>
public StringFormatMethodAttribute(string formatParameterName)
{
FormatParameterName = formatParameterName;
}
public string FormatParameterName { get; private set; }
}
/// <summary>
/// For a parameter that is expected to be one of the limited set of values.
/// Specify fields of which type should be used as values for this parameter.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)]
public sealed class ValueProviderAttribute : Attribute
{
public ValueProviderAttribute(string name)
{
Name = name;
}
[NotNull] public string Name { get; private set; }
}
/// <summary>
/// Indicates that the function argument should be string literal and match one
/// of the parameters of the caller function. For example, ReSharper annotates
/// the parameter of <see cref="System.ArgumentNullException"/>.
/// </summary>
/// <example><code>
/// void Foo(string param) {
/// if (param == null)
/// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol
/// }
/// </code></example>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class InvokerParameterNameAttribute : Attribute { }
/// <summary>
/// Indicates that the method is contained in a type that implements
/// <c>System.ComponentModel.INotifyPropertyChanged</c> interface and this method
/// is used to notify that some property value changed.
/// </summary>
/// <remarks>
/// The method should be non-static and conform to one of the supported signatures:
/// <list>
/// <item><c>NotifyChanged(string)</c></item>
/// <item><c>NotifyChanged(params string[])</c></item>
/// <item><c>NotifyChanged{T}(Expression{Func{T}})</c></item>
/// <item><c>NotifyChanged{T,U}(Expression{Func{T,U}})</c></item>
/// <item><c>SetProperty{T}(ref T, T, string)</c></item>
/// </list>
/// </remarks>
/// <example><code>
/// public class Foo : INotifyPropertyChanged {
/// public event PropertyChangedEventHandler PropertyChanged;
///
/// [NotifyPropertyChangedInvocator]
/// protected virtual void NotifyChanged(string propertyName) { ... }
///
/// string _name;
///
/// public string Name {
/// get { return _name; }
/// set { _name = value; NotifyChanged("LastName"); /* Warning */ }
/// }
/// }
/// </code>
/// Examples of generated notifications:
/// <list>
/// <item><c>NotifyChanged("Property")</c></item>
/// <item><c>NotifyChanged(() =&gt; Property)</c></item>
/// <item><c>NotifyChanged((VM x) =&gt; x.Property)</c></item>
/// <item><c>SetProperty(ref myField, value, "Property")</c></item>
/// </list>
/// </example>
[AttributeUsage(AttributeTargets.Method)]
public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute
{
public NotifyPropertyChangedInvocatorAttribute() { }
public NotifyPropertyChangedInvocatorAttribute(string parameterName)
{
ParameterName = parameterName;
}
public string ParameterName { get; private set; }
}
/// <summary>
/// Describes dependency between method input and output.
/// </summary>
/// <syntax>
/// <p>Function Definition Table syntax:</p>
/// <list>
/// <item>FDT ::= FDTRow [;FDTRow]*</item>
/// <item>FDTRow ::= Input =&gt; Output | Output &lt;= Input</item>
/// <item>Input ::= ParameterName: Value [, Input]*</item>
/// <item>Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value}</item>
/// <item>Value ::= true | false | null | notnull | canbenull</item>
/// </list>
/// If method has single input parameter, it's name could be omitted.<br/>
/// Using <c>halt</c> (or <c>void</c>/<c>nothing</c>, which is the same)
/// for method output means that the methos doesn't return normally.<br/>
/// <c>canbenull</c> annotation is only applicable for output parameters.<br/>
/// You can use multiple <c>[ContractAnnotation]</c> for each FDT row,
/// or use single attribute with rows separated by semicolon.<br/>
/// </syntax>
/// <examples><list>
/// <item><code>
/// [ContractAnnotation("=> halt")]
/// public void TerminationMethod()
/// </code></item>
/// <item><code>
/// [ContractAnnotation("halt &lt;= condition: false")]
/// public void Assert(bool condition, string text) // regular assertion method
/// </code></item>
/// <item><code>
/// [ContractAnnotation("s:null => true")]
/// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty()
/// </code></item>
/// <item><code>
/// // A method that returns null if the parameter is null,
/// // and not null if the parameter is not null
/// [ContractAnnotation("null => null; notnull => notnull")]
/// public object Transform(object data)
/// </code></item>
/// <item><code>
/// [ContractAnnotation("s:null=>false; =>true,result:notnull; =>false, result:null")]
/// public bool TryParse(string s, out Person result)
/// </code></item>
/// </list></examples>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class ContractAnnotationAttribute : Attribute
{
public ContractAnnotationAttribute([NotNull] string contract)
: this(contract, false) { }
public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates)
{
Contract = contract;
ForceFullStates = forceFullStates;
}
public string Contract { get; private set; }
public bool ForceFullStates { get; private set; }
}
/// <summary>
/// Indicates that marked element should be localized or not.
/// </summary>
/// <example><code>
/// [LocalizationRequiredAttribute(true)]
/// class Foo {
/// string str = "my string"; // Warning: Localizable string
/// }
/// </code></example>
[AttributeUsage(AttributeTargets.All)]
public sealed class LocalizationRequiredAttribute : Attribute
{
public LocalizationRequiredAttribute() : this(true) { }
public LocalizationRequiredAttribute(bool required)
{
Required = required;
}
public bool Required { get; private set; }
}
/// <summary>
/// Indicates that the value of the marked type (or its derivatives)
/// cannot be compared using '==' or '!=' operators and <c>Equals()</c>
/// should be used instead. However, using '==' or '!=' for comparison
/// with <c>null</c> is always permitted.
/// </summary>
/// <example><code>
/// [CannotApplyEqualityOperator]
/// class NoEquality { }
///
/// class UsesNoEquality {
/// void Test() {
/// var ca1 = new NoEquality();
/// var ca2 = new NoEquality();
/// if (ca1 != null) { // OK
/// bool condition = ca1 == ca2; // Warning
/// }
/// }
/// }
/// </code></example>
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class CannotApplyEqualityOperatorAttribute : Attribute { }
/// <summary>
/// When applied to a target attribute, specifies a requirement for any type marked
/// with the target attribute to implement or inherit specific type or types.
/// </summary>
/// <example><code>
/// [BaseTypeRequired(typeof(IComponent)] // Specify requirement
/// class ComponentAttribute : Attribute { }
///
/// [Component] // ComponentAttribute requires implementing IComponent interface
/// class MyComponent : IComponent { }
/// </code></example>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
[BaseTypeRequired(typeof(Attribute))]
public sealed class BaseTypeRequiredAttribute : Attribute
{
public BaseTypeRequiredAttribute([NotNull] Type baseType)
{
BaseType = baseType;
}
[NotNull] public Type BaseType { get; private set; }
}
/// <summary>
/// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library),
/// so this symbol will not be marked as unused (as well as by other usage inspections).
/// </summary>
[AttributeUsage(AttributeTargets.All)]
public sealed class UsedImplicitlyAttribute : Attribute
{
public UsedImplicitlyAttribute()
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }
public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags)
: this(useKindFlags, ImplicitUseTargetFlags.Default) { }
public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags)
: this(ImplicitUseKindFlags.Default, targetFlags) { }
public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
{
UseKindFlags = useKindFlags;
TargetFlags = targetFlags;
}
public ImplicitUseKindFlags UseKindFlags { get; private set; }
public ImplicitUseTargetFlags TargetFlags { get; private set; }
}
/// <summary>
/// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes
/// as unused (as well as by other usage inspections)
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)]
public sealed class MeansImplicitUseAttribute : Attribute
{
public MeansImplicitUseAttribute()
: this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { }
public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags)
: this(useKindFlags, ImplicitUseTargetFlags.Default) { }
public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags)
: this(ImplicitUseKindFlags.Default, targetFlags) { }
public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags)
{
UseKindFlags = useKindFlags;
TargetFlags = targetFlags;
}
[UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; }
[UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; }
}
[Flags]
public enum ImplicitUseKindFlags
{
Default = Access | Assign | InstantiatedWithFixedConstructorSignature,
/// <summary>Only entity marked with attribute considered used.</summary>
Access = 1,
/// <summary>Indicates implicit assignment to a member.</summary>
Assign = 2,
/// <summary>
/// Indicates implicit instantiation of a type with fixed constructor signature.
/// That means any unused constructor parameters won't be reported as such.
/// </summary>
InstantiatedWithFixedConstructorSignature = 4,
/// <summary>Indicates implicit instantiation of a type.</summary>
InstantiatedNoFixedConstructorSignature = 8,
}
/// <summary>
/// Specify what is considered used implicitly when marked
/// with <see cref="MeansImplicitUseAttribute"/> or <see cref="UsedImplicitlyAttribute"/>.
/// </summary>
[Flags]
public enum ImplicitUseTargetFlags
{
Default = Itself,
Itself = 1,
/// <summary>Members of entity marked with attribute are considered used.</summary>
Members = 2,
/// <summary>Entity marked with attribute and all its members considered used.</summary>
WithMembers = Itself | Members
}
/// <summary>
/// This attribute is intended to mark publicly available API
/// which should not be removed and so is treated as used.
/// </summary>
[MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)]
public sealed class PublicAPIAttribute : Attribute
{
public PublicAPIAttribute() { }
public PublicAPIAttribute([NotNull] string comment)
{
Comment = comment;
}
public string Comment { get; private set; }
}
/// <summary>
/// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack.
/// If the parameter is a delegate, indicates that delegate is executed while the method is executed.
/// If the parameter is an enumerable, indicates that it is enumerated while the method is executed.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class InstantHandleAttribute : Attribute { }
/// <summary>
/// Indicates that a method does not make any observable state changes.
/// The same as <c>System.Diagnostics.Contracts.PureAttribute</c>.
/// </summary>
/// <example><code>
/// [Pure] int Multiply(int x, int y) => x * y;
///
/// void M() {
/// Multiply(123, 42); // Waring: Return value of pure method is not used
/// }
/// </code></example>
[AttributeUsage(AttributeTargets.Method)]
public sealed class PureAttribute : Attribute { }
/// <summary>
/// Indicates that the return value of method invocation must be used.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class MustUseReturnValueAttribute : Attribute
{
public MustUseReturnValueAttribute() { }
public MustUseReturnValueAttribute([NotNull] string justification)
{
Justification = justification;
}
public string Justification { get; private set; }
}
/// <summary>
/// Indicates the type member or parameter of some type, that should be used instead of all other ways
/// to get the value that type. This annotation is useful when you have some "context" value evaluated
/// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one.
/// </summary>
/// <example><code>
/// class Foo {
/// [ProvidesContext] IBarService _barService = ...;
///
/// void ProcessNode(INode node) {
/// DoSomething(node, node.GetGlobalServices().Bar);
/// // ^ Warning: use value of '_barService' field
/// }
/// }
/// </code></example>
[AttributeUsage(
AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter |
AttributeTargets.Method)]
public sealed class ProvidesContextAttribute : Attribute { }
/// <summary>
/// Indicates that a parameter is a path to a file or a folder within a web project.
/// Path can be relative or absolute, starting from web root (~).
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class PathReferenceAttribute : Attribute
{
public PathReferenceAttribute() { }
public PathReferenceAttribute([PathReference] string basePath)
{
BasePath = basePath;
}
public string BasePath { get; private set; }
}
/// <summary>
/// An extension method marked with this attribute is processed by ReSharper code completion
/// as a 'Source Template'. When extension method is completed over some expression, it's source code
/// is automatically expanded like a template at call site.
/// </summary>
/// <remarks>
/// Template method body can contain valid source code and/or special comments starting with '$'.
/// Text inside these comments is added as source code when the template is applied. Template parameters
/// can be used either as additional method parameters or as identifiers wrapped in two '$' signs.
/// Use the <see cref="MacroAttribute"/> attribute to specify macros for parameters.
/// </remarks>
/// <example>
/// In this example, the 'forEach' method is a source template available over all values
/// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block:
/// <code>
/// [SourceTemplate]
/// public static void forEach&lt;T&gt;(this IEnumerable&lt;T&gt; xs) {
/// foreach (var x in xs) {
/// //$ $END$
/// }
/// }
/// </code>
/// </example>
[AttributeUsage(AttributeTargets.Method)]
public sealed class SourceTemplateAttribute : Attribute { }
/// <summary>
/// Allows specifying a macro for a parameter of a <see cref="SourceTemplateAttribute">source template</see>.
/// </summary>
/// <remarks>
/// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression
/// is defined in the <see cref="MacroAttribute.Expression"/> property. When applied on a method, the target
/// template parameter is defined in the <see cref="MacroAttribute.Target"/> property. To apply the macro silently
/// for the parameter, set the <see cref="MacroAttribute.Editable"/> property value = -1.
/// </remarks>
/// <example>
/// Applying the attribute on a source template method:
/// <code>
/// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")]
/// public static void forEach&lt;T&gt;(this IEnumerable&lt;T&gt; collection) {
/// foreach (var item in collection) {
/// //$ $END$
/// }
/// }
/// </code>
/// Applying the attribute on a template method parameter:
/// <code>
/// [SourceTemplate]
/// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) {
/// /*$ var $x$Id = "$newguid$" + x.ToString();
/// x.DoSomething($x$Id); */
/// }
/// </code>
/// </example>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)]
public sealed class MacroAttribute : Attribute
{
/// <summary>
/// Allows specifying a macro that will be executed for a <see cref="SourceTemplateAttribute">source template</see>
/// parameter when the template is expanded.
/// </summary>
public string Expression { get; set; }
/// <summary>
/// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed.
/// </summary>
/// <remarks>
/// If the target parameter is used several times in the template, only one occurrence becomes editable;
/// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence,
/// use values >= 0. To make the parameter non-editable when the template is expanded, use -1.
/// </remarks>>
public int Editable { get; set; }
/// <summary>
/// Identifies the target parameter of a <see cref="SourceTemplateAttribute">source template</see> if the
/// <see cref="MacroAttribute"/> is applied on a template method.
/// </summary>
public string Target { get; set; }
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute
{
public AspMvcAreaMasterLocationFormatAttribute(string format)
{
Format = format;
}
public string Format { get; private set; }
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute
{
public AspMvcAreaPartialViewLocationFormatAttribute(string format)
{
Format = format;
}
public string Format { get; private set; }
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute
{
public AspMvcAreaViewLocationFormatAttribute(string format)
{
Format = format;
}
public string Format { get; private set; }
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class AspMvcMasterLocationFormatAttribute : Attribute
{
public AspMvcMasterLocationFormatAttribute(string format)
{
Format = format;
}
public string Format { get; private set; }
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute
{
public AspMvcPartialViewLocationFormatAttribute(string format)
{
Format = format;
}
public string Format { get; private set; }
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class AspMvcViewLocationFormatAttribute : Attribute
{
public AspMvcViewLocationFormatAttribute(string format)
{
Format = format;
}
public string Format { get; private set; }
}
/// <summary>
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter
/// is an MVC action. If applied to a method, the MVC action name is calculated
/// implicitly from the context. Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
public sealed class AspMvcActionAttribute : Attribute
{
public AspMvcActionAttribute() { }
public AspMvcActionAttribute(string anonymousProperty)
{
AnonymousProperty = anonymousProperty;
}
public string AnonymousProperty { get; private set; }
}
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC area.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class AspMvcAreaAttribute : Attribute
{
public AspMvcAreaAttribute() { }
public AspMvcAreaAttribute(string anonymousProperty)
{
AnonymousProperty = anonymousProperty;
}
public string AnonymousProperty { get; private set; }
}
/// <summary>
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is
/// an MVC controller. If applied to a method, the MVC controller name is calculated
/// implicitly from the context. Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
public sealed class AspMvcControllerAttribute : Attribute
{
public AspMvcControllerAttribute() { }
public AspMvcControllerAttribute(string anonymousProperty)
{
AnonymousProperty = anonymousProperty;
}
public string AnonymousProperty { get; private set; }
}
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute
/// for custom wrappers similar to <c>System.Web.Mvc.Controller.View(String, String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class AspMvcMasterAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute
/// for custom wrappers similar to <c>System.Web.Mvc.Controller.View(String, Object)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class AspMvcModelTypeAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC
/// partial view. If applied to a method, the MVC partial view name is calculated implicitly
/// from the context. Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
public sealed class AspMvcPartialViewAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class AspMvcSuppressViewErrorAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class AspMvcDisplayTemplateAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class AspMvcEditorTemplateAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. Indicates that a parameter is an MVC template.
/// Use this attribute for custom wrappers similar to
/// <c>System.ComponentModel.DataAnnotations.UIHintAttribute(System.String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class AspMvcTemplateAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter
/// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly
/// from the context. Use this attribute for custom wrappers similar to
/// <c>System.Web.Mvc.Controller.View(Object)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
public sealed class AspMvcViewAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter
/// is an MVC view component name.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class AspMvcViewComponentAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter
/// is an MVC view component view. If applied to a method, the MVC view component view name is default.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
public sealed class AspMvcViewComponentViewAttribute : Attribute { }
/// <summary>
/// ASP.NET MVC attribute. When applied to a parameter of an attribute,
/// indicates that this parameter is an MVC action name.
/// </summary>
/// <example><code>
/// [ActionName("Foo")]
/// public ActionResult Login(string returnUrl) {
/// ViewBag.ReturnUrl = Url.Action("Foo"); // OK
/// return RedirectToAction("Bar"); // Error: Cannot resolve action
/// }
/// </code></example>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)]
public sealed class AspMvcActionSelectorAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)]
public sealed class HtmlElementAttributesAttribute : Attribute
{
public HtmlElementAttributesAttribute() { }
public HtmlElementAttributesAttribute(string name)
{
Name = name;
}
public string Name { get; private set; }
}
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)]
public sealed class HtmlAttributeValueAttribute : Attribute
{
public HtmlAttributeValueAttribute([NotNull] string name)
{
Name = name;
}
[NotNull] public string Name { get; private set; }
}
/// <summary>
/// Razor attribute. Indicates that a parameter or a method is a Razor section.
/// Use this attribute for custom wrappers similar to
/// <c>System.Web.WebPages.WebPageBase.RenderSection(String)</c>.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)]
public sealed class RazorSectionAttribute : Attribute { }
/// <summary>
/// Indicates how method, constructor invocation or property access
/// over collection type affects content of the collection.
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)]
public sealed class CollectionAccessAttribute : Attribute
{
public CollectionAccessAttribute(CollectionAccessType collectionAccessType)
{
CollectionAccessType = collectionAccessType;
}
public CollectionAccessType CollectionAccessType { get; private set; }
}
[Flags]
public enum CollectionAccessType
{
/// <summary>Method does not use or modify content of the collection.</summary>
None = 0,
/// <summary>Method only reads content of the collection but does not modify it.</summary>
Read = 1,
/// <summary>Method can change content of the collection but does not add new elements.</summary>
ModifyExistingContent = 2,
/// <summary>Method can add new elements to the collection.</summary>
UpdatedContent = ModifyExistingContent | 4
}
/// <summary>
/// Indicates that the marked method is assertion method, i.e. it halts control flow if
/// one of the conditions is satisfied. To set the condition, mark one of the parameters with
/// <see cref="AssertionConditionAttribute"/> attribute.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class AssertionMethodAttribute : Attribute { }
/// <summary>
/// Indicates the condition parameter of the assertion method. The method itself should be
/// marked by <see cref="AssertionMethodAttribute"/> attribute. The mandatory argument of
/// the attribute is the assertion type.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class AssertionConditionAttribute : Attribute
{
public AssertionConditionAttribute(AssertionConditionType conditionType)
{
ConditionType = conditionType;
}
public AssertionConditionType ConditionType { get; private set; }
}
/// <summary>
/// Specifies assertion type. If the assertion method argument satisfies the condition,
/// then the execution continues. Otherwise, execution is assumed to be halted.
/// </summary>
public enum AssertionConditionType
{
/// <summary>Marked parameter should be evaluated to true.</summary>
IS_TRUE = 0,
/// <summary>Marked parameter should be evaluated to false.</summary>
IS_FALSE = 1,
/// <summary>Marked parameter should be evaluated to null value.</summary>
IS_NULL = 2,
/// <summary>Marked parameter should be evaluated to not null value.</summary>
IS_NOT_NULL = 3,
}
/// <summary>
/// Indicates that the marked method unconditionally terminates control flow execution.
/// For example, it could unconditionally throw exception.
/// </summary>
[Obsolete("Use [ContractAnnotation('=> halt')] instead")]
[AttributeUsage(AttributeTargets.Method)]
public sealed class TerminatesProgramAttribute : Attribute { }
/// <summary>
/// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select,
/// .Where). This annotation allows inference of [InstantHandle] annotation for parameters
/// of delegate type by analyzing LINQ method chains.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class LinqTunnelAttribute : Attribute { }
/// <summary>
/// Indicates that IEnumerable, passed as parameter, is not enumerated.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class NoEnumerationAttribute : Attribute { }
/// <summary>
/// Indicates that parameter is regular expression pattern.
/// </summary>
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class RegexPatternAttribute : Attribute { }
/// <summary>
/// XAML attribute. Indicates the type that has <c>ItemsSource</c> property and should be treated
/// as <c>ItemsControl</c>-derived type, to enable inner items <c>DataContext</c> type resolve.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class XamlItemsControlAttribute : Attribute { }
/// <summary>
/// XAML attribute. Indicates the property of some <c>BindingBase</c>-derived type, that
/// is used to bind some item of <c>ItemsControl</c>-derived type. This annotation will
/// enable the <c>DataContext</c> type resolve for XAML bindings for such properties.
/// </summary>
/// <remarks>
/// Property should have the tree ancestor of the <c>ItemsControl</c> type or
/// marked with the <see cref="XamlItemsControlAttribute"/> attribute.
/// </remarks>
[AttributeUsage(AttributeTargets.Property)]
public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class AspChildControlTypeAttribute : Attribute
{
public AspChildControlTypeAttribute(string tagName, Type controlType)
{
TagName = tagName;
ControlType = controlType;
}
public string TagName { get; private set; }
public Type ControlType { get; private set; }
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
public sealed class AspDataFieldAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
public sealed class AspDataFieldsAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Property)]
public sealed class AspMethodPropertyAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class AspRequiredAttributeAttribute : Attribute
{
public AspRequiredAttributeAttribute([NotNull] string attribute)
{
Attribute = attribute;
}
public string Attribute { get; private set; }
}
[AttributeUsage(AttributeTargets.Property)]
public sealed class AspTypePropertyAttribute : Attribute
{
public bool CreateConstructorReferences { get; private set; }
public AspTypePropertyAttribute(bool createConstructorReferences)
{
CreateConstructorReferences = createConstructorReferences;
}
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class RazorImportNamespaceAttribute : Attribute
{
public RazorImportNamespaceAttribute(string name)
{
Name = name;
}
public string Name { get; private set; }
}
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public sealed class RazorInjectionAttribute : Attribute
{
public RazorInjectionAttribute(string type, string fieldName)
{
Type = type;
FieldName = fieldName;
}
public string Type { get; private set; }
public string FieldName { get; private set; }
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class RazorHelperCommonAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Property)]
public sealed class RazorLayoutAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method)]
public sealed class RazorWriteLiteralMethodAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Method)]
public sealed class RazorWriteMethodAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class RazorWriteMethodParameterAttribute : Attribute { }
/// <summary>
/// Prevents the Member Reordering feature from tossing members of the marked class.
/// </summary>
/// <remarks>
/// The attribute must be mentioned in your member reordering patterns
/// </remarks>
[AttributeUsage(AttributeTargets.All)]
public sealed class NoReorder : Attribute { }
}

View File

@@ -1,17 +1,19 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Windows.Controls;
using Wox.Plugin.WebSearch.Annotations;
using Wox.Plugin.WebSearch.SuggestionSources; using Wox.Plugin.WebSearch.SuggestionSources;
namespace Wox.Plugin.WebSearch namespace Wox.Plugin.WebSearch
{ {
public class WebSearchPlugin : IPlugin, ISettingProvider, IPluginI18n, IInstantQuery public class WebSearchPlugin : IPlugin, ISettingProvider, IPluginI18n, IInstantQuery, IMultipleActionKeywords
{ {
private PluginInitContext context; public PluginInitContext Context { get; private set; }
private IDisposable suggestionTimer;
public List<Result> Query(Query query) public List<Result> Query(Query query)
{ {
@@ -21,71 +23,63 @@ namespace Wox.Plugin.WebSearch
if (webSearch != null) if (webSearch != null)
{ {
string keyword = query.ActionKeyword; string keyword = query.Search;
string title = keyword; string title = keyword;
string subtitle = context.API.GetTranslation("wox_plugin_websearch_search") + " " + webSearch.Title; string subtitle = Context.API.GetTranslation("wox_plugin_websearch_search") + " " + webSearch.Title;
if (string.IsNullOrEmpty(keyword)) if (string.IsNullOrEmpty(keyword))
{ {
title = subtitle; title = subtitle;
subtitle = null; subtitle = string.Empty;
} }
context.API.PushResults(query, context.CurrentPluginMetadata, new List<Result>() var result = new Result
{ {
new Result() Title = title,
SubTitle = subtitle,
Score = 6,
IcoPath = webSearch.IconPath,
Action = c =>
{ {
Title = title, Process.Start(webSearch.Url.Replace("{q}", Uri.EscapeDataString(keyword ?? string.Empty)));
SubTitle = subtitle, return true;
Score = 6,
IcoPath = webSearch.IconPath,
Action = (c) =>
{
Process.Start(webSearch.Url.Replace("{q}", Uri.EscapeDataString(keyword)));
return true;
}
} }
}); };
results.Add(result);
if (WebSearchStorage.Instance.EnableWebSearchSuggestion && !string.IsNullOrEmpty(keyword)) if (WebSearchStorage.Instance.EnableWebSearchSuggestion && !string.IsNullOrEmpty(keyword))
{ {
if (suggestionTimer != null) // todo use Task.Wait when .net upgraded
{ results.AddRange(ResultsFromSuggestions(keyword, subtitle, webSearch));
suggestionTimer.Dispose();
}
suggestionTimer = EasyTimer.SetTimeout(() => { QuerySuggestions(keyword, query, subtitle, webSearch); }, 350);
} }
} }
return results; return results;
} }
private void QuerySuggestions(string keyword, Query query, string subtitle, WebSearch webSearch) private IEnumerable<Result> ResultsFromSuggestions(string keyword, string subtitle, WebSearch webSearch)
{ {
ISuggestionSource sugg = SuggestionSourceFactory.GetSuggestionSource(WebSearchStorage.Instance.WebSearchSuggestionSource, context); ISuggestionSource sugg = SuggestionSourceFactory.GetSuggestionSource(WebSearchStorage.Instance.WebSearchSuggestionSource, Context);
if (sugg != null) var suggestions = sugg?.GetSuggestions(keyword);
if (suggestions != null)
{ {
var result = sugg.GetSuggestions(keyword); var resultsFromSuggestion = suggestions.Select(o => new Result
if (result != null)
{ {
context.API.PushResults(query, context.CurrentPluginMetadata, Title = o,
result.Select(o => new Result() SubTitle = subtitle,
{ Score = 5,
Title = o, IcoPath = webSearch.IconPath,
SubTitle = subtitle, Action = c =>
Score = 5, {
IcoPath = webSearch.IconPath, Process.Start(webSearch.Url.Replace("{q}", Uri.EscapeDataString(o)));
Action = (c) => return true;
{ }
Process.Start(webSearch.Url.Replace("{q}", Uri.EscapeDataString(o))); });
return true; return resultsFromSuggestion;
}
}).ToList());
}
} }
return new List<Result>();
} }
public void Init(PluginInitContext context) public void Init(PluginInitContext context)
{ {
this.context = context; this.Context = context;
if (WebSearchStorage.Instance.WebSearches == null) if (WebSearchStorage.Instance.WebSearches == null)
WebSearchStorage.Instance.WebSearches = WebSearchStorage.Instance.LoadDefaultWebSearches(); WebSearchStorage.Instance.WebSearches = WebSearchStorage.Instance.LoadDefaultWebSearches();
@@ -93,9 +87,9 @@ namespace Wox.Plugin.WebSearch
#region ISettingProvider Members #region ISettingProvider Members
public System.Windows.Controls.Control CreateSettingPanel() public Control CreateSettingPanel()
{ {
return new WebSearchesSetting(context); return new WebSearchesSetting(this);
} }
#endregion #endregion
@@ -107,15 +101,35 @@ namespace Wox.Plugin.WebSearch
public string GetTranslatedPluginTitle() public string GetTranslatedPluginTitle()
{ {
return context.API.GetTranslation("wox_plugin_websearch_plugin_name"); return Context.API.GetTranslation("wox_plugin_websearch_plugin_name");
} }
public string GetTranslatedPluginDescription() public string GetTranslatedPluginDescription()
{ {
return context.API.GetTranslation("wox_plugin_websearch_plugin_description"); return Context.API.GetTranslation("wox_plugin_websearch_plugin_description");
} }
public bool IsInstantQuery(string query) => false; public bool IsInstantQuery(string query) => false;
[NotifyPropertyChangedInvocator]
public void NotifyActionKeywordsUpdated(string oldActionKeywords, string newActionKeywords)
{
ActionKeywordsChanged?.Invoke(this, new ActionKeywordsChangedEventArgs
{
OldActionKeyword = oldActionKeywords,
NewActionKeyword = newActionKeywords
});
}
[NotifyPropertyChangedInvocator]
public void NotifyActionKeywordsAdded(string newActionKeywords)
{
ActionKeywordsChanged?.Invoke(this, new ActionKeywordsChangedEventArgs
{
NewActionKeyword = newActionKeywords
});
}
public event ActionKeywordsChangedEventHandler ActionKeywordsChanged;
} }
} }

View File

@@ -37,7 +37,7 @@
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="5" Grid.Column="1"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="5" Grid.Column="1">
<Button x:Name="btnCancel" Click="BtnCancel_OnClick" Margin="10 0 10 0" Width="80" Height="25" Content="{DynamicResource wox_plugin_websearch_cancel}"></Button> <Button x:Name="btnCancel" Click="BtnCancel_OnClick" Margin="10 0 10 0" Width="80" Height="25" Content="{DynamicResource wox_plugin_websearch_cancel}"></Button>
<Button x:Name="btnAdd" Margin="10 0 10 0" Width="80" Height="25" Click="btnAdd_OnClick"> <Button x:Name="btnConfirm" Margin="10 0 10 0" Width="80" Height="25" Click="btnConfirm_OnClick">
<TextBlock x:Name="lblAdd" Text="{DynamicResource wox_plugin_websearch_add}"></TextBlock></Button> <TextBlock x:Name="lblAdd" Text="{DynamicResource wox_plugin_websearch_add}"></TextBlock></Button>
</StackPanel> </StackPanel>
</Grid> </Grid>

View File

@@ -5,37 +5,41 @@ using System.Reflection;
using System.Windows; using System.Windows;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using Microsoft.Win32; using Microsoft.Win32;
using Wox.Infrastructure.Exception;
namespace Wox.Plugin.WebSearch namespace Wox.Plugin.WebSearch
{ {
public partial class WebSearchSetting : Window public partial class WebSearchSetting : Window
{ {
private string defaultWebSearchImageDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Images\\websearch"); private const string _imageDirectoryName = "Images";
private WebSearchesSetting settingWindow; private string _pluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
private bool update; private readonly WebSearchesSetting _settingWindow;
private WebSearch updateWebSearch; private bool _isUpdate;
private PluginInitContext context; private WebSearch _updateWebSearch;
private readonly PluginInitContext _context;
private readonly WebSearchPlugin _plguin;
public WebSearchSetting(WebSearchesSetting settingWidow,PluginInitContext context) public WebSearchSetting(WebSearchesSetting settingWidow)
{ {
this.context = context; _plguin = settingWidow.Plugin;
this.settingWindow = settingWidow; _context = settingWidow.Context;
_settingWindow = settingWidow;
InitializeComponent(); InitializeComponent();
} }
public void UpdateItem(WebSearch webSearch) public void UpdateItem(WebSearch webSearch)
{ {
updateWebSearch = WebSearchStorage.Instance.WebSearches.FirstOrDefault(o => o == webSearch); _updateWebSearch = WebSearchStorage.Instance.WebSearches.FirstOrDefault(o => o == webSearch);
if (updateWebSearch == null || string.IsNullOrEmpty(updateWebSearch.Url)) if (_updateWebSearch == null || string.IsNullOrEmpty(_updateWebSearch.Url))
{ {
string warning = context.API.GetTranslation("wox_plugin_websearch_invalid_web_search"); string warning = _context.API.GetTranslation("wox_plugin_websearch_invalid_web_search");
MessageBox.Show(warning); MessageBox.Show(warning);
Close(); Close();
return; return;
} }
update = true; _isUpdate = true;
lblAdd.Text = "Update"; lblAdd.Text = "Update";
tbIconPath.Text = webSearch.IconPath; tbIconPath.Text = webSearch.IconPath;
ShowIcon(webSearch.IconPath); ShowIcon(webSearch.IconPath);
@@ -47,13 +51,7 @@ namespace Wox.Plugin.WebSearch
private void ShowIcon(string path) private void ShowIcon(string path)
{ {
try imgIcon.Source = new BitmapImage(new Uri(Path.Combine(_pluginDirectory, path), UriKind.Absolute));
{
imgIcon.Source = new BitmapImage(new Uri(path));
}
catch (Exception)
{
}
} }
private void BtnCancel_OnClick(object sender, RoutedEventArgs e) private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
@@ -61,12 +59,15 @@ namespace Wox.Plugin.WebSearch
Close(); Close();
} }
private void btnAdd_OnClick(object sender, RoutedEventArgs e) /// <summary>
/// Confirm button for both add and update
/// </summary>
private void btnConfirm_OnClick(object sender, RoutedEventArgs e)
{ {
string title = tbTitle.Text; string title = tbTitle.Text;
if (string.IsNullOrEmpty(title)) if (string.IsNullOrEmpty(title))
{ {
string warning = context.API.GetTranslation("wox_plugin_websearch_input_title"); string warning = _context.API.GetTranslation("wox_plugin_websearch_input_title");
MessageBox.Show(warning); MessageBox.Show(warning);
return; return;
} }
@@ -74,89 +75,79 @@ namespace Wox.Plugin.WebSearch
string url = tbUrl.Text; string url = tbUrl.Text;
if (string.IsNullOrEmpty(url)) if (string.IsNullOrEmpty(url))
{ {
string warning = context.API.GetTranslation("wox_plugin_websearch_input_url"); string warning = _context.API.GetTranslation("wox_plugin_websearch_input_url");
MessageBox.Show(warning); MessageBox.Show(warning);
return; return;
} }
string action = tbActionword.Text; string newActionKeyword = tbActionword.Text.Trim();
if (string.IsNullOrEmpty(action)) if (_isUpdate)
{ {
string warning = context.API.GetTranslation("wox_plugin_websearch_input_action_keyword"); try
MessageBox.Show(warning);
return;
}
if (!update)
{
if (WebSearchStorage.Instance.WebSearches.Exists(o => o.ActionKeyword == action))
{ {
string warning = context.API.GetTranslation("wox_plugin_websearch_action_keyword_exist"); _plguin.NotifyActionKeywordsUpdated(_updateWebSearch.ActionKeyword, newActionKeyword);
MessageBox.Show(warning); }
catch (WoxPluginException exception)
{
MessageBox.Show(exception.Message);
return;
}
_updateWebSearch.ActionKeyword = newActionKeyword;
_updateWebSearch.IconPath = tbIconPath.Text;
_updateWebSearch.Enabled = cbEnable.IsChecked ?? false;
_updateWebSearch.Url = url;
_updateWebSearch.Title = title;
}
else
{
try
{
_plguin.NotifyActionKeywordsAdded(newActionKeyword);
}
catch (WoxPluginException exception)
{
MessageBox.Show(exception.Message);
return; return;
} }
WebSearchStorage.Instance.WebSearches.Add(new WebSearch() WebSearchStorage.Instance.WebSearches.Add(new WebSearch()
{ {
ActionKeyword = action, ActionKeyword = newActionKeyword,
Enabled = cbEnable.IsChecked ?? false, Enabled = cbEnable.IsChecked ?? false,
IconPath = tbIconPath.Text, IconPath = tbIconPath.Text,
Url = url, Url = url,
Title = title Title = title
}); });
//save the action keywords, the order is not metters. Wox will read this metadata when save settings.
context.CurrentPluginMetadata.ActionKeywords.Add(action);
string msg = context.API.GetTranslation("wox_plugin_websearch_succeed");
MessageBox.Show(msg);
} }
else
{
if (WebSearchStorage.Instance.WebSearches.Exists(o => o.ActionKeyword == action && o != updateWebSearch))
{
string warning = context.API.GetTranslation("wox_plugin_websearch_action_keyword_exist");
MessageBox.Show(warning);
return;
}
updateWebSearch.ActionKeyword = action;
updateWebSearch.IconPath = tbIconPath.Text;
updateWebSearch.Enabled = cbEnable.IsChecked ?? false;
updateWebSearch.Url = url;
updateWebSearch.Title= title;
//save the action keywords, the order is not metters. Wox will read this metadata when save settings.
context.CurrentPluginMetadata.ActionKeywords.Add(action);
string msg = context.API.GetTranslation("wox_plugin_websearch_succeed");
MessageBox.Show(msg);
}
WebSearchStorage.Instance.Save(); WebSearchStorage.Instance.Save();
_settingWindow.ReloadWebSearchView();
settingWindow.ReloadWebSearchView();
Close(); Close();
} }
private void BtnSelectIcon_OnClick(object sender, RoutedEventArgs e) private void BtnSelectIcon_OnClick(object sender, RoutedEventArgs e)
{ {
if(!Directory.Exists(defaultWebSearchImageDirectory)) if (!Directory.Exists(_pluginDirectory))
{ {
defaultWebSearchImageDirectory = _pluginDirectory =
Path.GetDirectoryName(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); Path.GetDirectoryName(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
} }
var dlg = new OpenFileDialog var dlg = new OpenFileDialog
{ {
InitialDirectory = defaultWebSearchImageDirectory, InitialDirectory = Path.Combine(_pluginDirectory, _imageDirectoryName),
Filter ="Image files (*.jpg, *.jpeg, *.gif, *.png, *.bmp) |*.jpg; *.jpeg; *.gif; *.png; *.bmp" Filter = "Image files (*.jpg, *.jpeg, *.gif, *.png, *.bmp) |*.jpg; *.jpeg; *.gif; *.png; *.bmp"
}; };
bool? result = dlg.ShowDialog(); bool? result = dlg.ShowDialog();
if (result == true) if (result == true)
{ {
string filename = dlg.FileName; string filename = dlg.FileName;
tbIconPath.Text = filename; if (filename != null)
ShowIcon(filename); {
tbIconPath.Text = Path.Combine(_imageDirectoryName, Path.GetFileName(filename));
ShowIcon(tbIconPath.Text);
}
} }
} }
} }

View File

@@ -41,7 +41,7 @@ namespace Wox.Plugin.WebSearch
{ {
Title = "Google", Title = "Google",
ActionKeyword = "g", ActionKeyword = "g",
IconPath = @"Images\websearch\google.png", IconPath = @"Images\google.png",
Url = "https://www.google.com/search?q={q}", Url = "https://www.google.com/search?q={q}",
Enabled = true Enabled = true
}; };
@@ -52,7 +52,7 @@ namespace Wox.Plugin.WebSearch
{ {
Title = "Wikipedia", Title = "Wikipedia",
ActionKeyword = "wiki", ActionKeyword = "wiki",
IconPath = @"Images\websearch\wiki.png", IconPath = @"Images\wiki.png",
Url = "http://en.wikipedia.org/wiki/{q}", Url = "http://en.wikipedia.org/wiki/{q}",
Enabled = true Enabled = true
}; };
@@ -62,7 +62,7 @@ namespace Wox.Plugin.WebSearch
{ {
Title = "FindIcon", Title = "FindIcon",
ActionKeyword = "findicon", ActionKeyword = "findicon",
IconPath = @"Images\websearch\pictures.png", IconPath = @"Images\pictures.png",
Url = "http://findicons.com/search/{q}", Url = "http://findicons.com/search/{q}",
Enabled = true Enabled = true
}; };

View File

@@ -10,14 +10,14 @@ namespace Wox.Plugin.WebSearch
/// </summary> /// </summary>
public partial class WebSearchesSetting : UserControl public partial class WebSearchesSetting : UserControl
{ {
PluginInitContext context; public PluginInitContext Context { get; }
public WebSearchPlugin Plugin { get; }
public WebSearchesSetting(PluginInitContext context) public WebSearchesSetting(WebSearchPlugin plugin)
{ {
this.context = context; Context = plugin.Context;
Plugin = plugin;
InitializeComponent(); InitializeComponent();
Loaded += Setting_Loaded; Loaded += Setting_Loaded;
} }
@@ -28,7 +28,7 @@ namespace Wox.Plugin.WebSearch
comboBoxSuggestionSource.Visibility = WebSearchStorage.Instance.EnableWebSearchSuggestion comboBoxSuggestionSource.Visibility = WebSearchStorage.Instance.EnableWebSearchSuggestion
? Visibility.Visible ? Visibility.Visible
: Visibility.Collapsed; : Visibility.Collapsed;
List<ComboBoxItem> items = new List<ComboBoxItem>() List<ComboBoxItem> items = new List<ComboBoxItem>()
{ {
new ComboBoxItem() {Content = "Google"}, new ComboBoxItem() {Content = "Google"},
@@ -51,7 +51,7 @@ namespace Wox.Plugin.WebSearch
private void btnAddWebSearch_OnClick(object sender, RoutedEventArgs e) private void btnAddWebSearch_OnClick(object sender, RoutedEventArgs e)
{ {
WebSearchSetting webSearch = new WebSearchSetting(this,context); WebSearchSetting webSearch = new WebSearchSetting(this);
webSearch.ShowDialog(); webSearch.ShowDialog();
} }
@@ -60,9 +60,9 @@ namespace Wox.Plugin.WebSearch
WebSearch selectedWebSearch = webSearchView.SelectedItem as WebSearch; WebSearch selectedWebSearch = webSearchView.SelectedItem as WebSearch;
if (selectedWebSearch != null) if (selectedWebSearch != null)
{ {
string msg = string.Format(context.API.GetTranslation("wox_plugin_websearch_delete_warning"),selectedWebSearch.Title); string msg = string.Format(Context.API.GetTranslation("wox_plugin_websearch_delete_warning"), selectedWebSearch.Title);
if (MessageBox.Show(msg,string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes) if (MessageBox.Show(msg, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{ {
WebSearchStorage.Instance.WebSearches.Remove(selectedWebSearch); WebSearchStorage.Instance.WebSearches.Remove(selectedWebSearch);
webSearchView.Items.Refresh(); webSearchView.Items.Refresh();
@@ -70,7 +70,7 @@ namespace Wox.Plugin.WebSearch
} }
else else
{ {
string warning =context.API.GetTranslation("wox_plugin_websearch_pls_select_web_search"); string warning = Context.API.GetTranslation("wox_plugin_websearch_pls_select_web_search");
MessageBox.Show(warning); MessageBox.Show(warning);
} }
} }
@@ -80,13 +80,13 @@ namespace Wox.Plugin.WebSearch
WebSearch selectedWebSearch = webSearchView.SelectedItem as WebSearch; WebSearch selectedWebSearch = webSearchView.SelectedItem as WebSearch;
if (selectedWebSearch != null) if (selectedWebSearch != null)
{ {
WebSearchSetting webSearch = new WebSearchSetting(this,context); WebSearchSetting webSearch = new WebSearchSetting(this);
webSearch.UpdateItem(selectedWebSearch); webSearch.UpdateItem(selectedWebSearch);
webSearch.ShowDialog(); webSearch.ShowDialog();
} }
else else
{ {
string warning = context.API.GetTranslation("wox_plugin_websearch_pls_select_web_search"); string warning = Context.API.GetTranslation("wox_plugin_websearch_pls_select_web_search");
MessageBox.Show(warning); MessageBox.Show(warning);
} }
} }
@@ -109,7 +109,7 @@ namespace Wox.Plugin.WebSearch
{ {
if (e.AddedItems.Count > 0) if (e.AddedItems.Count > 0)
{ {
WebSearchStorage.Instance.WebSearchSuggestionSource = ((ComboBoxItem) e.AddedItems[0]).Content.ToString(); WebSearchStorage.Instance.WebSearchSuggestionSource = ((ComboBoxItem)e.AddedItems[0]).Content.ToString();
WebSearchStorage.Instance.Save(); WebSearchStorage.Instance.Save();
} }
} }

View File

@@ -50,7 +50,7 @@
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="EasyTimer.cs" /> <Compile Include="Properties\Annotations.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SuggestionSources\Baidu.cs" /> <Compile Include="SuggestionSources\Baidu.cs" />
<Compile Include="SuggestionSources\Google.cs" /> <Compile Include="SuggestionSources\Google.cs" />
@@ -67,7 +67,13 @@
<Compile Include="WebSearchStorage.cs" /> <Compile Include="WebSearchStorage.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Images\websearch\pictures.png"> <None Include="Images\google.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Images\pictures.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Images\wiki.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<Content Include="Languages\en.xaml"> <Content Include="Languages\en.xaml">
@@ -94,9 +100,6 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Page> </Page>
</ItemGroup> </ItemGroup>
<ItemGroup>
<WCFMetadata Include="Service References\" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Wox.Infrastructure\Wox.Infrastructure.csproj"> <ProjectReference Include="..\..\Wox.Infrastructure\Wox.Infrastructure.csproj">
<Project>{4fd29318-a8ab-4d8f-aa47-60bc241b8da3}</Project> <Project>{4fd29318-a8ab-4d8f-aa47-60bc241b8da3}</Project>
@@ -118,16 +121,6 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="Images\websearch\google.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Include="Images\websearch\wiki.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -1,13 +1,13 @@
Wox [![Build status](https://ci.appveyor.com/api/projects/status/bfktntbivg32e103)](https://ci.appveyor.com/project/qianlifeng/wox) <a href="https://chocolatey.org/packages/wox"><img src="https://img.shields.io/badge/chocolatey-wox-b4884f.svg?style=flat" style="height:auto; width: 100%"></a> [![Bountysource](https://www.bountysource.com/badge/team?team_id=39433&style=raised)](https://www.bountysource.com/teams/wox?utm_source=Wox&utm_medium=shield&utm_campaign=raised) Wox [![Build status](https://ci.appveyor.com/api/projects/status/bfktntbivg32e103)](https://ci.appveyor.com/project/qianlifeng/wox) [![Bountysource](https://www.bountysource.com/badge/team?team_id=39433&style=raised)](https://www.bountysource.com/teams/wox?utm_source=Wox&utm_medium=shield&utm_campaign=raised)
========= =========
[Wox](http://www.getwox.com) is a launcher for windows, inspired by [Alfred](http://www.alfredapp.com/) and [Launchy](http://www.launchy.net/). Wox indexes your programs, your documents, project files, folders, and bookmarks. You can launch everything with just a few keystrokes!. [Wox](http://www.getwox.com) is a launcher for windows, inspired by [Alfred](http://www.alfredapp.com/) and [Launchy](http://www.launchy.net/). You can launch everything with just a few keystrokes!.
Features Features
========= =========
1. Search for applications, folders, files and in the browser bookmarks (using a plugin) 1. Search all applications, folders, files, [bookmarks](https://www.getwox.com/plugin/16).
2. Search the web using shortcuts (e.g. search google with `g keyword` or Youtube `youtube keyword`) 2. Search Web using keyword (e.g. search google with `g search_term`)
3. Themes support, get more themes from [http://www.getwox.com/theme/builder](http://www.getwox.com/theme/builder) 3. Build custom themes in [http://www.getwox.com/theme/builder](http://www.getwox.com/theme/builder)
4. Plugin support, get more plugins from [http://www.getwox.com/plugin](http://www.getwox.com/plugin) 4. Install plugins from [http://www.getwox.com/plugin](http://www.getwox.com/plugin)
Screenshot Screenshot
========= =========
@@ -35,18 +35,13 @@ Simple Usage
Contribute Contribute
========= =========
If you are a developer, feel free to send a pull request to the **Dev** branch. We still have a lot of features and bugs to resolve. Take a look at [issues page](https://github.com/qianlifeng/Wox/issues) if you want to contribute :) If you are a developer, please feel free to send a pull request to the **Dev** branch. We still have a lot of features and bugs to resolve. Take a look at [issues page](https://github.com/qianlifeng/Wox/issues) if you want to contribute :)
If you are not a developer, you can still contribute to the project. Install the beta version, test things and help us with new entries and corrections to the docs [Wox doc](http://doc.getwox.com). Feedback is always welcome! If you are not a developer, please install the [Latest build](https://ci.appveyor.com/project/qianlifeng/wox/history) and report bugs (how to: [English](https://github.com/Wox-launcher/Wox/wiki/FAQ-English) [中文](https://github.com/Wox-launcher/Wox/wiki/FAQ-%E4%B8%AD%E6%96%87)). Feedback is always welcome!
Docs Docs
========= =========
Full documentation can be found here [Wox Documentation](http://doc.getwox.com). This is a good place to start if you are installing Wox for the first time. Feel free to contribute if you want to improve, correct or translate the documentation. Full documentation can be found here [Wox doc](http://doc.getwox.com). This is a good place to start if you are installing Wox for the first time.
In the future, all of the documentation will be moved back to Github wiki. In the future, all of the documentation will be moved back to Github wiki.
Discussion
=========
Do you have a question or an idea for a killer feature? Discuss things with our community at the [Wox Fourms](http://discussion.getwox.com).

View File

@@ -1,13 +0,0 @@
namespace Wox.Core.Exception
{
public class WoxPluginException : WoxException
{
public string PluginName { get; set; }
public WoxPluginException(string pluginName,System.Exception e)
: base(e.Message,e)
{
PluginName = pluginName;
}
}
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Wox.Infrastructure.Exception;
using Wox.Infrastructure.Logger; using Wox.Infrastructure.Logger;
using Wox.Plugin; using Wox.Plugin;
@@ -19,10 +20,10 @@ namespace Wox.Core.Plugin
try try
{ {
Assembly asm = Assembly.Load(AssemblyName.GetAssemblyName(metadata.ExecuteFilePath)); Assembly asm = Assembly.Load(AssemblyName.GetAssemblyName(metadata.ExecuteFilePath));
List<Type> types = asm.GetTypes().Where(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))).ToList(); List<Type> types = asm.GetTypes().Where(o => o.IsClass && !o.IsAbstract && o.GetInterfaces().Contains(typeof(IPlugin))).ToList();
if (types.Count == 0) if (types.Count == 0)
{ {
Log.Warn(string.Format("Couldn't load plugin {0}: didn't find the class that implement IPlugin", metadata.Name)); Log.Warn($"Couldn't load plugin {metadata.Name}: didn't find the class that implement IPlugin");
continue; continue;
} }
@@ -39,12 +40,7 @@ namespace Wox.Core.Plugin
} }
catch (System.Exception e) catch (System.Exception e)
{ {
Log.Error(string.Format("Couldn't load plugin {0}: {1}", metadata.Name, e.Message)); Log.Error(new WoxPluginException(metadata.Name, $"Couldn't load plugin", e));
#if (DEBUG)
{
throw;
}
#endif
} }
} }

View File

@@ -5,7 +5,7 @@ using System.Reflection;
using System.Threading; using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using Newtonsoft.Json; using Newtonsoft.Json;
using Wox.Core.Exception; using Wox.Infrastructure.Exception;
using Wox.Infrastructure.Logger; using Wox.Infrastructure.Logger;
using Wox.Plugin; using Wox.Plugin;
@@ -74,7 +74,7 @@ namespace Wox.Core.Plugin
} }
catch (System.Exception e) catch (System.Exception e)
{ {
Log.Error(e.Message); Log.Error(e);
} }
} }
return null; return null;

View File

@@ -2,8 +2,8 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using Wox.Core.Exception;
using Wox.Core.UserSettings; using Wox.Core.UserSettings;
using Wox.Infrastructure.Exception;
using Wox.Infrastructure.Logger; using Wox.Infrastructure.Logger;
using Wox.Plugin; using Wox.Plugin;
@@ -47,7 +47,7 @@ namespace Wox.Core.Plugin
} }
catch (System.Exception e) catch (System.Exception e)
{ {
Log.Error(ExceptionFormatter.FormatExcpetion(e)); Log.Fatal(e);
} }
} }
PluginMetadata metadata = GetPluginMetadata(directory); PluginMetadata metadata = GetPluginMetadata(directory);
@@ -63,7 +63,7 @@ namespace Wox.Core.Plugin
string configPath = Path.Combine(pluginDirectory, pluginConfigName); string configPath = Path.Combine(pluginDirectory, pluginConfigName);
if (!File.Exists(configPath)) if (!File.Exists(configPath))
{ {
Log.Warn(string.Format("parse plugin {0} failed: didn't find config file.", configPath)); Log.Warn($"parse plugin {configPath} failed: didn't find config file.");
return null; return null;
} }
@@ -77,40 +77,25 @@ namespace Wox.Core.Plugin
// for plugin still use old ActionKeyword // for plugin still use old ActionKeyword
metadata.ActionKeyword = metadata.ActionKeywords?[0]; metadata.ActionKeyword = metadata.ActionKeywords?[0];
} }
catch (System.Exception) catch (System.Exception e)
{ {
string error = string.Format("Parse plugin config {0} failed: json format is not valid", configPath); string msg = $"Parse plugin config {configPath} failed: json format is not valid";
Log.Warn(error); Log.Error(new WoxException(msg));
#if (DEBUG)
{
throw new WoxException(error);
}
#endif
return null; return null;
} }
if (!AllowedLanguage.IsAllowed(metadata.Language)) if (!AllowedLanguage.IsAllowed(metadata.Language))
{ {
string error = string.Format("Parse plugin config {0} failed: invalid language {1}", configPath, metadata.Language); string msg = $"Parse plugin config {configPath} failed: invalid language {metadata.Language}";
Log.Warn(error); Log.Error(new WoxException(msg));
#if (DEBUG)
{
throw new WoxException(error);
}
#endif
return null; return null;
} }
if (!File.Exists(metadata.ExecuteFilePath)) if (!File.Exists(metadata.ExecuteFilePath))
{ {
string error = string.Format("Parse plugin config {0} failed: ExecuteFile {1} didn't exist", configPath, metadata.ExecuteFilePath); string msg = $"Parse plugin config {configPath} failed: ExecuteFile {metadata.ExecuteFilePath} didn't exist";
Log.Warn(error); Log.Error(new WoxException(msg));
#if (DEBUG)
{
throw new WoxException(error);
}
#endif
return null; return null;
} }

View File

@@ -4,13 +4,13 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using Wox.Core.Exception;
using Wox.Core.i18n; using Wox.Core.i18n;
using Wox.Core.UI; using Wox.Core.UI;
using Wox.Core.UserSettings; using Wox.Core.UserSettings;
using Wox.Infrastructure;
using Wox.Infrastructure.Exception;
using Wox.Infrastructure.Logger; using Wox.Infrastructure.Logger;
using Wox.Plugin; using Wox.Plugin;
using Stopwatch = Wox.Infrastructure.Stopwatch;
namespace Wox.Core.Plugin namespace Wox.Core.Plugin
{ {
@@ -30,7 +30,7 @@ namespace Wox.Core.Plugin
public static IEnumerable<PluginPair> AllPlugins { get; private set; } public static IEnumerable<PluginPair> AllPlugins { get; private set; }
public static List<PluginPair> GlobalPlugins { get; } = new List<PluginPair>(); public static List<PluginPair> GlobalPlugins { get; } = new List<PluginPair>();
public static Dictionary<string, PluginPair> NonGlobalPlugins { get; } = new Dictionary<string, PluginPair>(); public static Dictionary<string, PluginPair> NonGlobalPlugins { get; set; } = new Dictionary<string, PluginPair>();
private static IEnumerable<PluginPair> InstantQueryPlugins { get; set; } private static IEnumerable<PluginPair> InstantQueryPlugins { get; set; }
public static IPublicAPI API { private set; get; } public static IPublicAPI API { private set; get; }
@@ -56,9 +56,9 @@ namespace Wox.Core.Plugin
{ {
Directory.CreateDirectory(pluginDirectory); Directory.CreateDirectory(pluginDirectory);
} }
catch (System.Exception e) catch (Exception e)
{ {
Log.Error(e.Message); Log.Error(e);
} }
} }
} }
@@ -69,7 +69,7 @@ namespace Wox.Core.Plugin
/// </summary> /// </summary>
public static void Init(IPublicAPI api) public static void Init(IPublicAPI api)
{ {
if (api == null) throw new WoxCritialException("api is null"); if (api == null) throw new WoxFatalException("api is null");
SetupPluginDirectories(); SetupPluginDirectories();
API = api; API = api;
@@ -164,7 +164,7 @@ namespace Wox.Core.Plugin
if (customizedPluginConfig != null && customizedPluginConfig.Disabled) continue; if (customizedPluginConfig != null && customizedPluginConfig.Disabled) continue;
if (IsInstantQueryPlugin(plugin)) if (IsInstantQueryPlugin(plugin))
{ {
Stopwatch.Debug($"Instant Query for {plugin.Metadata.Name}", () => Stopwatch.Normal($"Instant QueryForPlugin for {plugin.Metadata.Name}", () =>
{ {
QueryForPlugin(plugin, query); QueryForPlugin(plugin, query);
}); });
@@ -173,7 +173,10 @@ namespace Wox.Core.Plugin
{ {
ThreadPool.QueueUserWorkItem(state => ThreadPool.QueueUserWorkItem(state =>
{ {
QueryForPlugin(plugin, query); Stopwatch.Normal($"Normal QueryForPlugin for {plugin.Metadata.Name}", () =>
{
QueryForPlugin(plugin, query);
});
}); });
} }
} }
@@ -184,7 +187,7 @@ namespace Wox.Core.Plugin
try try
{ {
List<Result> results = new List<Result>(); List<Result> results = new List<Result>();
var milliseconds = Stopwatch.Normal($"Query for {pair.Metadata.Name}", () => var milliseconds = Stopwatch.Normal($"Plugin.Query cost for {pair.Metadata.Name}", () =>
{ {
results = pair.Plugin.Query(query) ?? results; results = pair.Plugin.Query(query) ?? results;
results.ForEach(o => { o.PluginID = pair.Metadata.ID; }); results.ForEach(o => { o.PluginID = pair.Metadata.ID; });
@@ -193,9 +196,9 @@ namespace Wox.Core.Plugin
pair.AvgQueryTime = pair.QueryCount == 1 ? milliseconds : (pair.AvgQueryTime + milliseconds) / 2; pair.AvgQueryTime = pair.QueryCount == 1 ? milliseconds : (pair.AvgQueryTime + milliseconds) / 2;
API.PushResults(query, pair.Metadata, results); API.PushResults(query, pair.Metadata, results);
} }
catch (System.Exception e) catch (Exception e)
{ {
throw new WoxPluginException(pair.Metadata.Name, e); throw new WoxPluginException(pair.Metadata.Name, $"QueryForPlugin failed", e);
} }
} }
@@ -237,18 +240,66 @@ namespace Wox.Core.Plugin
{ {
return plugin.LoadContextMenus(result); return plugin.LoadContextMenus(result);
} }
catch (System.Exception e) catch (Exception e)
{ {
Log.Error($"Couldn't load plugin context menus {pluginPair.Metadata.Name}: {e.Message}"); Log.Error(new WoxPluginException(pluginPair.Metadata.Name, $"Couldn't load plugin context menus", e));
#if (DEBUG)
{
throw;
}
#endif
} }
} }
return new List<Result>(); return new List<Result>();
} }
public static void UpdateActionKeywordForPlugin(PluginPair plugin, string oldActionKeyword, string newActionKeyword)
{
var actionKeywords = plugin.Metadata.ActionKeywords;
if (string.IsNullOrEmpty(newActionKeyword))
{
string msg = InternationalizationManager.Instance.GetTranslation("newActionKeywordsCannotBeEmpty");
throw new WoxPluginException(plugin.Metadata.Name, msg);
}
if (NonGlobalPlugins.ContainsKey(newActionKeyword))
{
string msg = InternationalizationManager.Instance.GetTranslation("newActionKeywordsHasBeenAssigned");
throw new WoxPluginException(plugin.Metadata.Name, msg);
}
// add new action keyword
if (string.IsNullOrEmpty(oldActionKeyword))
{
actionKeywords.Add(newActionKeyword);
if (newActionKeyword == Query.GlobalPluginWildcardSign)
{
GlobalPlugins.Add(plugin);
}
else
{
NonGlobalPlugins[newActionKeyword] = plugin;
}
}
// update existing action keyword
else
{
int index = actionKeywords.IndexOf(oldActionKeyword);
actionKeywords[index] = newActionKeyword;
if (oldActionKeyword == Query.GlobalPluginWildcardSign)
{
GlobalPlugins.Remove(plugin);
}
else
{
NonGlobalPlugins.Remove(oldActionKeyword);
}
if (newActionKeyword == Query.GlobalPluginWildcardSign)
{
GlobalPlugins.Add(plugin);
}
else
{
NonGlobalPlugins[newActionKeyword] = plugin;
}
}
}
} }
} }

View File

@@ -35,7 +35,7 @@ namespace Wox.Core.Theme
} }
catch (System.Exception e) catch (System.Exception e)
{ {
Log.Error(e.Message); Log.Error(e);
} }
} }
} }

View File

@@ -1,5 +1,5 @@
using System; using System;
using Wox.Core.Exception; using Wox.Infrastructure.Exception;
namespace Wox.Core.Updater namespace Wox.Core.Updater
{ {

View File

@@ -2,9 +2,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using Newtonsoft.Json; using Newtonsoft.Json;
using Wox.Infrastructure.Storage; using Wox.Infrastructure.Storage;
using Wox.Plugin;
namespace Wox.Core.UserSettings namespace Wox.Core.UserSettings
{ {
@@ -118,7 +120,7 @@ namespace Wox.Core.UserSettings
public void IncreaseActivateTimes() public void IncreaseActivateTimes()
{ {
ActivateTimes++; ActivateTimes++;
if (ActivateTimes%15 == 0) if (ActivateTimes % 15 == 0)
{ {
Save(); Save();
} }
@@ -162,6 +164,26 @@ namespace Wox.Core.UserSettings
storage.Language = "en"; storage.Language = "en";
} }
} }
public void UpdateActionKeyword(PluginMetadata metadata)
{
var customizedPluginConfig = CustomizedPluginConfigs.FirstOrDefault(o => o.ID == metadata.ID);
if (customizedPluginConfig == null)
{
CustomizedPluginConfigs.Add(new CustomizedPluginConfig()
{
Disabled = false,
ID = metadata.ID,
Name = metadata.Name,
ActionKeywords = metadata.ActionKeywords
});
}
else
{
customizedPluginConfig.ActionKeywords = metadata.ActionKeywords;
}
Save();
}
} }
public enum OpacityMode public enum OpacityMode

View File

@@ -27,7 +27,7 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>..\Output\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
@@ -58,13 +58,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="APIServer.cs" /> <Compile Include="APIServer.cs" />
<Compile Include="Exception\ExceptionFormatter.cs" />
<Compile Include="Exception\WoxCritialException.cs" />
<Compile Include="Exception\WoxException.cs" />
<Compile Include="Exception\WoxHttpException.cs" />
<Compile Include="Exception\WoxI18nException.cs" />
<Compile Include="Exception\WoxJsonRPCException.cs" />
<Compile Include="Exception\WoxPluginException.cs" />
<Compile Include="Updater\Release.cs" /> <Compile Include="Updater\Release.cs" />
<Compile Include="Updater\UpdaterManager.cs" /> <Compile Include="Updater\UpdaterManager.cs" />
<Compile Include="Updater\WoxUpdateSource.cs" /> <Compile Include="Updater\WoxUpdateSource.cs" />

View File

@@ -4,9 +4,9 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Windows; using System.Windows;
using Wox.Core.Exception;
using Wox.Core.UI; using Wox.Core.UI;
using Wox.Core.UserSettings; using Wox.Core.UserSettings;
using Wox.Infrastructure.Exception;
using Wox.Infrastructure.Logger; using Wox.Infrastructure.Logger;
using Wox.Plugin; using Wox.Plugin;
@@ -32,7 +32,7 @@ namespace Wox.Core.i18n
} }
catch (System.Exception e) catch (System.Exception e)
{ {
Log.Error(e.Message); Log.Error(e);
} }
} }
} }
@@ -122,12 +122,8 @@ namespace Wox.Core.i18n
} }
catch (System.Exception e) catch (System.Exception e)
{ {
Log.Warn("Update Plugin metadata translation failed:" + e.Message); var woxPluginException = new WoxPluginException(pluginPair.Metadata.Name, "Update Plugin metadata translation failed:", e);
#if (DEBUG) Log.Error(woxPluginException);
{
throw;
}
#endif
} }
} }

View File

@@ -5,7 +5,7 @@ using System.Text;
using System.Xml; using System.Xml;
using Microsoft.Win32; using Microsoft.Win32;
namespace Wox.Core.Exception namespace Wox.Infrastructure.Exception
{ {
public class ExceptionFormatter public class ExceptionFormatter
{ {

View File

@@ -1,4 +1,4 @@
namespace Wox.Core.Exception namespace Wox.Infrastructure.Exception
{ {
/// <summary> /// <summary>
/// Base Wox Exceptions /// Base Wox Exceptions
@@ -14,7 +14,6 @@
public WoxException(string msg, System.Exception innerException) public WoxException(string msg, System.Exception innerException)
: base(msg, innerException) : base(msg, innerException)
{ {
} }
} }
} }

View File

@@ -1,11 +1,11 @@
namespace Wox.Core.Exception namespace Wox.Infrastructure.Exception
{ {
/// <summary> /// <summary>
/// Represent exceptions that wox can't handle and MUST close running Wox. /// Represent exceptions that wox can't handle and MUST close running Wox.
/// </summary> /// </summary>
public class WoxCritialException : WoxException public class WoxFatalException : WoxException
{ {
public WoxCritialException(string msg) : base(msg) public WoxFatalException(string msg) : base(msg)
{ {
} }
} }

View File

@@ -1,4 +1,4 @@
namespace Wox.Core.Exception namespace Wox.Infrastructure.Exception
{ {
public class WoxHttpException :WoxException public class WoxHttpException :WoxException
{ {

View File

@@ -1,6 +1,6 @@
namespace Wox.Core.Exception namespace Wox.Infrastructure.Exception
{ {
public class WoxI18nException:WoxException public class WoxI18nException : WoxException
{ {
public WoxI18nException(string msg) : base(msg) public WoxI18nException(string msg) : base(msg)
{ {

View File

@@ -1,4 +1,4 @@
namespace Wox.Core.Exception namespace Wox.Infrastructure.Exception
{ {
public class WoxJsonRPCException : WoxException public class WoxJsonRPCException : WoxException
{ {

View File

@@ -0,0 +1,20 @@
using Wox.Plugin;
namespace Wox.Infrastructure.Exception
{
public class WoxPluginException : WoxException
{
public string PluginName { get; set; }
public WoxPluginException(string pluginName, string msg, System.Exception e)
: base($"{pluginName} : {msg}", e)
{
PluginName = pluginName;
}
public WoxPluginException(string pluginName, string msg) : base(msg)
{
PluginName = pluginName;
}
}
}

View File

@@ -1,5 +1,4 @@
using System; using System.IO;
using System.IO;
using System.Net; using System.Net;
using System.Text; using System.Text;
using Wox.Plugin; using Wox.Plugin;
@@ -55,7 +54,7 @@ namespace Wox.Infrastructure.Http
} }
} }
} }
catch (Exception e) catch (System.Exception e)
{ {
Logger.Log.Error(e); Logger.Log.Error(e);
return string.Empty; return string.Empty;
@@ -108,7 +107,7 @@ namespace Wox.Infrastructure.Http
} }
} }
} }
catch (Exception e) catch (System.Exception e)
{ {
Logger.Log.Error(e); Logger.Log.Error(e);
return string.Empty; return string.Empty;

View File

@@ -1,5 +1,6 @@
using System; using System.Diagnostics;
using NLog; using NLog;
using Wox.Infrastructure.Exception;
namespace Wox.Infrastructure.Logger namespace Wox.Infrastructure.Logger
{ {
@@ -7,34 +8,45 @@ namespace Wox.Infrastructure.Logger
{ {
private static NLog.Logger logger = LogManager.GetCurrentClassLogger(); private static NLog.Logger logger = LogManager.GetCurrentClassLogger();
public static void Error(string msg) public static void Error(System.Exception e)
{ {
logger.Error(msg); #if DEBUG
} throw e;
#else
public static void Error(Exception e) while (e.InnerException != null)
{ {
logger.Error(e.Message + "\r\n" + e.StackTrace); logger.Error(e.Message);
logger.Error(e.StackTrace);
e = e.InnerException;
}
#endif
} }
public static void Debug(string msg) public static void Debug(string msg)
{ {
System.Diagnostics.Debug.WriteLine($"DEBUG: {msg}");
logger.Debug(msg); logger.Debug(msg);
} }
public static void Info(string msg) public static void Info(string msg)
{ {
System.Diagnostics.Debug.WriteLine($"INFO: {msg}");
logger.Info(msg); logger.Info(msg);
} }
public static void Warn(string msg) public static void Warn(string msg)
{ {
System.Diagnostics.Debug.WriteLine($"WARN: {msg}");
logger.Warn(msg); logger.Warn(msg);
} }
public static void Fatal(string msg) public static void Fatal(System.Exception e)
{ {
logger.Fatal(msg); #if DEBUG
throw e;
#else
logger.Fatal(ExceptionFormatter.FormatExcpetion(e));
#endif
} }
} }
} }

View File

@@ -18,14 +18,6 @@ namespace Wox.Infrastructure
#endif #endif
} }
[Conditional("DEBUG")]
private static void WriteTimeInfo(string name, long milliseconds)
{
string info = $"{name} : {milliseconds}ms";
System.Diagnostics.Debug.WriteLine(info);
Log.Info(info);
}
/// <summary> /// <summary>
/// This stopwatch will also appear only in Debug mode /// This stopwatch will also appear only in Debug mode
/// </summary> /// </summary>
@@ -36,7 +28,8 @@ namespace Wox.Infrastructure
action(); action();
stopWatch.Stop(); stopWatch.Stop();
var milliseconds = stopWatch.ElapsedMilliseconds; var milliseconds = stopWatch.ElapsedMilliseconds;
WriteTimeInfo(name, milliseconds); string info = $"{name} : {milliseconds}ms";
Log.Debug(info);
return milliseconds; return milliseconds;
} }

View File

@@ -42,7 +42,7 @@ namespace Wox.Infrastructure.Storage
serializedObject = LoadDefault(); serializedObject = LoadDefault();
#if (DEBUG) #if (DEBUG)
{ {
throw new Exception("deserialize failed"); throw new System.Exception("deserialize failed");
} }
#endif #endif
} }
@@ -53,7 +53,7 @@ namespace Wox.Infrastructure.Storage
} }
} }
} }
catch (Exception e) catch (System.Exception e)
{ {
Log.Error(e); Log.Error(e);
serializedObject = LoadDefault(); serializedObject = LoadDefault();
@@ -101,7 +101,7 @@ namespace Wox.Infrastructure.Storage
binaryFormatter.Serialize(fileStream, serializedObject); binaryFormatter.Serialize(fileStream, serializedObject);
fileStream.Close(); fileStream.Close();
} }
catch (Exception e) catch (System.Exception e)
{ {
Log.Error(e); Log.Error(e);
#if (DEBUG) #if (DEBUG)

View File

@@ -25,7 +25,7 @@ namespace Wox.Infrastructure.Storage
{ {
serializedObject = JsonConvert.DeserializeObject<T>(json); serializedObject = JsonConvert.DeserializeObject<T>(json);
} }
catch (Exception) catch (System.Exception)
{ {
serializedObject = LoadDefault(); serializedObject = LoadDefault();
} }

View File

@@ -115,7 +115,7 @@ namespace Wox.Infrastructure
needsCommandLine = peHeaderReader.OptionalHeader64.Subsystem == 3; needsCommandLine = peHeaderReader.OptionalHeader64.Subsystem == 3;
} }
catch (Exception) catch (System.Exception)
{ {
// Error reading the headers. We will try to run the command the standard way. // Error reading the headers. We will try to run the command the standard way.
needsCommandLine = false; needsCommandLine = false;
@@ -149,7 +149,7 @@ namespace Wox.Infrastructure
{ {
global::System.Diagnostics.Process.Start(startInfo); global::System.Diagnostics.Process.Start(startInfo);
} }
catch (Exception e) catch (System.Exception e)
{ {
if (!startInfo.ErrorDialog) if (!startInfo.ErrorDialog)
throw e; throw e;

View File

@@ -46,9 +46,17 @@
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Exception\ExceptionFormatter.cs" />
<Compile Include="Exception\WoxException.cs" />
<Compile Include="Exception\WoxFatalException.cs" />
<Compile Include="Exception\WoxHttpException.cs" />
<Compile Include="Exception\WoxI18nException.cs" />
<Compile Include="Exception\WoxJsonRPCException.cs" />
<Compile Include="Exception\WoxPluginException.cs" />
<Compile Include="Hotkey\InterceptKeys.cs" /> <Compile Include="Hotkey\InterceptKeys.cs" />
<Compile Include="Hotkey\KeyEvent.cs" /> <Compile Include="Hotkey\KeyEvent.cs" />
<Compile Include="Logger\Log.cs" /> <Compile Include="Logger\Log.cs" />

View File

@@ -38,4 +38,17 @@ namespace Wox.Plugin
string GetTranslatedPluginDescription(); string GetTranslatedPluginDescription();
} }
public interface IMultipleActionKeywords
{
event ActionKeywordsChangedEventHandler ActionKeywordsChanged;
}
public class ActionKeywordsChangedEventArgs : EventArgs
{
public string OldActionKeyword { get; set; }
public string NewActionKeyword { get; set; }
}
public delegate void ActionKeywordsChangedEventHandler(IMultipleActionKeywords sender, ActionKeywordsChangedEventArgs e);
} }

View File

@@ -15,5 +15,24 @@
{ {
return Metadata.Name; return Metadata.Name;
} }
public override bool Equals(object obj)
{
PluginPair r = obj as PluginPair;
if (r != null)
{
return string.Equals(r.Metadata.ID, Metadata.ID);
}
else
{
return false;
}
}
public override int GetHashCode()
{
var hashcode = Metadata.ID?.GetHashCode() ?? 0;
return hashcode;
}
} }
} }

View File

@@ -47,7 +47,10 @@ namespace Wox.Plugin
Result r = obj as Result; Result r = obj as Result;
if (r != null) if (r != null)
{ {
return string.Equals(r.Title, Title) && string.Equals(r.SubTitle, SubTitle); var equality = string.Equals(r.Title, Title) &&
string.Equals(r.SubTitle, SubTitle) &&
r.Score == Score;
return equality;
} }
else else
{ {
@@ -57,7 +60,10 @@ namespace Wox.Plugin
public override int GetHashCode() public override int GetHashCode()
{ {
return (Title?.GetHashCode() ?? 0) ^ (SubTitle?.GetHashCode() ?? 0); var hashcode = (Title?.GetHashCode() ?? 0) ^
(SubTitle?.GetHashCode() ?? 0) ^
(Score.GetHashCode());
return hashcode;
} }
public override string ToString() public override string ToString()

View File

@@ -1,6 +1,6 @@
using NUnit.Framework; using NUnit.Framework;
using Wox.Core.Exception;
using Wox.Core.Plugin; using Wox.Core.Plugin;
using Wox.Infrastructure.Exception;
namespace Wox.Test.Plugins namespace Wox.Test.Plugins
{ {
@@ -11,7 +11,7 @@ namespace Wox.Test.Plugins
[Test] [Test]
public void PublicAPIIsNullTest() public void PublicAPIIsNullTest()
{ {
Assert.Throws(typeof(WoxCritialException), () => PluginManager.Init(null)); Assert.Throws(typeof(WoxFatalException), () => PluginManager.Init(null));
} }
} }
} }

View File

@@ -1,34 +1,34 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using Wox.Core.i18n; using Wox.Core.i18n;
using Wox.Core.Plugin; using Wox.Core.Plugin;
using Wox.Core.UserSettings; using Wox.Core.UserSettings;
using Wox.Infrastructure.Exception;
using Wox.Plugin; using Wox.Plugin;
namespace Wox namespace Wox
{ {
public partial class ActionKeywords : Window public partial class ActionKeywords : Window
{ {
private PluginMetadata pluginMetadata; private PluginPair _plugin;
public ActionKeywords(string pluginId) public ActionKeywords(string pluginId)
{ {
InitializeComponent(); InitializeComponent();
PluginPair plugin = PluginManager.GetPluginForId(pluginId); _plugin = PluginManager.GetPluginForId(pluginId);
if (plugin == null) if (_plugin == null)
{ {
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("cannotFindSpecifiedPlugin")); MessageBox.Show(InternationalizationManager.Instance.GetTranslation("cannotFindSpecifiedPlugin"));
Close(); Close();
return; return;
} }
pluginMetadata = plugin.Metadata;
} }
private void ActionKeyword_OnLoaded(object sender, RoutedEventArgs e) private void ActionKeyword_OnLoaded(object sender, RoutedEventArgs e)
{ {
tbOldActionKeyword.Text = string.Join(Query.ActionKeywordSeperater, pluginMetadata.ActionKeywords.ToArray()); tbOldActionKeyword.Text = string.Join(Query.ActionKeywordSeperater, _plugin.Metadata.ActionKeywords.ToArray());
tbAction.Focus(); tbAction.Focus();
} }
@@ -37,42 +37,23 @@ namespace Wox
Close(); Close();
} }
private void btnDone_OnClick(object sender, RoutedEventArgs e) private void btnDone_OnClick(object sender, RoutedEventArgs _)
{ {
if (string.IsNullOrEmpty(tbAction.Text)) var oldActionKeyword = _plugin.Metadata.ActionKeywords[0];
var newActionKeyword = tbAction.Text.Trim();
try
{ {
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("newActionKeywordCannotBeEmpty")); // update in-memory data
PluginManager.UpdateActionKeywordForPlugin(_plugin, oldActionKeyword, newActionKeyword);
}
catch (WoxPluginException e)
{
MessageBox.Show(e.Message);
return; return;
} }
// update persistant data
UserSettingStorage.Instance.UpdateActionKeyword(_plugin.Metadata);
var actionKeywords = tbAction.Text.Trim().Split(new[] { Query.ActionKeywordSeperater }, StringSplitOptions.RemoveEmptyEntries).ToList();
//check new action keyword didn't used by other plugin
if (actionKeywords[0] != Query.GlobalPluginWildcardSign && PluginManager.AllPlugins.
SelectMany(p => p.Metadata.ActionKeywords).
Any(k => actionKeywords.Contains(k)))
{
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("newActionKeywordHasBeenAssigned"));
return;
}
pluginMetadata.ActionKeywords = actionKeywords;
var customizedPluginConfig = UserSettingStorage.Instance.CustomizedPluginConfigs.FirstOrDefault(o => o.ID == pluginMetadata.ID);
if (customizedPluginConfig == null)
{
UserSettingStorage.Instance.CustomizedPluginConfigs.Add(new CustomizedPluginConfig()
{
Disabled = false,
ID = pluginMetadata.ID,
Name = pluginMetadata.Name,
ActionKeywords = actionKeywords
});
}
else
{
customizedPluginConfig.ActionKeywords = actionKeywords;
}
UserSettingStorage.Instance.Save();
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("succeed")); MessageBox.Show(InternationalizationManager.Instance.GetTranslation("succeed"));
Close(); Close();
} }

View File

@@ -1,13 +1,5 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<configuration> <configuration>
<configSections>
<section name="exceptionless" type="Exceptionless.Configuration.ExceptionlessSection, Exceptionless" />
</configSections>
<runtime>
<!--http://stackoverflow.com/questions/186854/how-to-prevent-an-exception-in-a-background-thread-from-terminating-an-application-->
<!--prevent non-ui exception crash wox-->
<legacyUnhandledExceptionPolicy enabled="1" />
</runtime>
<startup> <startup>
<supportedRuntime version="v4.0" /> <supportedRuntime version="v4.0" />
</startup> </startup>

View File

@@ -42,7 +42,7 @@ namespace Wox
} }
[Conditional("DEBUG")] [Conditional("RELEASE")]
private void RegisterUnhandledException() private void RegisterUnhandledException()
{ {
// let exception throw as normal is better for Debug // let exception throw as normal is better for Debug

View File

@@ -1,6 +1,6 @@
using System; using System;
using System.Windows.Threading; using System.Windows.Threading;
using Wox.Core.Exception; using Wox.Infrastructure.Exception;
using Wox.Infrastructure.Logger; using Wox.Infrastructure.Logger;
namespace Wox.Helper namespace Wox.Helper
@@ -9,7 +9,7 @@ namespace Wox.Helper
{ {
public static void Report(Exception e) public static void Report(Exception e)
{ {
Log.Error(ExceptionFormatter.FormatExcpetion(e)); Log.Fatal(e);
new CrashReporter.CrashReporter(e).Show(); new CrashReporter.CrashReporter(e).Show();
} }

View File

@@ -9,6 +9,7 @@ using Wox.Plugin;
namespace Wox.Helper namespace Wox.Helper
{ {
class ListBoxItems : ObservableCollection<Result> class ListBoxItems : ObservableCollection<Result>
// todo implement custom moveItem,removeItem,insertItem for better performance
{ {
public void RemoveAll(Predicate<Result> predicate) public void RemoveAll(Predicate<Result> predicate)
{ {
@@ -17,12 +18,50 @@ namespace Wox.Helper
List<Result> itemsToRemove = Items.Where(x => predicate(x)).ToList(); List<Result> itemsToRemove = Items.Where(x => predicate(x)).ToList();
if (itemsToRemove.Count > 0) if (itemsToRemove.Count > 0)
{ {
itemsToRemove.ForEach(item => Items.Remove(item)); itemsToRemove.ForEach(item => Items.Remove(item));
OnPropertyChanged(new PropertyChangedEventArgs("Count")); OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, itemsToRemove)); // fuck ms
// http://blogs.msdn.com/b/nathannesbit/archive/2009/04/20/addrange-and-observablecollection.aspx
// http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/listcollectionviewcollectionview-doesnt-support-notifycollectionchanged-with-multiple-items.aspx
// PS: don't use Reset for other data updates, it will cause UI flickering
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
} }
} }
public void Update(List<Result> newItems)
{
int newCount = newItems.Count;
int oldCount = Items.Count;
int location = newCount > oldCount ? oldCount : newCount;
for (int i = 0; i < location; i++)
{
Result oldItem = Items[i];
Result newItem = newItems[i];
if (!Equals(oldItem, newItem))
{
this[i] = newItem;
}
}
if (newCount > oldCount)
{
for (int i = oldCount; i < newCount; i++)
{
Add(newItems[i]);
}
}
else
{
int removeIndex = newCount;
for (int i = newCount; i < oldCount; i++)
{
RemoveAt(removeIndex);
}
}
}
} }
} }

View File

@@ -14,9 +14,6 @@
ShowInTaskbar="False" ShowInTaskbar="False"
Style="{DynamicResource WindowStyle}" Style="{DynamicResource WindowStyle}"
Icon="Images\app.png"> Icon="Images\app.png">
<Window.Resources>
<ResourceDictionary Source="/PresentationFramework.Classic,Version=3.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35,processorArchitecture=MSIL;component/themes/Classic.xaml"/>
</Window.Resources>
<Border Style="{DynamicResource WindowBorderStyle}" MouseDown="Border_OnMouseDown"> <Border Style="{DynamicResource WindowBorderStyle}" MouseDown="Border_OnMouseDown">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TextBox Style="{DynamicResource QueryBoxStyle}" PreviewDragOver="TbQuery_OnPreviewDragOver" AllowDrop="True" <TextBox Style="{DynamicResource QueryBoxStyle}" PreviewDragOver="TbQuery_OnPreviewDragOver" AllowDrop="True"

View File

@@ -32,6 +32,7 @@ using IDataObject = System.Windows.IDataObject;
using KeyEventArgs = System.Windows.Input.KeyEventArgs; using KeyEventArgs = System.Windows.Input.KeyEventArgs;
using MenuItem = System.Windows.Forms.MenuItem; using MenuItem = System.Windows.Forms.MenuItem;
using MessageBox = System.Windows.MessageBox; using MessageBox = System.Windows.MessageBox;
using Stopwatch = Wox.Infrastructure.Stopwatch;
using ToolTip = System.Windows.Controls.ToolTip; using ToolTip = System.Windows.Controls.ToolTip;
namespace Wox namespace Wox
@@ -162,7 +163,7 @@ namespace Wox
o.PluginID = plugin.ID; o.PluginID = plugin.ID;
o.OriginQuery = query; o.OriginQuery = query;
}); });
UpdateResultView(results); UpdateResultView(results, plugin, query);
} }
public void ShowContextMenu(PluginMetadata plugin, List<Result> results) public void ShowContextMenu(PluginMetadata plugin, List<Result> results)
@@ -176,7 +177,7 @@ namespace Wox
o.ContextMenu = null; o.ContextMenu = null;
}); });
pnlContextMenu.Clear(); pnlContextMenu.Clear();
pnlContextMenu.AddResults(results); pnlContextMenu.AddResults(results, plugin.ID);
pnlContextMenu.Visibility = Visibility.Visible; pnlContextMenu.Visibility = Visibility.Visible;
pnlResult.Visibility = Visibility.Collapsed; pnlResult.Visibility = Visibility.Collapsed;
} }
@@ -418,11 +419,12 @@ namespace Wox
private void QueryContextMenu() private void QueryContextMenu()
{ {
var contextMenuId = "Context Menu Id";
pnlContextMenu.Clear(); pnlContextMenu.Clear();
var query = tbQuery.Text.ToLower(); var query = tbQuery.Text.ToLower();
if (string.IsNullOrEmpty(query)) if (string.IsNullOrEmpty(query))
{ {
pnlContextMenu.AddResults(CurrentContextMenus); pnlContextMenu.AddResults(CurrentContextMenus, contextMenuId);
} }
else else
{ {
@@ -435,32 +437,25 @@ namespace Wox
filterResults.Add(contextMenu); filterResults.Add(contextMenu);
} }
} }
pnlContextMenu.AddResults(filterResults); pnlContextMenu.AddResults(filterResults, contextMenuId);
} }
} }
private void TbQuery_OnTextChanged(object sender, TextChangedEventArgs e) private void TbQuery_OnTextChanged(object sender, TextChangedEventArgs e)
{ {
if (_ignoreTextChange) { _ignoreTextChange = false; return; } if (_ignoreTextChange) { _ignoreTextChange = false; return; }
toolTip.IsOpen = false;
if (IsInContextMenuMode)
{
QueryContextMenu();
return;
}
string query = tbQuery.Text.Trim(); string query = tbQuery.Text.Trim();
if (!string.IsNullOrEmpty(query)) if (!string.IsNullOrEmpty(query))
{ {
toolTip.IsOpen = false;
if (IsInContextMenuMode)
{
QueryContextMenu();
return;
}
Query(query); Query(query);
Dispatcher.DelayInvoke("ShowProgressbar", () =>
{
if (!string.IsNullOrEmpty(query) && query != _lastQuery.RawQuery && !_queryHasReturn)
{
StartProgress();
}
}, TimeSpan.FromMilliseconds(150));
//reset query history index after user start new query //reset query history index after user start new query
ResetQueryHistoryIndex(); ResetQueryHistoryIndex();
} }
@@ -472,10 +467,12 @@ namespace Wox
private void ResetQueryHistoryIndex() private void ResetQueryHistoryIndex()
{ {
pnlResult.RemoveResultsFor(QueryHistoryStorage.MetaData);
QueryHistoryStorage.Instance.Reset(); QueryHistoryStorage.Instance.Reset();
} }
private void Query(string text) private void Query(string text)
{ {
_queryHasReturn = false;
var query = PluginManager.QueryInit(text); var query = PluginManager.QueryInit(text);
if (query != null) if (query != null)
{ {
@@ -486,21 +483,28 @@ namespace Wox
{ {
if (!string.IsNullOrEmpty(keyword)) if (!string.IsNullOrEmpty(keyword))
{ {
pnlResult.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword]); pnlResult.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata);
} }
} }
else else
{ {
if (string.IsNullOrEmpty(keyword)) if (string.IsNullOrEmpty(keyword))
{ {
pnlResult.RemoveResultsFor(PluginManager.NonGlobalPlugins[lastKeyword]); pnlResult.RemoveResultsFor(PluginManager.NonGlobalPlugins[lastKeyword].Metadata);
} }
else if (lastKeyword != keyword) else if (lastKeyword != keyword)
{ {
pnlResult.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword]); pnlResult.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata);
} }
} }
_lastQuery = query; _lastQuery = query;
Dispatcher.DelayInvoke("ShowProgressbar", () =>
{
if (!string.IsNullOrEmpty(query.RawQuery) && query.RawQuery == _lastQuery.RawQuery && !_queryHasReturn)
{
StartProgress();
}
}, TimeSpan.FromMilliseconds(150));
PluginManager.QueryForAllPlugins(query); PluginManager.QueryForAllPlugins(query);
} }
StopProgress(); StopProgress();
@@ -737,9 +741,11 @@ namespace Wox
{ {
if (history != null) if (history != null)
{ {
var historyMetadata = QueryHistoryStorage.MetaData;
ChangeQueryText(history.Query, true); ChangeQueryText(history.Query, true);
var executeQueryHistoryTitle = GetTranslation("executeQuery"); var executeQueryHistoryTitle = GetTranslation("executeQuery");
var lastExecuteTime = GetTranslation("lastExecuteTime"); var lastExecuteTime = GetTranslation("lastExecuteTime");
pnlResult.RemoveResultsExcept(historyMetadata);
UpdateResultViewInternal(new List<Result>() UpdateResultViewInternal(new List<Result>()
{ {
new Result(){ new Result(){
@@ -752,7 +758,7 @@ namespace Wox
return false; return false;
} }
} }
}); }, historyMetadata);
} }
} }
@@ -833,28 +839,27 @@ namespace Wox
} }
} }
private void UpdateResultView(List<Result> list) private void UpdateResultView(List<Result> list, PluginMetadata metadata, Query originQuery)
{ {
_queryHasReturn = true; _queryHasReturn = true;
progressBar.Dispatcher.Invoke(new Action(StopProgress)); progressBar.Dispatcher.Invoke(new Action(StopProgress));
if (list == null || list.Count == 0) return;
if (list.Count > 0) list.ForEach(o =>
{ {
list.ForEach(o => o.Score += UserSelectedRecordStorage.Instance.GetSelectedCount(o) * 5;
{ });
o.Score += UserSelectedRecordStorage.Instance.GetSelectedCount(o) * 5; if (originQuery.RawQuery == _lastQuery.RawQuery)
}); {
List<Result> l = list.Where(o => o.OriginQuery != null && o.OriginQuery.RawQuery == _lastQuery.RawQuery).ToList(); UpdateResultViewInternal(list, metadata);
UpdateResultViewInternal(l);
} }
} }
private void UpdateResultViewInternal(List<Result> list) private void UpdateResultViewInternal(List<Result> list, PluginMetadata metadata)
{ {
Dispatcher.Invoke(new Action(() => Dispatcher.Invoke(new Action(() =>
{ {
pnlResult.AddResults(list); Stopwatch.Normal($"UI update cost for {metadata.Name}",
() => { pnlResult.AddResults(list, metadata.ID); });
})); }));
} }
@@ -903,7 +908,7 @@ namespace Wox
textBeforeEnterContextMenuMode = tbQuery.Text; textBeforeEnterContextMenuMode = tbQuery.Text;
ChangeQueryText(""); ChangeQueryText("");
pnlContextMenu.Clear(); pnlContextMenu.Clear();
pnlContextMenu.AddResults(results); pnlContextMenu.AddResults(results, result.PluginID);
CurrentContextMenus = results; CurrentContextMenus = results;
pnlContextMenu.Visibility = Visibility.Visible; pnlContextMenu.Visibility = Visibility.Visible;
pnlResult.Visibility = Visibility.Collapsed; pnlResult.Visibility = Visibility.Collapsed;

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
@@ -21,7 +22,7 @@ namespace Wox
public event Action<Result> LeftMouseClickEvent; public event Action<Result> LeftMouseClickEvent;
public event Action<Result> RightMouseClickEvent; public event Action<Result> RightMouseClickEvent;
public event Action<Result, IDataObject, DragEventArgs> ItemDropEvent; public event Action<Result, IDataObject, DragEventArgs> ItemDropEvent;
private readonly ListBoxItems _results; //todo, for better performance, override the default linear search private readonly ListBoxItems _results;
private readonly object _resultsUpdateLock = new object(); private readonly object _resultsUpdateLock = new object();
protected virtual void OnRightMouseClick(Result result) protected virtual void OnRightMouseClick(Result result)
@@ -39,75 +40,74 @@ namespace Wox
public int MaxResultsToShow { get { return UserSettingStorage.Instance.MaxResultsToShow * 50; } } public int MaxResultsToShow { get { return UserSettingStorage.Instance.MaxResultsToShow * 50; } }
internal void RemoveResultsFor(PluginPair plugin) internal void RemoveResultsFor(PluginMetadata metadata)
{ {
lock (_resultsUpdateLock) lock (_resultsUpdateLock)
{ {
_results.RemoveAll(r => r.PluginID == plugin.Metadata.ID); _results.RemoveAll(r => r.PluginID == metadata.ID);
} }
} }
internal void RemoveResultsExcept(PluginPair plugin) internal void RemoveResultsExcept(PluginMetadata metadata)
{ {
lock (_resultsUpdateLock) lock (_resultsUpdateLock)
{ {
_results.RemoveAll(r => r.PluginID != plugin.Metadata.ID); _results.RemoveAll(r => r.PluginID != metadata.ID);
} }
} }
public void AddResults(List<Result> newResults, string resultId)
public void AddResults(List<Result> newResults)
{ {
if (newResults != null && newResults.Count > 0) lock (_resultsUpdateLock)
{ {
lock (_resultsUpdateLock) var resultCopy = _results.ToList();
var oldResults = resultCopy.Where(r => r.PluginID == resultId).ToList();
// intersection of A (old results) and B (new newResults)
var intersection = oldResults.Intersect(newResults).ToList();
// remove result of relative complement of B in A
foreach (var result in oldResults.Except(intersection))
{ {
var pluginId = newResults[0].PluginID; resultCopy.Remove(result);
var oldResults = _results.Where(r => r.PluginID == pluginId).ToList();
// intersection of A (old results) and B (new newResults)
var intersection = oldResults.Intersect(newResults).ToList();
// remove result of relative complement of B in A
foreach (var result in oldResults.Except(intersection))
{
_results.Remove(result);
}
// update scores
foreach (var result in newResults)
{
if (IsTopMostResult(result))
{
result.Score = int.MaxValue;
}
}
// update index for result in intersection of A and B
foreach (var result in intersection)
{
int oldIndex = _results.IndexOf(result);
int oldScore = _results[oldIndex].Score;
if (result.Score != oldScore)
{
int newIndex = InsertIndexOf(result.Score);
if (newIndex != oldIndex)
{
_results.Move(oldIndex, newIndex);
}
}
}
// insert result in relative complement of A in B
foreach (var result in newResults.Except(intersection))
{
int newIndex = InsertIndexOf(result.Score);
_results.Insert(newIndex, result);
}
lbResults.Margin = lbResults.Items.Count > 0 ? new Thickness { Top = 8 } : new Thickness { Top = 0 };
SelectFirst();
} }
// update scores
foreach (var result in newResults)
{
if (IsTopMostResult(result))
{
result.Score = int.MaxValue;
}
}
// update index for result in intersection of A and B
foreach (var result in intersection)
{
int oldIndex = resultCopy.IndexOf(result);
int oldScore = resultCopy[oldIndex].Score;
if (result.Score != oldScore)
{
int newIndex = InsertIndexOf(result.Score, resultCopy);
if (newIndex != oldIndex)
{
var item = resultCopy[oldIndex];
resultCopy.RemoveAt(oldIndex);
resultCopy.Insert(newIndex, item);
}
}
}
// insert result in relative complement of A in B
foreach (var result in newResults.Except(intersection))
{
int newIndex = InsertIndexOf(result.Score, resultCopy);
resultCopy.Insert(newIndex, result);
}
// update UI in one run, so it can avoid UI flickering
_results.Update(resultCopy);
lbResults.Margin = lbResults.Items.Count > 0 ? new Thickness { Top = 8 } : new Thickness { Top = 0 };
SelectFirst();
} }
} }
@@ -116,13 +116,13 @@ namespace Wox
return TopMostRecordStorage.Instance.IsTopMost(result); return TopMostRecordStorage.Instance.IsTopMost(result);
} }
private int InsertIndexOf(int newScore) private int InsertIndexOf(int newScore, IList<Result> list)
{ {
int index = 0; int index = 0;
for (; index < lbResults.Items.Count; index++) for (; index < list.Count; index++)
{ {
Result result = lbResults.Items[index] as Result; var result = list[index];
if (newScore > result?.Score) if (newScore > result.Score)
{ {
break; break;
} }

View File

@@ -106,9 +106,7 @@
<CheckBox x:Name="cbDisablePlugin" Click="CbDisablePlugin_OnClick"> <CheckBox x:Name="cbDisablePlugin" Click="CbDisablePlugin_OnClick">
<TextBlock Text="{DynamicResource disable}"></TextBlock> <TextBlock Text="{DynamicResource disable}"></TextBlock>
</CheckBox> </CheckBox>
<TextBlock x:Name="pluginActionKeywordsTitle" Margin="20 0 0 0"> <TextBlock x:Name="pluginActionKeywordsTitle" Margin="20 0 0 0" Text="{DynamicResource actionKeywords}" />
<TextBlock Text="{DynamicResource actionKeywords}"></TextBlock>
</TextBlock>
<TextBlock Margin="5 0 0 0" ToolTip="Change Action Keywords" Cursor="Hand" MouseUp="PluginActionKeywords_OnMouseUp" Foreground="Blue" Text="keys" x:Name="pluginActionKeywords"></TextBlock> <TextBlock Margin="5 0 0 0" ToolTip="Change Action Keywords" Cursor="Hand" MouseUp="PluginActionKeywords_OnMouseUp" Foreground="Blue" Text="keys" x:Name="pluginActionKeywords"></TextBlock>
<TextBlock Margin="10 0 0 0" Text="Init time: 0ms" x:Name="pluginInitTime"></TextBlock> <TextBlock Margin="10 0 0 0" Text="Init time: 0ms" x:Name="pluginInitTime"></TextBlock>
<TextBlock Margin="10 0 0 0" Text="Query time: 0ms" x:Name="pluginQueryTime"></TextBlock> <TextBlock Margin="10 0 0 0" Text="Query time: 0ms" x:Name="pluginQueryTime"></TextBlock>
@@ -144,9 +142,6 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<StackPanel x:Name="PreviewPanel" Grid.Row="0" Margin="0"> <StackPanel x:Name="PreviewPanel" Grid.Row="0" Margin="0">
<StackPanel x:Name="PreviewMainPanel" Orientation="Horizontal" Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel x:Name="PreviewMainPanel" Orientation="Horizontal" Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel.Resources>
<ResourceDictionary Source="/PresentationFramework.Classic,Version=3.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35,processorArchitecture=MSIL;component/themes/Classic.xaml"/>
</StackPanel.Resources>
<Border Width="500" Style="{DynamicResource WindowBorderStyle}"> <Border Width="500" Style="{DynamicResource WindowBorderStyle}">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
@@ -231,7 +226,7 @@
</DataTemplate> </DataTemplate>
</GridViewColumn.CellTemplate> </GridViewColumn.CellTemplate>
</GridViewColumn> </GridViewColumn>
<GridViewColumn Header="{DynamicResource actionKeyword}" Width="500"> <GridViewColumn Header="{DynamicResource actionKeywords}" Width="500">
<GridViewColumn.CellTemplate> <GridViewColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<TextBlock Text="{Binding ActionKeywords}"/> <TextBlock Text="{Binding ActionKeywords}"/>

View File

@@ -17,7 +17,7 @@ using Wox.Core.Theme;
using Wox.Core.Updater; using Wox.Core.Updater;
using Wox.Core.UserSettings; using Wox.Core.UserSettings;
using Wox.Helper; using Wox.Helper;
using Wox.Infrastructure; using Wox.Infrastructure.Exception;
using Wox.Plugin; using Wox.Plugin;
using Application = System.Windows.Forms.Application; using Application = System.Windows.Forms.Application;
using Stopwatch = Wox.Infrastructure.Stopwatch; using Stopwatch = Wox.Infrastructure.Stopwatch;
@@ -188,23 +188,6 @@ namespace Wox
{ {
OnHotkeyTabSelected(); OnHotkeyTabSelected();
} }
// save multiple action keywords settings, todo: this hack is ugly
var tab = e.RemovedItems.Count > 0 ? e.RemovedItems[0] : null;
if (ReferenceEquals(tab, tabPlugin))
{
var metadata = (lbPlugins.SelectedItem as PluginPair)?.Metadata;
if (metadata != null)
{
var customizedPluginConfig = UserSettingStorage.Instance.CustomizedPluginConfigs.FirstOrDefault(o => o.ID == metadata.ID);
if (customizedPluginConfig != null && !customizedPluginConfig.Disabled)
{
customizedPluginConfig.ActionKeywords = metadata.ActionKeywords;
UserSettingStorage.Instance.Save();
}
}
}
} }
#region General #region General
@@ -429,7 +412,7 @@ namespace Wox
IcoPath = "Images/app.png", IcoPath = "Images/app.png",
PluginDirectory = Path.GetDirectoryName(Application.ExecutablePath) PluginDirectory = Path.GetDirectoryName(Application.ExecutablePath)
} }
}); }, "test id");
foreach (string theme in ThemeManager.Theme.LoadAvailableThemes()) foreach (string theme in ThemeManager.Theme.LoadAvailableThemes())
{ {
@@ -535,50 +518,64 @@ namespace Wox
#region Plugin #region Plugin
private void lbPlugins_OnSelectionChanged(object sender, SelectionChangedEventArgs e) private void lbPlugins_OnSelectionChanged(object sender, SelectionChangedEventArgs _)
{ {
ISettingProvider provider = null;
var pair = lbPlugins.SelectedItem as PluginPair; var pair = lbPlugins.SelectedItem as PluginPair;
string pluginId = string.Empty; string pluginId = string.Empty;
List<string> actionKeywords = null;
if (pair != null) if (pair == null) return;
actionKeywords = pair.Metadata.ActionKeywords;
pluginAuthor.Visibility = Visibility.Visible;
pluginInitTime.Text =
string.Format(InternationalizationManager.Instance.GetTranslation("plugin_init_time"), pair.InitTime);
pluginQueryTime.Text =
string.Format(InternationalizationManager.Instance.GetTranslation("plugin_query_time"), pair.AvgQueryTime);
if (actionKeywords.Count > 1)
{ {
provider = pair.Plugin as ISettingProvider; pluginActionKeywordsTitle.Visibility = Visibility.Collapsed;
pluginAuthor.Visibility = Visibility.Visible; pluginActionKeywords.Visibility = Visibility.Collapsed;
pluginInitTime.Text =
string.Format(InternationalizationManager.Instance.GetTranslation("plugin_init_time"), pair.InitTime);
pluginQueryTime.Text =
string.Format(InternationalizationManager.Instance.GetTranslation("plugin_query_time"), pair.AvgQueryTime);
if (pair.Metadata.ActionKeywords.Count > 1)
{
pluginActionKeywordsTitle.Visibility = Visibility.Collapsed;
pluginActionKeywords.Visibility = Visibility.Collapsed;
}
else
{
pluginActionKeywordsTitle.Visibility = Visibility.Visible;
pluginActionKeywords.Visibility = Visibility.Visible;
}
tbOpenPluginDirecoty.Visibility = Visibility.Visible;
pluginTitle.Text = pair.Metadata.Name;
pluginTitle.Cursor = Cursors.Hand;
pluginActionKeywords.Text = string.Join(Query.ActionKeywordSeperater, pair.Metadata.ActionKeywords.ToArray());
pluginAuthor.Text = InternationalizationManager.Instance.GetTranslation("author") + ": " + pair.Metadata.Author;
pluginSubTitle.Text = pair.Metadata.Description;
pluginId = pair.Metadata.ID;
pluginIcon.Source = ImageLoader.ImageLoader.Load(pair.Metadata.FullIcoPath);
} }
else
{
pluginActionKeywordsTitle.Visibility = Visibility.Visible;
pluginActionKeywords.Visibility = Visibility.Visible;
}
tbOpenPluginDirecoty.Visibility = Visibility.Visible;
pluginTitle.Text = pair.Metadata.Name;
pluginTitle.Cursor = Cursors.Hand;
pluginActionKeywords.Text = string.Join(Query.ActionKeywordSeperater, actionKeywords.ToArray());
pluginAuthor.Text = InternationalizationManager.Instance.GetTranslation("author") + ": " + pair.Metadata.Author;
pluginSubTitle.Text = pair.Metadata.Description;
pluginId = pair.Metadata.ID;
pluginIcon.Source = ImageLoader.ImageLoader.Load(pair.Metadata.FullIcoPath);
var customizedPluginConfig = UserSettingStorage.Instance.CustomizedPluginConfigs.FirstOrDefault(o => o.ID == pluginId); var customizedPluginConfig = UserSettingStorage.Instance.CustomizedPluginConfigs.FirstOrDefault(o => o.ID == pluginId);
cbDisablePlugin.IsChecked = customizedPluginConfig != null && customizedPluginConfig.Disabled; cbDisablePlugin.IsChecked = customizedPluginConfig != null && customizedPluginConfig.Disabled;
PluginContentPanel.Content = null; PluginContentPanel.Content = null;
if (provider != null) var settingProvider = pair.Plugin as ISettingProvider;
if (settingProvider != null)
{ {
Control control = null; Control control;
if (!featureControls.TryGetValue(provider, out control)) if (!featureControls.TryGetValue(settingProvider, out control))
featureControls.Add(provider, control = provider.CreateSettingPanel()); {
var multipleActionKeywordsProvider = settingProvider as IMultipleActionKeywords;
if (multipleActionKeywordsProvider != null)
{
multipleActionKeywordsProvider.ActionKeywordsChanged += (o, e) =>
{
// update in-memory data
PluginManager.UpdateActionKeywordForPlugin(pair, e.OldActionKeyword, e.NewActionKeyword);
// update persistant data
UserSettingStorage.Instance.UpdateActionKeyword(pair.Metadata);
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("succeed"));
};
}
featureControls.Add(settingProvider, control = settingProvider.CreateSettingPanel());
}
PluginContentPanel.Content = control; PluginContentPanel.Content = control;
control.HorizontalAlignment = HorizontalAlignment.Stretch; control.HorizontalAlignment = HorizontalAlignment.Stretch;
control.VerticalAlignment = VerticalAlignment.Stretch; control.VerticalAlignment = VerticalAlignment.Stretch;

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using Newtonsoft.Json; using Newtonsoft.Json;
using Wox.Infrastructure.Storage; using Wox.Infrastructure.Storage;
using Wox.Plugin;
namespace Wox.Storage namespace Wox.Storage
{ {
@@ -16,6 +17,9 @@ namespace Wox.Storage
private int MaxHistory = 300; private int MaxHistory = 300;
private int cursor = 0; private int cursor = 0;
public static PluginMetadata MetaData { get; } = new PluginMetadata
{ ID = "Query history", Name = "Query history" };
protected override string ConfigFolder protected override string ConfigFolder
{ {
get { return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Config"); } get { return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Config"); }
@@ -77,7 +81,7 @@ namespace Wox.Storage
return History.OrderByDescending(o => o.ExecutedDateTime).ToList(); return History.OrderByDescending(o => o.ExecutedDateTime).ToList();
} }
} }
public class HistoryItem public class HistoryItem
{ {
public string Query { get; set; } public string Query { get; set; }

View File

@@ -48,7 +48,7 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>..\Output\Release\</OutputPath> <OutputPath>..\Output\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE;RELEASE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
@@ -295,7 +295,9 @@
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
<None Include="App.config" /> <None Include="App.config">
<SubType>Designer</SubType>
</None>
<None Include="Properties\Settings.settings"> <None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator> <Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput> <LastGenOutput>Settings.Designer.cs</LastGenOutput>
@@ -320,9 +322,6 @@
<Name>Wox.Plugin</Name> <Name>Wox.Plugin</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<WCFMetadata Include="Service References\" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" /> <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup> </ItemGroup>
@@ -379,7 +378,6 @@ cd "$(TargetDir)Plugins" &amp; del /s /q NAppUpdate.Framework.dll
cd "$(TargetDir)Plugins" &amp; del /s /q Wox.Infrastructure.dll cd "$(TargetDir)Plugins" &amp; del /s /q Wox.Infrastructure.dll
cd "$(TargetDir)Plugins" &amp; del /s /q Wox.Infrastructure.pdb cd "$(TargetDir)Plugins" &amp; del /s /q Wox.Infrastructure.pdb
cd "$(TargetDir)Plugins" &amp; del /s /q Newtonsoft.Json.dll cd "$(TargetDir)Plugins" &amp; del /s /q Newtonsoft.Json.dll
cd "$(TargetDir)Plugins" &amp; del /s /q WindowsInput.dll
)</PostBuildEvent> )</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.