Merge "dev" into "improve-calculator"

This commit is contained in:
SysC0mp 2020-02-26 19:45:37 +01:00
commit ec2aaa8457
98 changed files with 2216 additions and 1216 deletions

View file

@ -37,32 +37,13 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath>
</Reference>
<Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Data.SQLite, Version=1.0.111.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Data.SQLite.Core.1.0.111.0\lib\net451\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.Data.SQLite.EF6, Version=1.0.111.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Data.SQLite.EF6.1.0.111.0\lib\net451\System.Data.SQLite.EF6.dll</HintPath>
</Reference>
<Reference Include="System.Data.SQLite.Linq, Version=1.0.111.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<HintPath>..\..\packages\System.Data.SQLite.Linq.1.0.111.0\lib\net451\System.Data.SQLite.Linq.dll</HintPath>
</Reference>
<Reference Include="System.Xaml" /> <Reference Include="System.Xaml" />
<Reference Include="UnidecodeSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\UnidecodeSharp.1.0.0.0\lib\net35\UnidecodeSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -79,7 +60,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="app.config" /> <None Include="app.config" />
<None Include="packages.config" />
<None Include="plugin.json"> <None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -123,16 +103,19 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Page> </Page>
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup>
<PackageReference Include="System.Data.SQLite">
<Version>1.0.111</Version>
</PackageReference>
<PackageReference Include="System.Data.SQLite.Core">
<Version>1.0.111</Version>
</PackageReference>
<PackageReference Include="UnidecodeSharp">
<Version>1.0.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" /> <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Import Project="..\..\packages\System.Data.SQLite.Core.1.0.111.0\build\net451\System.Data.SQLite.Core.targets" Condition="Exists('..\..\packages\System.Data.SQLite.Core.1.0.111.0\build\net451\System.Data.SQLite.Core.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\System.Data.SQLite.Core.1.0.111.0\build\net451\System.Data.SQLite.Core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\System.Data.SQLite.Core.1.0.111.0\build\net451\System.Data.SQLite.Core.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<configSections> <configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections> </configSections>
<startup> <startup>
@ -18,10 +18,5 @@
<provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" /> <provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
</providers> </providers>
</entityFramework> </entityFramework>
<system.data>
<DbProviderFactories>
<remove invariant="System.Data.SQLite.EF6" />
<add name="SQLite Data Provider (Entity Framework 6)" invariant="System.Data.SQLite.EF6" description=".NET Framework Data Provider for SQLite (Entity Framework 6)" type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6" />
<remove invariant="System.Data.SQLite" /><add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".NET Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" /></DbProviderFactories>
</system.data>
</configuration> </configuration>

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="EntityFramework" version="6.2.0" targetFramework="net452" />
<package id="System.Data.SQLite" version="1.0.111.0" targetFramework="net452" />
<package id="System.Data.SQLite.Core" version="1.0.111.0" targetFramework="net452" />
<package id="System.Data.SQLite.EF6" version="1.0.111.0" targetFramework="net452" />
<package id="System.Data.SQLite.Linq" version="1.0.111.0" targetFramework="net452" />
<package id="UnidecodeSharp" version="1.0.0.0" targetFramework="net452" />
</packages>

View file

@ -64,7 +64,6 @@
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="plugin.json"> <None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -130,6 +129,17 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="Mages">
<Version>1.5.0</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Page Include="Views\CalculatorSettings.xaml"> <Page Include="Views\CalculatorSettings.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="Mages" version="1.5.0" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -55,7 +55,6 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="plugin.json"> <None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -112,6 +111,14 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</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,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -27,7 +27,6 @@ namespace Wox.Plugin.ControlPanel
Directory.CreateDirectory(iconFolder); Directory.CreateDirectory(iconFolder);
} }
foreach (ControlPanelItem item in controlPanelItems) foreach (ControlPanelItem item in controlPanelItems)
{ {
if (!File.Exists(iconFolder + item.GUID + fileType) && item.Icon != null) if (!File.Exists(iconFolder + item.GUID + fileType) && item.Icon != null)
@ -43,7 +42,10 @@ namespace Wox.Plugin.ControlPanel
foreach (var item in controlPanelItems) foreach (var item in controlPanelItems)
{ {
item.Score = Score(item, query.Search); var titleMatch = StringMatcher.FuzzySearch(query.Search, item.LocalizedString);
var subTitleMatch = StringMatcher.FuzzySearch(query.Search, item.InfoTip);
item.Score = Math.Max(titleMatch.Score, subTitleMatch.Score);
if (item.Score > 0) if (item.Score > 0)
{ {
var result = new Result var result = new Result
@ -66,6 +68,16 @@ namespace Wox.Plugin.ControlPanel
return true; return true;
} }
}; };
if (item.Score == titleMatch.Score)
{
result.TitleHighlightData = titleMatch.MatchData;
}
else
{
result.SubTitleHighlightData = subTitleMatch.MatchData;
}
results.Add(result); results.Add(result);
} }
} }
@ -74,26 +86,6 @@ namespace Wox.Plugin.ControlPanel
return panelItems; return panelItems;
} }
private int Score(ControlPanelItem item, string query)
{
var scores = new List<int> {0};
if (!string.IsNullOrEmpty(item.LocalizedString))
{
var score1 = StringMatcher.FuzzySearch(query, item.LocalizedString).ScoreAfterSearchPrecisionFilter();
var score2 = StringMatcher.ScoreForPinyin(item.LocalizedString, query);
scores.Add(score1);
scores.Add(score2);
}
if (!string.IsNullOrEmpty(item.InfoTip))
{
var score1 = StringMatcher.FuzzySearch(query, item.InfoTip).ScoreAfterSearchPrecisionFilter();
var score2 = StringMatcher.ScoreForPinyin(item.InfoTip, query);
scores.Add(score1);
scores.Add(score2);
}
return scores.Max();
}
public string GetTranslatedPluginTitle() public string GetTranslatedPluginTitle()
{ {
return context.API.GetTranslation("wox_plugin_controlpanel_plugin_name"); return context.API.GetTranslation("wox_plugin_controlpanel_plugin_name");

View file

@ -52,7 +52,6 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="plugin.json"> <None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -114,6 +113,14 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</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,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -2,76 +2,36 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading;
using Wox.Infrastructure.Logger;
using Wox.Plugin.Everything.Everything.Exceptions; using Wox.Plugin.Everything.Everything.Exceptions;
namespace Wox.Plugin.Everything.Everything namespace Wox.Plugin.Everything.Everything
{ {
public sealed class EverythingAPI public interface IEverythingApi
{ {
#region DllImport /// <summary>
[DllImport(Main.DLL, CharSet = CharSet.Unicode)] /// Searches the specified key word.
private static extern int Everything_SetSearchW(string lpSearchString); /// </summary>
[DllImport(Main.DLL)] /// <param name="keyWord">The key word.</param>
private static extern void Everything_SetMatchPath(bool bEnable); /// <param name="token">token that allow cancellation</param>
[DllImport(Main.DLL)] /// <param name="offset">The offset.</param>
private static extern void Everything_SetMatchCase(bool bEnable); /// <param name="maxCount">The max count.</param>
[DllImport(Main.DLL)] /// <returns></returns>
private static extern void Everything_SetMatchWholeWord(bool bEnable); List<SearchResult> Search(string keyWord, CancellationToken token, int offset = 0, int maxCount = 100);
[DllImport(Main.DLL)]
private static extern void Everything_SetRegex(bool bEnable);
[DllImport(Main.DLL)]
private static extern void Everything_SetMax(int dwMax);
[DllImport(Main.DLL)]
private static extern void Everything_SetOffset(int dwOffset);
[DllImport(Main.DLL)] void Load(string sdkPath);
private static extern bool Everything_GetMatchPath(); }
[DllImport(Main.DLL)]
private static extern bool Everything_GetMatchCase();
[DllImport(Main.DLL)]
private static extern bool Everything_GetMatchWholeWord();
[DllImport(Main.DLL)]
private static extern bool Everything_GetRegex();
[DllImport(Main.DLL)]
private static extern UInt32 Everything_GetMax();
[DllImport(Main.DLL)]
private static extern UInt32 Everything_GetOffset();
[DllImport(Main.DLL, CharSet = CharSet.Unicode)]
private static extern string Everything_GetSearchW();
[DllImport(Main.DLL)]
private static extern StateCode Everything_GetLastError();
[DllImport(Main.DLL, CharSet = CharSet.Unicode)] public sealed class EverythingApi : IEverythingApi
private static extern bool Everything_QueryW(bool bWait); {
private const int BufferSize = 4096;
[DllImport(Main.DLL)] private readonly object _syncObject = new object();
private static extern void Everything_SortResultsByPath(); // cached buffer to remove redundant allocations.
private readonly StringBuilder _buffer = new StringBuilder(BufferSize);
[DllImport(Main.DLL)] public enum StateCode
private static extern int Everything_GetNumFileResults();
[DllImport(Main.DLL)]
private static extern int Everything_GetNumFolderResults();
[DllImport(Main.DLL)]
private static extern int Everything_GetNumResults();
[DllImport(Main.DLL)]
private static extern int Everything_GetTotFileResults();
[DllImport(Main.DLL)]
private static extern int Everything_GetTotFolderResults();
[DllImport(Main.DLL)]
private static extern int Everything_GetTotResults();
[DllImport(Main.DLL)]
private static extern bool Everything_IsVolumeResult(int nIndex);
[DllImport(Main.DLL)]
private static extern bool Everything_IsFolderResult(int nIndex);
[DllImport(Main.DLL)]
private static extern bool Everything_IsFileResult(int nIndex);
[DllImport(Main.DLL, CharSet = CharSet.Unicode)]
private static extern void Everything_GetResultFullPathNameW(int nIndex, StringBuilder lpString, int nMaxCount);
[DllImport(Main.DLL)]
private static extern void Everything_Reset();
#endregion
enum StateCode
{ {
OK, OK,
MemoryError, MemoryError,
@ -87,15 +47,15 @@ namespace Wox.Plugin.Everything.Everything
/// Gets or sets a value indicating whether [match path]. /// Gets or sets a value indicating whether [match path].
/// </summary> /// </summary>
/// <value><c>true</c> if [match path]; otherwise, <c>false</c>.</value> /// <value><c>true</c> if [match path]; otherwise, <c>false</c>.</value>
public Boolean MatchPath public bool MatchPath
{ {
get get
{ {
return Everything_GetMatchPath(); return EverythingApiDllImport.Everything_GetMatchPath();
} }
set set
{ {
Everything_SetMatchPath(value); EverythingApiDllImport.Everything_SetMatchPath(value);
} }
} }
@ -103,15 +63,15 @@ namespace Wox.Plugin.Everything.Everything
/// Gets or sets a value indicating whether [match case]. /// Gets or sets a value indicating whether [match case].
/// </summary> /// </summary>
/// <value><c>true</c> if [match case]; otherwise, <c>false</c>.</value> /// <value><c>true</c> if [match case]; otherwise, <c>false</c>.</value>
public Boolean MatchCase public bool MatchCase
{ {
get get
{ {
return Everything_GetMatchCase(); return EverythingApiDllImport.Everything_GetMatchCase();
} }
set set
{ {
Everything_SetMatchCase(value); EverythingApiDllImport.Everything_SetMatchCase(value);
} }
} }
@ -119,15 +79,15 @@ namespace Wox.Plugin.Everything.Everything
/// Gets or sets a value indicating whether [match whole word]. /// Gets or sets a value indicating whether [match whole word].
/// </summary> /// </summary>
/// <value><c>true</c> if [match whole word]; otherwise, <c>false</c>.</value> /// <value><c>true</c> if [match whole word]; otherwise, <c>false</c>.</value>
public Boolean MatchWholeWord public bool MatchWholeWord
{ {
get get
{ {
return Everything_GetMatchWholeWord(); return EverythingApiDllImport.Everything_GetMatchWholeWord();
} }
set set
{ {
Everything_SetMatchWholeWord(value); EverythingApiDllImport.Everything_SetMatchWholeWord(value);
} }
} }
@ -135,15 +95,15 @@ namespace Wox.Plugin.Everything.Everything
/// Gets or sets a value indicating whether [enable regex]. /// Gets or sets a value indicating whether [enable regex].
/// </summary> /// </summary>
/// <value><c>true</c> if [enable regex]; otherwise, <c>false</c>.</value> /// <value><c>true</c> if [enable regex]; otherwise, <c>false</c>.</value>
public Boolean EnableRegex public bool EnableRegex
{ {
get get
{ {
return Everything_GetRegex(); return EverythingApiDllImport.Everything_GetRegex();
} }
set set
{ {
Everything_SetRegex(value); EverythingApiDllImport.Everything_SetRegex(value);
} }
} }
@ -152,12 +112,91 @@ namespace Wox.Plugin.Everything.Everything
/// </summary> /// </summary>
public void Reset() public void Reset()
{ {
Everything_Reset(); lock (_syncObject)
{
EverythingApiDllImport.Everything_Reset();
}
} }
private void no() /// <summary>
/// Searches the specified key word and reset the everything API afterwards
/// </summary>
/// <param name="keyWord">The key word.</param>
/// <param name="token">when cancelled the current search will stop and exit (and would not reset)</param>
/// <param name="offset">The offset.</param>
/// <param name="maxCount">The max count.</param>
/// <returns></returns>
public List<SearchResult> Search(string keyWord, CancellationToken token, int offset = 0, int maxCount = 100)
{ {
switch (Everything_GetLastError()) if (string.IsNullOrEmpty(keyWord))
throw new ArgumentNullException(nameof(keyWord));
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset));
if (maxCount < 0)
throw new ArgumentOutOfRangeException(nameof(maxCount));
lock (_syncObject)
{
if (keyWord.StartsWith("@"))
{
EverythingApiDllImport.Everything_SetRegex(true);
keyWord = keyWord.Substring(1);
}
EverythingApiDllImport.Everything_SetSearchW(keyWord);
EverythingApiDllImport.Everything_SetOffset(offset);
EverythingApiDllImport.Everything_SetMax(maxCount);
if (token.IsCancellationRequested)
{
return null;
}
if (!EverythingApiDllImport.Everything_QueryW(true))
{
CheckAndThrowExceptionOnError();
return null;
}
var results = new List<SearchResult>();
for (int idx = 0; idx < EverythingApiDllImport.Everything_GetNumResults(); ++idx)
{
if (token.IsCancellationRequested)
{
return null;
}
EverythingApiDllImport.Everything_GetResultFullPathNameW(idx, _buffer, BufferSize);
var result = new SearchResult { FullPath = _buffer.ToString() };
if (EverythingApiDllImport.Everything_IsFolderResult(idx))
result.Type = ResultType.Folder;
else if (EverythingApiDllImport.Everything_IsFileResult(idx))
result.Type = ResultType.File;
results.Add(result);
}
Reset();
return results;
}
}
[DllImport("kernel32.dll")]
private static extern int LoadLibrary(string name);
public void Load(string sdkPath)
{
LoadLibrary(sdkPath);
}
private static void CheckAndThrowExceptionOnError()
{
switch (EverythingApiDllImport.Everything_GetLastError())
{ {
case StateCode.CreateThreadError: case StateCode.CreateThreadError:
throw new CreateThreadException(); throw new CreateThreadException();
@ -175,71 +214,5 @@ namespace Wox.Plugin.Everything.Everything
throw new RegisterClassExException(); throw new RegisterClassExException();
} }
} }
/// <summary>
/// Searches the specified key word.
/// </summary>
/// <param name="keyWord">The key word.</param>
/// <param name="offset">The offset.</param>
/// <param name="maxCount">The max count.</param>
/// <returns></returns>
public IEnumerable<SearchResult> Search(string keyWord, int offset = 0, int maxCount = 100)
{
if (string.IsNullOrEmpty(keyWord))
throw new ArgumentNullException("keyWord");
if (offset < 0)
throw new ArgumentOutOfRangeException("offset");
if (maxCount < 0)
throw new ArgumentOutOfRangeException("maxCount");
if (keyWord.StartsWith("@"))
{
Everything_SetRegex(true);
keyWord = keyWord.Substring(1);
}
Everything_SetSearchW(keyWord);
Everything_SetOffset(offset);
Everything_SetMax(maxCount);
if (!Everything_QueryW(true))
{
switch (Everything_GetLastError())
{
case StateCode.CreateThreadError:
throw new CreateThreadException();
case StateCode.CreateWindowError:
throw new CreateWindowException();
case StateCode.InvalidCallError:
throw new InvalidCallException();
case StateCode.InvalidIndexError:
throw new InvalidIndexException();
case StateCode.IPCError:
throw new IPCErrorException();
case StateCode.MemoryError:
throw new MemoryErrorException();
case StateCode.RegisterClassExError:
throw new RegisterClassExException();
}
yield break;
}
const int bufferSize = 4096;
StringBuilder buffer = new StringBuilder(bufferSize);
for (int idx = 0; idx < Everything_GetNumResults(); ++idx)
{
Everything_GetResultFullPathNameW(idx, buffer, bufferSize);
var result = new SearchResult { FullPath = buffer.ToString() };
if (Everything_IsFolderResult(idx))
result.Type = ResultType.Folder;
else if (Everything_IsFileResult(idx))
result.Type = ResultType.File;
yield return result;
}
}
} }
} }

View file

@ -0,0 +1,92 @@
using System.Runtime.InteropServices;
using System.Text;
namespace Wox.Plugin.Everything.Everything
{
public sealed class EverythingApiDllImport
{
[DllImport(Main.DLL, CharSet = CharSet.Unicode)]
internal static extern int Everything_SetSearchW(string lpSearchString);
[DllImport(Main.DLL)]
internal static extern void Everything_SetMatchPath(bool bEnable);
[DllImport(Main.DLL)]
internal static extern void Everything_SetMatchCase(bool bEnable);
[DllImport(Main.DLL)]
internal static extern void Everything_SetMatchWholeWord(bool bEnable);
[DllImport(Main.DLL)]
internal static extern void Everything_SetRegex(bool bEnable);
[DllImport(Main.DLL)]
internal static extern void Everything_SetMax(int dwMax);
[DllImport(Main.DLL)]
internal static extern void Everything_SetOffset(int dwOffset);
[DllImport(Main.DLL)]
internal static extern bool Everything_GetMatchPath();
[DllImport(Main.DLL)]
internal static extern bool Everything_GetMatchCase();
[DllImport(Main.DLL)]
internal static extern bool Everything_GetMatchWholeWord();
[DllImport(Main.DLL)]
internal static extern bool Everything_GetRegex();
[DllImport(Main.DLL)]
internal static extern uint Everything_GetMax();
[DllImport(Main.DLL)]
internal static extern uint Everything_GetOffset();
[DllImport(Main.DLL, CharSet = CharSet.Unicode)]
internal static extern string Everything_GetSearchW();
[DllImport(Main.DLL)]
internal static extern EverythingApi.StateCode Everything_GetLastError();
[DllImport(Main.DLL, CharSet = CharSet.Unicode)]
internal static extern bool Everything_QueryW(bool bWait);
[DllImport(Main.DLL)]
internal static extern void Everything_SortResultsByPath();
[DllImport(Main.DLL)]
internal static extern int Everything_GetNumFileResults();
[DllImport(Main.DLL)]
internal static extern int Everything_GetNumFolderResults();
[DllImport(Main.DLL)]
internal static extern int Everything_GetNumResults();
[DllImport(Main.DLL)]
internal static extern int Everything_GetTotFileResults();
[DllImport(Main.DLL)]
internal static extern int Everything_GetTotFolderResults();
[DllImport(Main.DLL)]
internal static extern int Everything_GetTotResults();
[DllImport(Main.DLL)]
internal static extern bool Everything_IsVolumeResult(int nIndex);
[DllImport(Main.DLL)]
internal static extern bool Everything_IsFolderResult(int nIndex);
[DllImport(Main.DLL)]
internal static extern bool Everything_IsFileResult(int nIndex);
[DllImport(Main.DLL, CharSet = CharSet.Unicode)]
internal static extern void Everything_GetResultFullPathNameW(int nIndex, StringBuilder lpString, int nMaxCount);
[DllImport(Main.DLL)]
internal static extern void Everything_Reset();
}
}

View file

@ -3,11 +3,12 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using Wox.Infrastructure; using Wox.Infrastructure;
using Wox.Infrastructure.Logger;
using Wox.Infrastructure.Storage; using Wox.Infrastructure.Storage;
using Wox.Plugin.Everything.Everything; using Wox.Plugin.Everything.Everything;
@ -15,14 +16,16 @@ namespace Wox.Plugin.Everything
{ {
public class Main : IPlugin, ISettingProvider, IPluginI18n, IContextMenu, ISavable public class Main : IPlugin, ISettingProvider, IPluginI18n, IContextMenu, ISavable
{ {
private readonly EverythingAPI _api = new EverythingAPI();
public const string DLL = "Everything.dll"; public const string DLL = "Everything.dll";
private readonly IEverythingApi _api = new EverythingApi();
private PluginInitContext _context; private PluginInitContext _context;
private Settings _settings; private Settings _settings;
private PluginJsonStorage<Settings> _storage; private PluginJsonStorage<Settings> _storage;
private CancellationTokenSource _cancellationTokenSource;
public void Save() public void Save()
{ {
@ -31,53 +34,24 @@ namespace Wox.Plugin.Everything
public List<Result> Query(Query query) public List<Result> Query(Query query)
{ {
_cancellationTokenSource?.Cancel(); // cancel if already exist
var cts = _cancellationTokenSource = new CancellationTokenSource();
var results = new List<Result>(); var results = new List<Result>();
if (!string.IsNullOrEmpty(query.Search)) if (!string.IsNullOrEmpty(query.Search))
{ {
var keyword = query.Search; var keyword = query.Search;
if (_settings.MaxSearchCount <= 0)
{
_settings.MaxSearchCount = 50;
}
try try
{ {
var searchList = _api.Search(keyword, maxCount: _settings.MaxSearchCount).ToList(); var searchList = _api.Search(keyword, cts.Token, maxCount: _settings.MaxSearchCount);
foreach (var s in searchList) if (searchList == null)
{ {
var path = s.FullPath; return results;
}
string workingDir = null; foreach (var searchResult in searchList)
if (_settings.UseLocationAsWorkingDir) {
workingDir = Path.GetDirectoryName(path); var r = CreateResult(keyword, searchResult);
Result r = new Result();
r.Title = Path.GetFileName(path);
r.SubTitle = path;
r.IcoPath = path;
r.Action = c =>
{
bool hide;
try
{
Process.Start(new ProcessStartInfo
{
FileName = path,
UseShellExecute = true,
WorkingDirectory = workingDir
});
hide = true;
}
catch (Win32Exception)
{
var name = $"Plugin: {_context.CurrentPluginMetadata.Name}";
var message = "Can't open this file";
_context.API.ShowMsg(name, message, string.Empty);
hide = false;
}
return hide;
};
r.ContextData = s;
results.Add(r); results.Add(r);
} }
} }
@ -91,6 +65,7 @@ namespace Wox.Plugin.Everything
} }
catch (Exception e) catch (Exception e)
{ {
Log.Exception("EverythingPlugin", "Query Error", e);
results.Add(new Result results.Add(new Result
{ {
Title = _context.API.GetTranslation("wox_plugin_everything_query_error"), Title = _context.API.GetTranslation("wox_plugin_everything_query_error"),
@ -106,13 +81,51 @@ namespace Wox.Plugin.Everything
} }
} }
_api.Reset();
return results; return results;
} }
[DllImport("kernel32.dll")] private Result CreateResult(string keyword, SearchResult searchResult)
private static extern int LoadLibrary(string name); {
var path = searchResult.FullPath;
string workingDir = null;
if (_settings.UseLocationAsWorkingDir)
workingDir = Path.GetDirectoryName(path);
var r = new Result
{
Title = Path.GetFileName(path),
SubTitle = path,
IcoPath = path,
TitleHighlightData = StringMatcher.FuzzySearch(keyword, Path.GetFileName(path)).MatchData,
Action = c =>
{
bool hide;
try
{
Process.Start(new ProcessStartInfo
{
FileName = path, UseShellExecute = true, WorkingDirectory = workingDir
});
hide = true;
}
catch (Win32Exception)
{
var name = $"Plugin: {_context.CurrentPluginMetadata.Name}";
var message = "Can't open this file";
_context.API.ShowMsg(name, message, string.Empty);
hide = false;
}
return hide;
},
ContextData = searchResult,
SubTitleHighlightData = StringMatcher.FuzzySearch(keyword, path).MatchData
};
return r;
}
private List<ContextMenu> GetDefaultContextMenu() private List<ContextMenu> GetDefaultContextMenu()
{ {
@ -147,6 +160,10 @@ namespace Wox.Plugin.Everything
_context = context; _context = context;
_storage = new PluginJsonStorage<Settings>(); _storage = new PluginJsonStorage<Settings>();
_settings = _storage.Load(); _settings = _storage.Load();
if (_settings.MaxSearchCount <= 0)
{
_settings.MaxSearchCount = Settings.DefaultMaxSearchCount;
}
var pluginDirectory = context.CurrentPluginMetadata.PluginDirectory; var pluginDirectory = context.CurrentPluginMetadata.PluginDirectory;
const string sdk = "EverythingSDK"; const string sdk = "EverythingSDK";
@ -156,7 +173,7 @@ namespace Wox.Plugin.Everything
var sdkPath = Path.Combine(sdkDirectory, DLL); var sdkPath = Path.Combine(sdkDirectory, DLL);
Constant.EverythingSDKPath = sdkPath; Constant.EverythingSDKPath = sdkPath;
LoadLibrary(sdkPath); _api.Load(sdkPath);
} }
private static string CpuType() private static string CpuType()

View file

@ -7,11 +7,13 @@ namespace Wox.Plugin.Everything
{ {
public class Settings public class Settings
{ {
public const int DefaultMaxSearchCount = 50;
public string EditorPath { get; set; } = ""; public string EditorPath { get; set; } = "";
public List<ContextMenu> ContextMenus = new List<ContextMenu>(); public List<ContextMenu> ContextMenus = new List<ContextMenu>();
public int MaxSearchCount { get; set; } = 100; public int MaxSearchCount { get; set; } = DefaultMaxSearchCount;
public bool UseLocationAsWorkingDir { get; set; } = false; public bool UseLocationAsWorkingDir { get; set; } = false;
} }

View file

@ -61,6 +61,7 @@
<Compile Include="EverythingSettings.xaml.cs"> <Compile Include="EverythingSettings.xaml.cs">
<DependentUpon>EverythingSettings.xaml</DependentUpon> <DependentUpon>EverythingSettings.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Everything\EverythingApiDllImport.cs" />
<Compile Include="Everything\Exceptions\CreateThreadException.cs" /> <Compile Include="Everything\Exceptions\CreateThreadException.cs" />
<Compile Include="Everything\Exceptions\CreateWindowException.cs" /> <Compile Include="Everything\Exceptions\CreateWindowException.cs" />
<Compile Include="Everything\EverythingAPI.cs" /> <Compile Include="Everything\EverythingAPI.cs" />
@ -100,7 +101,6 @@
<Content Include="Images\warning.png"> <Content Include="Images\warning.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="plugin.json"> <None Include="plugin.json">
@ -163,6 +163,17 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>9.0.1</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent> <PostBuildEvent>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -0,0 +1,218 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using Wox.Infrastructure.Logger;
using Wox.Infrastructure.Image;
using Wox.Plugin.SharedCommands;
namespace Wox.Plugin.Folder
{
internal class ContextMenuLoader : IContextMenu
{
private readonly PluginInitContext _context;
public ContextMenuLoader(PluginInitContext context)
{
_context = context;
}
public List<Result> LoadContextMenus(Result selectedResult)
{
var contextMenus = new List<Result>();
if (selectedResult.ContextData is SearchResult record)
{
if (record.Type == ResultType.File)
{
contextMenus.Add(CreateOpenWithEditorResult(record));
contextMenus.Add(CreateOpenContainingFolderResult(record));
}
var icoPath = (record.Type == ResultType.File) ? Main.FileImagePath : Main.FolderImagePath;
var fileOrFolder = (record.Type == ResultType.File) ? "file" : "folder";
contextMenus.Add(new Result
{
Title = "Copy path",
SubTitle = $"Copy the current {fileOrFolder} path to clipboard",
Action = (context) =>
{
try
{
Clipboard.SetText(record.FullPath);
return true;
}
catch (Exception e)
{
var message = "Fail to set text in clipboard";
LogException(message, e);
_context.API.ShowMsg(message);
return false;
}
},
IcoPath = Main.CopyImagePath
});
contextMenus.Add(new Result
{
Title = $"Copy {fileOrFolder}",
SubTitle = $"Copy the {fileOrFolder} to clipboard",
Action = (context) =>
{
try
{
Clipboard.SetFileDropList(new System.Collections.Specialized.StringCollection { record.FullPath });
return true;
}
catch (Exception e)
{
var message = $"Fail to set {fileOrFolder} in clipboard";
LogException(message, e);
_context.API.ShowMsg(message);
return false;
}
},
IcoPath = icoPath
});
if (record.Type == ResultType.File || record.Type == ResultType.Folder)
contextMenus.Add(new Result
{
Title = $"Delete {fileOrFolder}",
SubTitle = $"Delete the selected {fileOrFolder}",
Action = (context) =>
{
try
{
if (record.Type == ResultType.File)
File.Delete(record.FullPath);
else
Directory.Delete(record.FullPath);
}
catch(Exception e)
{
var message = $"Fail to delete {fileOrFolder} at {record.FullPath}";
LogException(message, e);
_context.API.ShowMsg(message);
return false;
}
return true;
},
IcoPath = Main.DeleteFileFolderImagePath
});
if (record.Type == ResultType.File && CanRunAsDifferentUser(record.FullPath))
contextMenus.Add(new Result
{
Title = "Run as different user",
Action = (context) =>
{
try
{
Task.Run(()=> ShellCommand.RunAsDifferentUser(record.FullPath.SetProcessStartInfo()));
}
catch (FileNotFoundException e)
{
var name = "Plugin: Folder";
var message = $"File not found: {e.Message}";
_context.API.ShowMsg(name, message);
}
return true;
},
IcoPath = "Images/user.png"
});
}
return contextMenus;
}
private Result CreateOpenContainingFolderResult(SearchResult record)
{
return new Result
{
Title = "Open containing folder",
Action = _ =>
{
try
{
Process.Start("explorer.exe", $" /select,\"{record.FullPath}\"");
}
catch(Exception e)
{
var message = $"Fail to open file at {record.FullPath}";
LogException(message, e);
_context.API.ShowMsg(message);
return false;
}
return true;
},
IcoPath = Main.FolderImagePath
};
}
private Result CreateOpenWithEditorResult(SearchResult record)
{
string editorPath = "notepad.exe"; // TODO add the ability to create a custom editor
var name = "Open With Editor: " + Path.GetFileNameWithoutExtension(editorPath);
return new Result
{
Title = name,
Action = _ =>
{
try
{
Process.Start(editorPath, record.FullPath);
return true;
}
catch (Exception e)
{
var message = $"Fail to editor for file at {record.FullPath}";
LogException(message, e);
_context.API.ShowMsg(message);
return false;
}
},
IcoPath = editorPath
};
}
public void LogException(string message, Exception e)
{
Log.Exception($"|Wox.Plugin.Folder.ContextMenu|{message}", e);
}
private bool CanRunAsDifferentUser(string path)
{
switch(Path.GetExtension(path))
{
case ".exe":
case ".bat":
return true;
default:
return false;
}
}
}
public class SearchResult
{
public string FullPath { get; set; }
public ResultType Type { get; set; }
}
public enum ResultType
{
Volume,
Folder,
File
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View file

@ -5,17 +5,26 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using Wox.Infrastructure;
using Wox.Infrastructure.Storage; using Wox.Infrastructure.Storage;
namespace Wox.Plugin.Folder namespace Wox.Plugin.Folder
{ {
public class Main : IPlugin, ISettingProvider, IPluginI18n, ISavable public class Main : IPlugin, ISettingProvider, IPluginI18n, ISavable, IContextMenu
{ {
public const string FolderImagePath = "Images\\folder.png";
public const string FileImagePath = "Images\\file.png";
public const string DeleteFileFolderImagePath = "Images\\deletefilefolder.png";
public const string CopyImagePath = "Images\\copy.png";
private string DefaultFolderSubtitleString = "Ctrl + Enter to open the directory";
private static List<string> _driverNames; private static List<string> _driverNames;
private PluginInitContext _context; private PluginInitContext _context;
private readonly Settings _settings; private readonly Settings _settings;
private readonly PluginJsonStorage<Settings> _storage; private readonly PluginJsonStorage<Settings> _storage;
private IContextMenu _contextMenuLoader;
public Main() public Main()
{ {
@ -36,6 +45,7 @@ namespace Wox.Plugin.Folder
public void Init(PluginInitContext context) public void Init(PluginInitContext context)
{ {
_context = context; _context = context;
_contextMenuLoader = new ContextMenuLoader(context);
InitialDriverList(); InitialDriverList();
} }
@ -44,7 +54,7 @@ namespace Wox.Plugin.Folder
var results = GetUserFolderResults(query); var results = GetUserFolderResults(query);
string search = query.Search.ToLower(); string search = query.Search.ToLower();
if (_driverNames != null && !_driverNames.Any(search.StartsWith)) if (!IsDriveOrSharedFolder(search))
return results; return results;
results.AddRange(QueryInternal_Directory_Exists(query)); results.AddRange(QueryInternal_Directory_Exists(query));
@ -58,13 +68,34 @@ namespace Wox.Plugin.Folder
return results; return results;
} }
private Result CreateFolderResult(string title, string path, string queryActionKeyword) private static bool IsDriveOrSharedFolder(string search)
{
if (search.StartsWith(@"\\"))
{ // share folder
return true;
}
if (_driverNames != null && _driverNames.Any(search.StartsWith))
{ // normal drive letter
return true;
}
if (_driverNames == null && search.Length > 2 && char.IsLetter(search[0]) && search[1] == ':')
{ // when we don't have the drive letters we can try...
return true; // we don't know so let's give it the possibility
}
return false;
}
private Result CreateFolderResult(string title, string subtitle, string path, Query query)
{ {
return new Result return new Result
{ {
Title = title, Title = title,
IcoPath = path, IcoPath = path,
SubTitle = "Ctrl + Enter to open the directory", SubTitle = subtitle,
TitleHighlightData = StringMatcher.FuzzySearch(query.Search, title).MatchData,
Action = c => Action = c =>
{ {
if (c.SpecialKeyState.CtrlPressed) if (c.SpecialKeyState.CtrlPressed)
@ -82,9 +113,12 @@ namespace Wox.Plugin.Folder
} }
string changeTo = path.EndsWith("\\") ? path : path + "\\"; string changeTo = path.EndsWith("\\") ? path : path + "\\";
_context.API.ChangeQuery(string.IsNullOrEmpty(queryActionKeyword)? changeTo : queryActionKeyword + " " + changeTo); _context.API.ChangeQuery(string.IsNullOrEmpty(query.ActionKeyword) ?
changeTo :
query.ActionKeyword + " " + changeTo);
return false; return false;
} },
ContextData = new SearchResult { Type = ResultType.Folder, FullPath = path }
}; };
} }
@ -93,8 +127,8 @@ namespace Wox.Plugin.Folder
string search = query.Search.ToLower(); string search = query.Search.ToLower();
var userFolderLinks = _settings.FolderLinks.Where( var userFolderLinks = _settings.FolderLinks.Where(
x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase)); x => x.Nickname.StartsWith(search, StringComparison.OrdinalIgnoreCase));
var results = userFolderLinks.Select(item => CreateFolderResult(item.Nickname, item.Path, query.ActionKeyword)) var results = userFolderLinks.Select(item =>
.ToList(); CreateFolderResult(item.Nickname, DefaultFolderSubtitleString, item.Path, query)).ToList();
return results; return results;
} }
@ -132,48 +166,67 @@ namespace Wox.Plugin.Folder
incompleteName = search.Substring(index + 1).ToLower(); incompleteName = search.Substring(index + 1).ToLower();
search = search.Substring(0, index + 1); search = search.Substring(0, index + 1);
if (!Directory.Exists(search)) if (!Directory.Exists(search))
{
return results; return results;
}
} }
else else
{
return results; return results;
}
} }
else else
{ // folder exist, add \ at the end of doesn't exist {
// folder exist, add \ at the end of doesn't exist
if (!search.EndsWith("\\")) if (!search.EndsWith("\\"))
{
search += "\\"; search += "\\";
}
} }
results.Add(CreateOpenCurrentFolderResult(incompleteName, search)); results.Add(CreateOpenCurrentFolderResult(incompleteName, search));
var searchOption = SearchOption.TopDirectoryOnly;
var directoryInfo = new DirectoryInfo(search);
var searchOption= SearchOption.TopDirectoryOnly;
incompleteName += "*"; incompleteName += "*";
if (incompleteName.StartsWith(">")) // give the ability to search all folder when starting with > // give the ability to search all folder when starting with >
if (incompleteName.StartsWith(">"))
{ {
searchOption = SearchOption.AllDirectories; searchOption = SearchOption.AllDirectories;
incompleteName = incompleteName.Substring(1);
// match everything before and after search term using supported wildcard '*', ie. *searchterm*
incompleteName = "*" + incompleteName.Substring(1);
} }
var folderList = new List<Result>();
var fileList = new List<Result>();
var folderSubtitleString = DefaultFolderSubtitleString;
try try
{ {
// search folder and add results // search folder and add results
var directoryInfo = new DirectoryInfo(search);
var fileSystemInfos = directoryInfo.GetFileSystemInfos(incompleteName, searchOption); var fileSystemInfos = directoryInfo.GetFileSystemInfos(incompleteName, searchOption);
foreach (var fileSystemInfo in fileSystemInfos) foreach (var fileSystemInfo in fileSystemInfos)
{ {
if ((fileSystemInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue; if ((fileSystemInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) continue;
var result = if(fileSystemInfo is DirectoryInfo)
fileSystemInfo is DirectoryInfo {
? CreateFolderResult(fileSystemInfo.Name, fileSystemInfo.FullName, query.ActionKeyword) if (searchOption == SearchOption.AllDirectories)
: CreateFileResult(fileSystemInfo.FullName); folderSubtitleString = fileSystemInfo.FullName;
results.Add(result);
folderList.Add(CreateFolderResult(fileSystemInfo.Name, folderSubtitleString, fileSystemInfo.FullName, query));
}
else
{
fileList.Add(CreateFileResult(fileSystemInfo.FullName, query));
}
} }
} }
catch(Exception e) catch (Exception e)
{ {
if (e is UnauthorizedAccessException || e is ArgumentException) if (e is UnauthorizedAccessException || e is ArgumentException)
{ {
@ -185,15 +238,18 @@ namespace Wox.Plugin.Folder
throw; throw;
} }
return results; // Intial ordering, this order can be updated later by UpdateResultView.MainViewModel based on history of user selection.
return results.Concat(folderList.OrderBy(x => x.Title)).Concat(fileList.OrderBy(x => x.Title)).ToList();
} }
private static Result CreateFileResult(string filePath) private static Result CreateFileResult(string filePath, Query query)
{ {
var result = new Result var result = new Result
{ {
Title = Path.GetFileName(filePath), Title = Path.GetFileName(filePath),
SubTitle = filePath,
IcoPath = filePath, IcoPath = filePath,
TitleHighlightData = StringMatcher.FuzzySearch(query.Search, Path.GetFileName(filePath)).MatchData,
Action = c => Action = c =>
{ {
try try
@ -206,7 +262,8 @@ namespace Wox.Plugin.Folder
} }
return true; return true;
} },
ContextData = new SearchResult { Type = ResultType.File, FullPath = filePath}
}; };
return result; return result;
} }
@ -243,5 +300,10 @@ namespace Wox.Plugin.Folder
{ {
return _context.API.GetTranslation("wox_plugin_folder_plugin_description"); return _context.API.GetTranslation("wox_plugin_folder_plugin_description");
} }
public List<Result> LoadContextMenus(Result selectedResult)
{
return _contextMenuLoader.LoadContextMenus(selectedResult);
}
} }
} }

View file

@ -58,6 +58,7 @@
<Compile Include="..\..\SolutionAssemblyInfo.cs"> <Compile Include="..\..\SolutionAssemblyInfo.cs">
<Link>Properties\SolutionAssemblyInfo.cs</Link> <Link>Properties\SolutionAssemblyInfo.cs</Link>
</Compile> </Compile>
<Compile Include="ContextMenuLoader.cs" />
<Compile Include="FolderLink.cs" /> <Compile Include="FolderLink.cs" />
<Compile Include="Main.cs" /> <Compile Include="Main.cs" />
<Compile Include="FolderPluginSettings.xaml.cs"> <Compile Include="FolderPluginSettings.xaml.cs">
@ -67,7 +68,6 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="plugin.json"> <None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -80,6 +80,15 @@
<None Include="Images\copy.png"> <None Include="Images\copy.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="Images\deletefilefolder.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Content Include="Images\file.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="Images\user.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Content Include="Languages\en.xaml"> <Content Include="Languages\en.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
@ -126,6 +135,17 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>9.0.1</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</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,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -33,10 +33,6 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="JetBrains.Annotations, Version=10.3.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>..\..\packages\JetBrains.Annotations.10.3.0\lib\net\JetBrains.Annotations.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
@ -49,7 +45,6 @@
<Compile Include="Main.cs" /> <Compile Include="Main.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="plugin.json"> <None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -115,6 +110,14 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</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,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using Newtonsoft.Json; using Newtonsoft.Json;
using Wox.Infrastructure;
using Wox.Infrastructure.Http; using Wox.Infrastructure.Http;
using Wox.Infrastructure.Logger; using Wox.Infrastructure.Logger;
@ -142,6 +143,8 @@ namespace Wox.Plugin.PluginManagement
Title = r.name, Title = r.name,
SubTitle = r.description, SubTitle = r.description,
IcoPath = "Images\\plugin.png", IcoPath = "Images\\plugin.png",
TitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, r.name).MatchData,
SubTitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, r.description).MatchData,
Action = c => Action = c =>
{ {
MessageBoxResult result = MessageBox.Show("Are you sure you wish to install the \'" + r.name + "\' plugin", MessageBoxResult result = MessageBox.Show("Are you sure you wish to install the \'" + r.name + "\' plugin",
@ -191,6 +194,8 @@ namespace Wox.Plugin.PluginManagement
Title = plugin.Name, Title = plugin.Name,
SubTitle = plugin.Description, SubTitle = plugin.Description,
IcoPath = plugin.IcoPath, IcoPath = plugin.IcoPath,
TitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, plugin.Name).MatchData,
SubTitleHighlightData = StringMatcher.FuzzySearch(query.SecondSearch, plugin.Description).MatchData,
Action = e => Action = e =>
{ {
UnInstallPlugin(plugin); UnInstallPlugin(plugin);

View file

@ -34,14 +34,6 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="JetBrains.Annotations, Version=10.3.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>..\..\packages\JetBrains.Annotations.10.3.0\lib\net\JetBrains.Annotations.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
@ -68,7 +60,6 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="plugin.json"> <None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -120,6 +111,17 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>9.0.1</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent> <PostBuildEvent>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View file

@ -31,6 +31,7 @@
<system:String x:Key="wox_plugin_program_update_file_suffixes">Successfully updated file suffixes</system:String> <system:String x:Key="wox_plugin_program_update_file_suffixes">Successfully updated file suffixes</system:String>
<system:String x:Key="wox_plugin_program_suffixes_cannot_empty">File suffixes can't be empty</system:String> <system:String x:Key="wox_plugin_program_suffixes_cannot_empty">File suffixes can't be empty</system:String>
<system:String x:Key="wox_plugin_program_run_as_different_user">Run As Different User</system:String>
<system:String x:Key="wox_plugin_program_run_as_administrator">Run As Administrator</system:String> <system:String x:Key="wox_plugin_program_run_as_administrator">Run As Administrator</system:String>
<system:String x:Key="wox_plugin_program_open_containing_folder">Open containing folder</system:String> <system:String x:Key="wox_plugin_program_open_containing_folder">Open containing folder</system:String>
<system:String x:Key="wox_plugin_program_disable_program">Disable this program from displaying</system:String> <system:String x:Key="wox_plugin_program_disable_program">Disable this program from displaying</system:String>

View file

@ -78,8 +78,8 @@ namespace Wox.Plugin.Program
} }
var results1 = win32.AsParallel() var results1 = win32.AsParallel()
.Where(p => p.Enabled) .Where(p => p.Enabled)
.Select(p => p.Result(query.Search, _context.API)); .Select(p => p.Result(query.Search, _context.API));
var results2 = uwps.AsParallel() var results2 = uwps.AsParallel()
.Where(p => p.Enabled) .Where(p => p.Enabled)
@ -191,22 +191,19 @@ namespace Wox.Plugin.Program
); );
} }
public static bool StartProcess(ProcessStartInfo info) public static void StartProcess(Func<ProcessStartInfo, Process> runProcess, ProcessStartInfo info)
{ {
bool hide; bool hide;
try try
{ {
Process.Start(info); runProcess(info);
hide = true;
} }
catch (Exception) catch (Exception)
{ {
var name = "Plugin: Program"; var name = "Plugin: Program";
var message = $"Can't start: {info.FileName}"; var message = $"Unable to start: {info.FileName}";
_context.API.ShowMsg(name, message, string.Empty); _context.API.ShowMsg(name, message, string.Empty);
hide = false;
} }
return hide;
} }
public void ReloadData() public void ReloadData()

View file

@ -35,7 +35,6 @@ namespace Wox.Plugin.Program.Programs
public UWP(Package package) public UWP(Package package)
{ {
Location = package.InstalledLocation.Path; Location = package.InstalledLocation.Path;
Name = package.Id.Name; Name = package.Id.Name;
FullName = package.Id.FullName; FullName = package.Id.FullName;
@ -266,11 +265,9 @@ namespace Wox.Plugin.Program.Programs
private int Score(string query) private int Score(string query)
{ {
var score1 = StringMatcher.FuzzySearch(query, DisplayName).ScoreAfterSearchPrecisionFilter(); var displayNameMatch = StringMatcher.FuzzySearch(query, DisplayName);
var score2 = StringMatcher.ScoreForPinyin(DisplayName, query); var descriptionMatch = StringMatcher.FuzzySearch(query, Description);
var score3 = StringMatcher.FuzzySearch(query, Description).ScoreAfterSearchPrecisionFilter(); var score = new[] { displayNameMatch.Score, descriptionMatch.Score }.Max();
var score4 = StringMatcher.ScoreForPinyin(Description, query);
var score = new[] { score1, score2, score3, score4 }.Max();
return score; return score;
} }
@ -299,14 +296,18 @@ namespace Wox.Plugin.Program.Programs
Description.Substring(0, DisplayName.Length) == DisplayName) Description.Substring(0, DisplayName.Length) == DisplayName)
{ {
result.Title = Description; result.Title = Description;
result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData;
} }
else if (!string.IsNullOrEmpty(Description)) else if (!string.IsNullOrEmpty(Description))
{ {
result.Title = $"{DisplayName}: {Description}"; var title = $"{DisplayName}: {Description}";
result.Title = title;
result.TitleHighlightData = StringMatcher.FuzzySearch(query, title).MatchData;
} }
else else
{ {
result.Title = DisplayName; result.Title = DisplayName;
result.TitleHighlightData = StringMatcher.FuzzySearch(query, DisplayName).MatchData;
} }
return result; return result;
} }
@ -318,11 +319,14 @@ namespace Wox.Plugin.Program.Programs
new Result new Result
{ {
Title = api.GetTranslation("wox_plugin_program_open_containing_folder"), Title = api.GetTranslation("wox_plugin_program_open_containing_folder"),
Action = _ => Action = _ =>
{ {
var hide = Main.StartProcess(new ProcessStartInfo(Package.Location)); Main.StartProcess(Process.Start, new ProcessStartInfo(Package.Location));
return hide;
return true;
}, },
IcoPath = "Images/folder.png" IcoPath = "Images/folder.png"
} }
}; };

View file

@ -6,10 +6,12 @@ using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security; using System.Security;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32; using Microsoft.Win32;
using Shell; using Shell;
using Wox.Infrastructure; using Wox.Infrastructure;
using Wox.Plugin.Program.Logger; using Wox.Plugin.Program.Logger;
using Wox.Plugin.SharedCommands;
namespace Wox.Plugin.Program.Programs namespace Wox.Plugin.Program.Programs
{ {
@ -33,12 +35,10 @@ namespace Wox.Plugin.Program.Programs
private int Score(string query) private int Score(string query)
{ {
var score1 = StringMatcher.FuzzySearch(query, Name).ScoreAfterSearchPrecisionFilter(); var nameMatch = StringMatcher.FuzzySearch(query, Name);
var score2 = StringMatcher.ScoreForPinyin(Name, query); var descriptionMatch = StringMatcher.FuzzySearch(query, Description);
var score3 = StringMatcher.FuzzySearch(query, Description).ScoreAfterSearchPrecisionFilter(); var executableNameMatch = StringMatcher.FuzzySearch(query, ExecutableName);
var score4 = StringMatcher.ScoreForPinyin(Description, query); var score = new[] { nameMatch.Score, descriptionMatch.Score, executableNameMatch.Score }.Max();
var score5 = StringMatcher.FuzzySearch(query, ExecutableName).ScoreAfterSearchPrecisionFilter();
var score = new[] { score1, score2, score3, score4, score5 }.Max();
return score; return score;
} }
@ -64,8 +64,10 @@ namespace Wox.Plugin.Program.Programs
FileName = FullPath, FileName = FullPath,
WorkingDirectory = ParentDirectory WorkingDirectory = ParentDirectory
}; };
var hide = Main.StartProcess(info);
return hide; Main.StartProcess(Process.Start, info);
return true;
} }
}; };
@ -73,14 +75,18 @@ namespace Wox.Plugin.Program.Programs
Description.Substring(0, Name.Length) == Name) Description.Substring(0, Name.Length) == Name)
{ {
result.Title = Description; result.Title = Description;
result.TitleHighlightData = StringMatcher.FuzzySearch(query, Description).MatchData;
} }
else if (!string.IsNullOrEmpty(Description)) else if (!string.IsNullOrEmpty(Description))
{ {
result.Title = $"{Name}: {Description}"; var title = $"{Name}: {Description}";
result.Title = title;
result.TitleHighlightData = StringMatcher.FuzzySearch(query, title).MatchData;
} }
else else
{ {
result.Title = Name; result.Title = Name;
result.TitleHighlightData = StringMatcher.FuzzySearch(query, Name).MatchData;
} }
return result; return result;
@ -91,6 +97,19 @@ namespace Wox.Plugin.Program.Programs
{ {
var contextMenus = new List<Result> var contextMenus = new List<Result>
{ {
new Result
{
Title = api.GetTranslation("wox_plugin_program_run_as_different_user"),
Action = _ =>
{
var info = FullPath.SetProcessStartInfo(ParentDirectory);
Task.Run(() => Main.StartProcess(ShellCommand.RunAsDifferentUser, info));
return true;
},
IcoPath = "Images/user.png"
},
new Result new Result
{ {
Title = api.GetTranslation("wox_plugin_program_run_as_administrator"), Title = api.GetTranslation("wox_plugin_program_run_as_administrator"),
@ -102,8 +121,10 @@ namespace Wox.Plugin.Program.Programs
WorkingDirectory = ParentDirectory, WorkingDirectory = ParentDirectory,
Verb = "runas" Verb = "runas"
}; };
var hide = Main.StartProcess(info);
return hide; Task.Run(() => Main.StartProcess(Process.Start, info));
return true;
}, },
IcoPath = "Images/cmd.png" IcoPath = "Images/cmd.png"
}, },
@ -112,8 +133,9 @@ namespace Wox.Plugin.Program.Programs
Title = api.GetTranslation("wox_plugin_program_open_containing_folder"), Title = api.GetTranslation("wox_plugin_program_open_containing_folder"),
Action = _ => Action = _ =>
{ {
var hide = Main.StartProcess(new ProcessStartInfo(ParentDirectory)); Main.StartProcess(Process.Start, new ProcessStartInfo(ParentDirectory));
return hide;
return true;
}, },
IcoPath = "Images/folder.png" IcoPath = "Images/folder.png"
} }

View file

@ -40,18 +40,7 @@
<HintPath>.\AppxPackagingTlb.dll</HintPath> <HintPath>.\AppxPackagingTlb.dll</HintPath>
<EmbedInteropTypes>True</EmbedInteropTypes> <EmbedInteropTypes>True</EmbedInteropTypes>
</Reference> </Reference>
<Reference Include="JetBrains.Annotations, Version=10.3.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>..\..\packages\JetBrains.Annotations.10.3.0\lib\net\JetBrains.Annotations.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\packages\NLog.4.2.0\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
<Reference Include="ShObjIdlTlb"> <Reference Include="ShObjIdlTlb">
@ -94,7 +83,6 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="plugin.json"> <None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -112,6 +100,9 @@
<None Include="Images\disable.png"> <None Include="Images\disable.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="Images\user.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Content Include="Languages\en.xaml"> <Content Include="Languages\en.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
@ -163,14 +154,24 @@
<Name>Wox.Plugin</Name> <Name>Wox.Plugin</Name>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>9.0.1</Version>
</PackageReference>
<PackageReference Include="NLog">
<Version>4.2.0</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
<PackageReference Include="UwpDesktop">
<Version>10.0.14393.3</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\packages\UwpDesktop.10.0.14393.3\build\portable-net45+uap\UwpDesktop.targets" Condition="Exists('..\..\packages\UwpDesktop.10.0.14393.3\build\portable-net45+uap\UwpDesktop.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\UwpDesktop.10.0.14393.3\build\portable-net45+uap\UwpDesktop.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\UwpDesktop.10.0.14393.3\build\portable-net45+uap\UwpDesktop.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="NLog" version="4.2.0" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
<package id="UwpDesktop" version="10.0.14393.3" targetFramework="net452" />
</packages>

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View file

@ -4,6 +4,8 @@
<system:String x:Key="wox_plugin_cmd_relace_winr">Replace Win+R</system:String> <system:String x:Key="wox_plugin_cmd_relace_winr">Replace Win+R</system:String>
<system:String x:Key="wox_plugin_cmd_leave_cmd_open">Do not close Command Prompt after command execution</system:String> <system:String x:Key="wox_plugin_cmd_leave_cmd_open">Do not close Command Prompt after command execution</system:String>
<system:String x:Key="wox_plugin_cmd_always_run_as_administrator">Always run as administrator</system:String>
<system:String x:Key="wox_plugin_cmd_run_as_different_user">Run as different user</system:String>
<system:String x:Key="wox_plugin_cmd_plugin_name">Shell</system:String> <system:String x:Key="wox_plugin_cmd_plugin_name">Shell</system:String>
<system:String x:Key="wox_plugin_cmd_plugin_description">Allows to execute system commands from Wox. Commands should start with ></system:String> <system:String x:Key="wox_plugin_cmd_plugin_description">Allows to execute system commands from Wox. Commands should start with ></system:String>
<system:String x:Key="wox_plugin_cmd_cmd_has_been_executed_times">this command has been executed {0} times</system:String> <system:String x:Key="wox_plugin_cmd_cmd_has_been_executed_times">this command has been executed {0} times</system:String>

View file

@ -1,14 +1,17 @@
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.Threading.Tasks;
using System.Windows; using System.Windows;
using WindowsInput; using WindowsInput;
using WindowsInput.Native; using WindowsInput.Native;
using Wox.Infrastructure.Hotkey; using Wox.Infrastructure.Hotkey;
using Wox.Infrastructure.Logger; using Wox.Infrastructure.Logger;
using Wox.Infrastructure.Storage; using Wox.Infrastructure.Storage;
using Wox.Plugin.SharedCommands;
using Application = System.Windows.Application; using Application = System.Windows.Application;
using Control = System.Windows.Controls.Control; using Control = System.Windows.Controls.Control;
using Keys = System.Windows.Forms.Keys; using Keys = System.Windows.Forms.Keys;
@ -83,7 +86,7 @@ namespace Wox.Plugin.Shell
IcoPath = Image, IcoPath = Image,
Action = c => Action = c =>
{ {
Execute(m); Execute(Process.Start, PrepareProcessStartInfo(m));
return true; return true;
} }
})); }));
@ -116,7 +119,7 @@ namespace Wox.Plugin.Shell
IcoPath = Image, IcoPath = Image,
Action = c => Action = c =>
{ {
Execute(m.Key); Execute(Process.Start, PrepareProcessStartInfo(m.Key));
return true; return true;
} }
}; };
@ -135,7 +138,7 @@ namespace Wox.Plugin.Shell
IcoPath = Image, IcoPath = Image,
Action = c => Action = c =>
{ {
Execute(cmd); Execute(Process.Start, PrepareProcessStartInfo(cmd));
return true; return true;
} }
}; };
@ -153,27 +156,26 @@ namespace Wox.Plugin.Shell
IcoPath = Image, IcoPath = Image,
Action = c => Action = c =>
{ {
Execute(m.Key); Execute(Process.Start, PrepareProcessStartInfo(m.Key));
return true; return true;
} }
}).Take(5); }).Take(5);
return history.ToList(); return history.ToList();
} }
private void Execute(string command, bool runAsAdministrator = false) private ProcessStartInfo PrepareProcessStartInfo(string command, bool runAsAdministrator = false)
{ {
command = command.Trim(); command = command.Trim();
command = Environment.ExpandEnvironmentVariables(command); command = Environment.ExpandEnvironmentVariables(command);
var workingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var runAsAdministratorArg = !runAsAdministrator && !_settings.RunAsAdministrator ? "" : "runas";
ProcessStartInfo info; ProcessStartInfo info;
if (_settings.Shell == Shell.Cmd) if (_settings.Shell == Shell.Cmd)
{ {
var arguments = _settings.LeaveShellOpen ? $"/k \"{command}\"" : $"/c \"{command}\" & pause"; var arguments = _settings.LeaveShellOpen ? $"/k \"{command}\"" : $"/c \"{command}\" & pause";
info = new ProcessStartInfo
{ info = ShellCommand.SetProcessStartInfo("cmd.exe", workingDirectory, arguments, runAsAdministratorArg);
FileName = "cmd.exe",
Arguments = arguments,
};
} }
else if (_settings.Shell == Shell.Powershell) else if (_settings.Shell == Shell.Powershell)
{ {
@ -186,11 +188,8 @@ namespace Wox.Plugin.Shell
{ {
arguments = $"\"{command} ; Read-Host -Prompt \\\"Press Enter to continue\\\"\""; arguments = $"\"{command} ; Read-Host -Prompt \\\"Press Enter to continue\\\"\"";
} }
info = new ProcessStartInfo
{ info = ShellCommand.SetProcessStartInfo("powershell.exe", workingDirectory, arguments, runAsAdministratorArg);
FileName = "powershell.exe",
Arguments = arguments
};
} }
else if (_settings.Shell == Shell.RunCommand) else if (_settings.Shell == Shell.RunCommand)
{ {
@ -200,41 +199,48 @@ namespace Wox.Plugin.Shell
var filename = parts[0]; var filename = parts[0];
if (ExistInPath(filename)) if (ExistInPath(filename))
{ {
var arguemtns = parts[1]; var arguments = parts[1];
info = new ProcessStartInfo info = ShellCommand.SetProcessStartInfo(filename, workingDirectory, arguments, runAsAdministratorArg);
{
FileName = filename,
Arguments = arguemtns
};
} }
else else
{ {
info = new ProcessStartInfo(command); info = ShellCommand.SetProcessStartInfo(command, verb: runAsAdministratorArg);
} }
} }
else else
{ {
info = new ProcessStartInfo(command); info = ShellCommand.SetProcessStartInfo(command, verb: runAsAdministratorArg);
} }
} }
else else
{ {
return; throw new NotImplementedException();
} }
info.UseShellExecute = true; info.UseShellExecute = true;
info.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
info.Verb = runAsAdministrator ? "runas" : "";
_settings.AddCmdHistory(command);
return info;
}
private void Execute(Func<ProcessStartInfo, Process> startProcess,ProcessStartInfo info)
{
try try
{ {
Process.Start(info); startProcess(info);
_settings.AddCmdHistory(command);
} }
catch (FileNotFoundException e) catch (FileNotFoundException e)
{ {
MessageBox.Show($"Command not found: {e.Message}"); var name = "Plugin: Shell";
var message = $"Command not found: {e.Message}";
_context.API.ShowMsg(name, message);
}
catch(Win32Exception e)
{
var name = "Plugin: Shell";
var message = $"Error running the command: {e.Message}";
_context.API.ShowMsg(name, message);
} }
} }
@ -316,19 +322,31 @@ namespace Wox.Plugin.Shell
public List<Result> LoadContextMenus(Result selectedResult) public List<Result> LoadContextMenus(Result selectedResult)
{ {
return new List<Result> var resultlist = new List<Result>
{ {
new Result new Result
{ {
Title = _context.API.GetTranslation("wox_plugin_cmd_run_as_administrator"), Title = _context.API.GetTranslation("wox_plugin_cmd_run_as_different_user"),
Action = c => Action = c =>
{ {
Execute(selectedResult.Title, true); Task.Run(() =>Execute(ShellCommand.RunAsDifferentUser, PrepareProcessStartInfo(selectedResult.Title)));
return true; return true;
}, },
IcoPath = Image IcoPath = "Images/user.png"
} },
}; new Result
{
Title = _context.API.GetTranslation("wox_plugin_cmd_run_as_administrator"),
Action = c =>
{
Execute(Process.Start, PrepareProcessStartInfo(selectedResult.Title, true));
return true;
},
IcoPath = Image
}
};
return resultlist;
} }
} }
} }

View file

@ -7,6 +7,8 @@ namespace Wox.Plugin.Shell
public Shell Shell { get; set; } = Shell.Cmd; public Shell Shell { get; set; } = Shell.Cmd;
public bool ReplaceWinR { get; set; } = true; public bool ReplaceWinR { get; set; } = true;
public bool LeaveShellOpen { get; set; } public bool LeaveShellOpen { get; set; }
public bool RunAsAdministrator { get; set; } = true;
public Dictionary<string, int> Count = new Dictionary<string, int>(); public Dictionary<string, int> Count = new Dictionary<string, int>();
public void AddCmdHistory(string cmdName) public void AddCmdHistory(string cmdName)

View file

@ -12,10 +12,12 @@
<RowDefinition/> <RowDefinition/>
<RowDefinition/> <RowDefinition/>
<RowDefinition/> <RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<CheckBox Grid.Row="0" x:Name="ReplaceWinR" Content="{DynamicResource wox_plugin_cmd_relace_winr}" Margin="10" HorizontalAlignment="Left"/> <CheckBox Grid.Row="0" x:Name="ReplaceWinR" Content="{DynamicResource wox_plugin_cmd_relace_winr}" Margin="10" HorizontalAlignment="Left"/>
<CheckBox Grid.Row="1" x:Name="LeaveShellOpen" Content="{DynamicResource wox_plugin_cmd_leave_cmd_open}" Margin="10" HorizontalAlignment="Left"/> <CheckBox Grid.Row="1" x:Name="LeaveShellOpen" Content="{DynamicResource wox_plugin_cmd_leave_cmd_open}" Margin="10" HorizontalAlignment="Left"/>
<ComboBox Grid.Row="2" x:Name="ShellComboBox" Margin="10" HorizontalAlignment="Left" > <CheckBox Grid.Row="2" x:Name="AlwaysRunAsAdministrator" Content="{DynamicResource wox_plugin_cmd_always_run_as_administrator}" Margin="10" HorizontalAlignment="Left"/>
<ComboBox Grid.Row="3" x:Name="ShellComboBox" Margin="10" HorizontalAlignment="Left" >
<ComboBoxItem>CMD</ComboBoxItem> <ComboBoxItem>CMD</ComboBoxItem>
<ComboBoxItem>PowerShell</ComboBoxItem> <ComboBoxItem>PowerShell</ComboBoxItem>
<ComboBoxItem>RunCommand</ComboBoxItem> <ComboBoxItem>RunCommand</ComboBoxItem>

View file

@ -17,6 +17,7 @@ namespace Wox.Plugin.Shell
{ {
ReplaceWinR.IsChecked = _settings.ReplaceWinR; ReplaceWinR.IsChecked = _settings.ReplaceWinR;
LeaveShellOpen.IsChecked = _settings.LeaveShellOpen; LeaveShellOpen.IsChecked = _settings.LeaveShellOpen;
AlwaysRunAsAdministrator.IsChecked = _settings.RunAsAdministrator;
LeaveShellOpen.IsEnabled = _settings.Shell != Shell.RunCommand; LeaveShellOpen.IsEnabled = _settings.Shell != Shell.RunCommand;
LeaveShellOpen.Checked += (o, e) => LeaveShellOpen.Checked += (o, e) =>
@ -29,6 +30,16 @@ namespace Wox.Plugin.Shell
_settings.LeaveShellOpen = false; _settings.LeaveShellOpen = false;
}; };
AlwaysRunAsAdministrator.Checked += (o, e) =>
{
_settings.RunAsAdministrator = true;
};
AlwaysRunAsAdministrator.Unchecked += (o, e) =>
{
_settings.RunAsAdministrator = false;
};
ReplaceWinR.Checked += (o, e) => ReplaceWinR.Checked += (o, e) =>
{ {
_settings.ReplaceWinR = true; _settings.ReplaceWinR = true;

View file

@ -70,6 +70,9 @@
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Images\user.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Content Include="Languages\en.xaml"> <Content Include="Languages\en.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
@ -102,7 +105,6 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="plugin.json"> <None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -128,6 +130,20 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Page> </Page>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="InputSimulator">
<Version>1.0.4</Version>
</PackageReference>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>9.0.1</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</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,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="InputSimulator" version="1.0.4.0" targetFramework="net452" />
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -56,12 +56,21 @@ namespace Wox.Plugin.Sys
var results = new List<Result>(); var results = new List<Result>();
foreach (var c in commands) foreach (var c in commands)
{ {
var titleScore = StringMatcher.FuzzySearch(query.Search, c.Title).ScoreAfterSearchPrecisionFilter(); var titleMatch = StringMatcher.FuzzySearch(query.Search, c.Title);
var subTitleScore = StringMatcher.FuzzySearch(query.Search, c.SubTitle).ScoreAfterSearchPrecisionFilter(); var subTitleMatch = StringMatcher.FuzzySearch(query.Search, c.SubTitle);
var score = Math.Max(titleScore, subTitleScore);
var score = Math.Max(titleMatch.Score, subTitleMatch.Score);
if (score > 0) if (score > 0)
{ {
c.Score = score; c.Score = score;
if (score == titleMatch.Score)
{
c.TitleHighlightData = titleMatch.MatchData;
}
else
{
c.SubTitleHighlightData = subTitleMatch.MatchData;
}
results.Add(c); results.Add(c);
} }
} }

View file

@ -113,7 +113,6 @@
</Page> </Page>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="plugin.json"> <None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -146,6 +145,14 @@
<ItemGroup> <ItemGroup>
<Folder Include="Images\Images\" /> <Folder Include="Images\Images\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</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,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -56,7 +56,6 @@
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="plugin.json"> <None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -122,6 +121,14 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</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,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -34,14 +34,6 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="JetBrains.Annotations, Version=10.3.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>..\..\packages\JetBrains.Annotations.10.3.0\lib\net\JetBrains.Annotations.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
<Reference Include="System" /> <Reference Include="System" />
@ -183,7 +175,6 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="plugin.json"> <None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@ -193,6 +184,17 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>9.0.1</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</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,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -1,12 +1,12 @@
WoX WoX
=== ===
![Maintenance](https://img.shields.io/maintenance/yes/2019) ![Maintenance](https://img.shields.io/maintenance/yes/2020)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/jjw24/wox)](https://github.com/jjw24/Wox/releases/latest) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/jjw24/wox)](https://github.com/jjw24/Wox/releases/latest)
![GitHub Release Date](https://img.shields.io/github/release-date/jjw24/wox) ![GitHub Release Date](https://img.shields.io/github/release-date/jjw24/wox)
![GitHub commits since latest release](https://img.shields.io/github/commits-since/jjw24/wox/v1.3.524) ![GitHub commits since latest release](https://img.shields.io/github/commits-since/jjw24/wox/v1.3.524)
[![Build status](https://ci.appveyor.com/api/projects/status/bfktntbivg32e103/branch/master?svg=true)](https://ci.appveyor.com/project/happlebao/wox/branch/master) [![Build Status](https://dev.azure.com/Wox-Launcher/Wox/_apis/build/status/jjw24.Wox?branchName=master)](https://dev.azure.com/Wox-Launcher/Wox/_build/latest?definitionId=1&branchName=master)
[![Github All Releases](https://img.shields.io/github/downloads/Wox-launcher/Wox/total.svg)](https://github.com/Wox-launcher/Wox/releases) [![Github All Releases](https://img.shields.io/github/downloads/jjw24/Wox/total.svg)](https://github.com/jjw24/Wox/releases)
[![RamenBless](https://cdn.rawgit.com/LunaGao/BlessYourCodeTag/master/tags/ramen.svg)](https://github.com/LunaGao/BlessYourCodeTag) [![RamenBless](https://cdn.rawgit.com/LunaGao/BlessYourCodeTag/master/tags/ramen.svg)](https://github.com/LunaGao/BlessYourCodeTag)
**WoX** is a launcher for Windows that simply works. It's an alternative to [Alfred](https://www.alfredapp.com/) and [Launchy](http://www.launchy.net/). You can call it Windows omni-eXecutor if you want a long name. **WoX** is a launcher for Windows that simply works. It's an alternative to [Alfred](https://www.alfredapp.com/) and [Launchy](http://www.launchy.net/). You can call it Windows omni-eXecutor if you want a long name.
@ -49,6 +49,8 @@ Usage
----- -----
- Launch: <kbd>Alt</kbd>+<kbd>Space</kbd> - Launch: <kbd>Alt</kbd>+<kbd>Space</kbd>
- Context Menu: <kbd>Ctrl</kbd>+<kbd>O</kbd>
- Cancel/Return: <kbd>Esc</kbd>
- Install/Uninstall plugin: type `wpm install/uninstall` - Install/Uninstall plugin: type `wpm install/uninstall`
- Reset: delete `%APPDATA%\Wox` - Reset: delete `%APPDATA%\Wox`
- Log: `%APPDATA%\Wox\Logs` - Log: `%APPDATA%\Wox\Logs`

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -92,21 +93,36 @@ namespace Wox.Core.Plugin
Settings.UpdatePluginSettings(_metadatas); Settings.UpdatePluginSettings(_metadatas);
AllPlugins = PluginsLoader.Plugins(_metadatas, Settings); AllPlugins = PluginsLoader.Plugins(_metadatas, Settings);
} }
/// <summary>
/// Call initialize for all plugins
/// </summary>
/// <returns>return the list of failed to init plugins or null for none</returns>
public static void InitializePlugins(IPublicAPI api) public static void InitializePlugins(IPublicAPI api)
{ {
API = api; API = api;
var failedPlugins = new ConcurrentQueue<PluginPair>();
Parallel.ForEach(AllPlugins, pair => Parallel.ForEach(AllPlugins, pair =>
{ {
var milliseconds = Stopwatch.Debug($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", () => try
{ {
pair.Plugin.Init(new PluginInitContext var milliseconds = Stopwatch.Debug($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>", () =>
{ {
CurrentPluginMetadata = pair.Metadata, pair.Plugin.Init(new PluginInitContext
API = API {
CurrentPluginMetadata = pair.Metadata,
API = API
});
}); });
}); pair.Metadata.InitTime += milliseconds;
pair.Metadata.InitTime += milliseconds; Log.Info($"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
Log.Info($"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>"); }
catch (Exception e)
{
Log.Exception(nameof(PluginManager), $"Fail to Init plugin: {pair.Metadata.Name}", e);
pair.Metadata.Disabled = true; // TODO: not sure this really disable it later on
failedPlugins.Enqueue(pair);
}
}); });
_contextMenuPlugins = GetPluginsForInterface<IContextMenu>(); _contextMenuPlugins = GetPluginsForInterface<IContextMenu>();
@ -121,6 +137,11 @@ namespace Wox.Core.Plugin
.ForEach(x => NonGlobalPlugins[x] = plugin); .ForEach(x => NonGlobalPlugins[x] = plugin);
} }
if (failedPlugins.Any())
{
var failed = string.Join(",", failedPlugins.Select(x => x.Metadata.Name));
API.ShowMsg($"Fail to Init Plugins", $"Plugins: {failed} - fail to load and would be disabled, please contact plugin creator for help", "", false);
}
} }
public static void InstallPlugin(string path) public static void InstallPlugin(string path)

View file

@ -16,18 +16,23 @@ using Wox.Infrastructure.Logger;
namespace Wox.Core namespace Wox.Core
{ {
public static class Updater public class Updater
{ {
private static readonly Internationalization Translater = InternationalizationManager.Instance; public string GitHubRepository { get; }
public static async Task UpdateApp() public Updater(string gitHubRepository)
{
GitHubRepository = gitHubRepository;
}
public async Task UpdateApp()
{ {
UpdateManager m; UpdateManager m;
UpdateInfo u; UpdateInfo u;
try try
{ {
m = await GitHubUpdateManager(Constant.Repository); m = await GitHubUpdateManager(GitHubRepository);
} }
catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException) catch (Exception e) when (e is HttpRequestException || e is WebException || e is SocketException)
{ {
@ -66,8 +71,8 @@ namespace Wox.Core
await m.ApplyReleases(u); await m.ApplyReleases(u);
await m.CreateUninstallerRegistryEntry(); await m.CreateUninstallerRegistryEntry();
var newVersionTips = Translater.GetTranslation("newVersionTips"); var newVersionTips = this.NewVersinoTips(fr.Version.ToString());
newVersionTips = string.Format(newVersionTips, fr.Version);
MessageBox.Show(newVersionTips); MessageBox.Show(newVersionTips);
Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}"); Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
} }
@ -90,7 +95,7 @@ namespace Wox.Core
} }
/// https://github.com/Squirrel/Squirrel.Windows/blob/master/src/Squirrel/UpdateManager.Factory.cs /// https://github.com/Squirrel/Squirrel.Windows/blob/master/src/Squirrel/UpdateManager.Factory.cs
private static async Task<UpdateManager> GitHubUpdateManager(string repository) private async Task<UpdateManager> GitHubUpdateManager(string repository)
{ {
var uri = new Uri(repository); var uri = new Uri(repository);
var api = $"https://api.github.com/repos{uri.AbsolutePath}/releases"; var api = $"https://api.github.com/repos{uri.AbsolutePath}/releases";
@ -109,7 +114,7 @@ namespace Wox.Core
return manager; return manager;
} }
public static string NewVersinoTips(string version) public string NewVersinoTips(string version)
{ {
var translater = InternationalizationManager.Instance; var translater = InternationalizationManager.Instance;
var tips = string.Format(translater.GetTranslation("newVersionTips"), version); var tips = string.Format(translater.GetTranslation("newVersionTips"), version);

View file

@ -36,64 +36,8 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="DeltaCompressionDotNet, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1d14d6e5194e7f4a, processorArchitecture=MSIL">
<HintPath>..\packages\DeltaCompressionDotNet.1.0.0\lib\net45\DeltaCompressionDotNet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="DeltaCompressionDotNet.MsDelta, Version=1.0.0.0, Culture=neutral, PublicKeyToken=46b2138a390abf55, processorArchitecture=MSIL">
<HintPath>..\packages\DeltaCompressionDotNet.1.0.0\lib\net45\DeltaCompressionDotNet.MsDelta.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="DeltaCompressionDotNet.PatchApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3e8888ee913ed789, processorArchitecture=MSIL">
<HintPath>..\packages\DeltaCompressionDotNet.1.0.0\lib\net45\DeltaCompressionDotNet.PatchApi.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\squirrel.windows.1.5.2\lib\Net45\ICSharpCode.SharpZipLib.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="JetBrains.Annotations, Version=10.3.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>..\packages\JetBrains.Annotations.10.3.0\lib\net\JetBrains.Annotations.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Mdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Mdb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Pdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Pdb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Rocks, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\squirrel.windows.1.5.2\lib\Net45\NuGet.Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
<Reference Include="PropertyChanged, Version=1.51.0.0, Culture=neutral, PublicKeyToken=ee3ee20bcf148ddd, processorArchitecture=MSIL">
<HintPath>..\packages\PropertyChanged.Fody.1.51.0\lib\dotnet\PropertyChanged.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Splat, Version=1.6.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Splat.1.6.2\lib\Net45\Splat.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Squirrel, Version=1.5.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\squirrel.windows.1.5.2\lib\Net45\Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
@ -128,7 +72,6 @@
<Compile Include="Resource\Theme.cs" /> <Compile Include="Resource\Theme.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="Plugin\README.md" /> <None Include="Plugin\README.md" />
<None Include="README.md" /> <None Include="README.md" />
</ItemGroup> </ItemGroup>
@ -145,14 +88,31 @@
<ItemGroup> <ItemGroup>
<Content Include="FodyWeavers.xml" /> <Content Include="FodyWeavers.xml" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Fody">
<Version>1.29.2</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>9.0.1</Version>
</PackageReference>
<PackageReference Include="PropertyChanged.Fody">
<Version>1.51.0</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="squirrel.windows">
<Version>1.5.2</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Fody.1.29.2\build\dotnet\Fody.targets" Condition="Exists('..\packages\Fody.1.29.2\build\dotnet\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Fody.1.29.2\build\dotnet\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.1.29.2\build\dotnet\Fody.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View file

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DeltaCompressionDotNet" version="1.0.0" targetFramework="net452" />
<package id="Fody" version="1.29.2" targetFramework="net452" developmentDependency="true" />
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="Mono.Cecil" version="0.9.6.1" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="PropertyChanged.Fody" version="1.51.0" targetFramework="net452" developmentDependency="true" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net452" />
<package id="Splat" version="1.6.2" targetFramework="net452" />
<package id="squirrel.windows" version="1.5.2" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -2,49 +2,95 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using hyjiacan.util.p4n; using hyjiacan.util.p4n;
using hyjiacan.util.p4n.format; using hyjiacan.util.p4n.format;
using JetBrains.Annotations;
using Wox.Infrastructure.Logger; using Wox.Infrastructure.Logger;
using Wox.Infrastructure.Storage; using Wox.Infrastructure.Storage;
using Wox.Infrastructure.UserSettings; using Wox.Infrastructure.UserSettings;
namespace Wox.Infrastructure namespace Wox.Infrastructure
{ {
public static class Alphabet public interface IAlphabet
{ {
private static readonly HanyuPinyinOutputFormat Format = new HanyuPinyinOutputFormat(); string Translate(string stringToTranslate);
private static ConcurrentDictionary<string, string[][]> PinyinCache; }
private static BinaryStorage<ConcurrentDictionary<string, string[][]>> _pinyinStorage;
private static Settings _settings;
public static void Initialize(Settings settings) public class Alphabet : IAlphabet
{
private readonly HanyuPinyinOutputFormat Format = new HanyuPinyinOutputFormat();
private ConcurrentDictionary<string, string[][]> PinyinCache;
private BinaryStorage<ConcurrentDictionary<string, string[][]>> _pinyinStorage;
private Settings _settings;
public void Initialize([NotNull] Settings settings)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
InitializePinyinHelpers();
}
private void InitializePinyinHelpers()
{ {
_settings = settings;
Format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); Format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
Stopwatch.Normal("|Wox.Infrastructure.Alphabet.Initialize|Preload pinyin cache", () => Stopwatch.Normal("|Wox.Infrastructure.Alphabet.Initialize|Preload pinyin cache", () =>
{ {
_pinyinStorage = new BinaryStorage<ConcurrentDictionary<string, string[][]>>("Pinyin"); _pinyinStorage = new BinaryStorage<ConcurrentDictionary<string, string[][]>>("Pinyin");
PinyinCache = _pinyinStorage.TryLoad(new ConcurrentDictionary<string, string[][]>()); PinyinCache = _pinyinStorage.TryLoad(new ConcurrentDictionary<string, string[][]>());
// force pinyin library static constructor initialize // force pinyin library static constructor initialize
PinyinHelper.toHanyuPinyinStringArray('T', Format); PinyinHelper.toHanyuPinyinStringArray('T', Format);
}); });
Log.Info($"|Wox.Infrastructure.Alphabet.Initialize|Number of preload pinyin combination<{PinyinCache.Count}>"); Log.Info($"|Wox.Infrastructure.Alphabet.Initialize|Number of preload pinyin combination<{PinyinCache.Count}>");
} }
public static void Save() public string Translate(string str)
{ {
return ConvertChineseCharactersToPinyin(str);
}
public string ConvertChineseCharactersToPinyin(string source)
{
if (!_settings.ShouldUsePinyin)
return source;
if (string.IsNullOrEmpty(source))
return source;
if (!ContainsChinese(source))
return source;
var combination = PinyinCombination(source);
var pinyinArray=combination.Select(x => string.Join("", x));
var acronymArray = combination.Select(Acronym).Distinct();
var joinedSingleStringCombination = new StringBuilder();
var all = acronymArray.Concat(pinyinArray);
all.ToList().ForEach(x => joinedSingleStringCombination.Append(x));
return joinedSingleStringCombination.ToString();
}
public void Save()
{
if (!_settings.ShouldUsePinyin)
{
return;
}
_pinyinStorage.Save(PinyinCache); _pinyinStorage.Save(PinyinCache);
} }
private static string[] EmptyStringArray = new string[0]; private static string[] EmptyStringArray = new string[0];
private static string[][] Empty2DStringArray = new string[0][]; private static string[][] Empty2DStringArray = new string[0][];
[Obsolete("Not accurate, eg 音乐 will not return yinyue but returns yinle ")]
/// <summary> /// <summary>
/// replace chinese character with pinyin, non chinese character won't be modified /// replace chinese character with pinyin, non chinese character won't be modified
/// <param name="word"> should be word or sentence, instead of single character. e.g. 微软 </param> /// <param name="word"> should be word or sentence, instead of single character. e.g. 微软 </param>
/// </summary> /// </summary>
public static string[] Pinyin(string word) public string[] Pinyin(string word)
{ {
if (!_settings.ShouldUsePinyin) if (!_settings.ShouldUsePinyin)
{ {
@ -66,51 +112,48 @@ namespace Wox.Infrastructure
/// e.g. 音乐 will return yinyue and yinle /// e.g. 音乐 will return yinyue and yinle
/// <param name="characters"> should be word or sentence, instead of single character. e.g. 微软 </param> /// <param name="characters"> should be word or sentence, instead of single character. e.g. 微软 </param>
/// </summmary> /// </summmary>
public static string[][] PinyinComination(string characters) public string[][] PinyinCombination(string characters)
{ {
if (_settings.ShouldUsePinyin && !string.IsNullOrEmpty(characters)) if (!_settings.ShouldUsePinyin || string.IsNullOrEmpty(characters))
{
if (!PinyinCache.ContainsKey(characters))
{
var allPinyins = new List<string[]>();
foreach (var c in characters)
{
var pinyins = PinyinHelper.toHanyuPinyinStringArray(c, Format);
if (pinyins != null)
{
var r = pinyins.Distinct().ToArray();
allPinyins.Add(r);
}
else
{
var r = new[] { c.ToString() };
allPinyins.Add(r);
}
}
var combination = allPinyins.Aggregate(Combination).Select(c => c.Split(';')).ToArray();
PinyinCache[characters] = combination;
return combination;
}
else
{
return PinyinCache[characters];
}
}
else
{ {
return Empty2DStringArray; return Empty2DStringArray;
} }
if (!PinyinCache.ContainsKey(characters))
{
var allPinyins = new List<string[]>();
foreach (var c in characters)
{
var pinyins = PinyinHelper.toHanyuPinyinStringArray(c, Format);
if (pinyins != null)
{
var r = pinyins.Distinct().ToArray();
allPinyins.Add(r);
}
else
{
var r = new[] { c.ToString() };
allPinyins.Add(r);
}
}
var combination = allPinyins.Aggregate(Combination).Select(c => c.Split(';')).ToArray();
PinyinCache[characters] = combination;
return combination;
}
else
{
return PinyinCache[characters];
}
} }
public static string Acronym(string[] pinyin) public string Acronym(string[] pinyin)
{ {
var acronym = string.Join("", pinyin.Select(p => p[0])); var acronym = string.Join("", pinyin.Select(p => p[0]));
return acronym; return acronym;
} }
public static bool ContainsChinese(string word) public bool ContainsChinese(string word)
{ {
if (!_settings.ShouldUsePinyin) if (!_settings.ShouldUsePinyin)
{ {
@ -128,7 +171,7 @@ namespace Wox.Infrastructure
return chinese; return chinese;
} }
private static string[] Combination(string[] array1, string[] array2) private string[] Combination(string[] array1, string[] array2)
{ {
if (!_settings.ShouldUsePinyin) if (!_settings.ShouldUsePinyin)
{ {
@ -142,7 +185,5 @@ namespace Wox.Infrastructure
).ToArray(); ).ToArray();
return combination; return combination;
} }
} }
} }

View file

@ -16,7 +16,7 @@ namespace Wox.Infrastructure
public static FuzzyMatcher Create(string query) public static FuzzyMatcher Create(string query)
{ {
return new FuzzyMatcher(query, StringMatcher.DefaultMatchOption); return new FuzzyMatcher(query, new MatchOption());
} }
public static FuzzyMatcher Create(string query, MatchOption opt) public static FuzzyMatcher Create(string query, MatchOption opt)
@ -26,7 +26,7 @@ namespace Wox.Infrastructure
public MatchResult Evaluate(string str) public MatchResult Evaluate(string str)
{ {
return StringMatcher.FuzzySearch(query, str, opt); return StringMatcher.Instance.FuzzyMatch(query, str, opt);
} }
} }
} }

View file

@ -32,7 +32,6 @@ namespace Wox.Infrastructure
public static void ValidateDataDirectory(string bundledDataDirectory, string dataDirectory) public static void ValidateDataDirectory(string bundledDataDirectory, string dataDirectory)
{ {
if (!Directory.Exists(dataDirectory)) if (!Directory.Exists(dataDirectory))
{ {
Directory.CreateDirectory(dataDirectory); Directory.CreateDirectory(dataDirectory);

View file

@ -13,6 +13,16 @@ namespace Wox.Infrastructure.Http
{ {
private const string UserAgent = @"Mozilla/5.0 (Trident/7.0; rv:11.0) like Gecko"; private const string UserAgent = @"Mozilla/5.0 (Trident/7.0; rv:11.0) like Gecko";
static Http()
{
// need to be added so it would work on a win10 machine
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
}
public static HttpProxy Proxy { private get; set; } public static HttpProxy Proxy { private get; set; }
public static IWebProxy WebProxy() public static IWebProxy WebProxy()
{ {

View file

@ -39,6 +39,19 @@ namespace Wox.Infrastructure.Image
var contains = _data.ContainsKey(key); var contains = _data.ContainsKey(key);
return contains; return contains;
} }
public int CacheSize()
{
return _data.Count;
}
/// <summary>
/// return the number of unique images in the cache (by reference not by checking images content)
/// </summary>
public int UniqueImagesInCache()
{
return _data.Values.Distinct().Count();
}
} }
} }

View file

@ -0,0 +1,49 @@
using System;
using System.IO;
using System.Security.Cryptography;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Wox.Infrastructure.Image
{
public interface IImageHashGenerator
{
string GetHashFromImage(ImageSource image);
}
public class ImageHashGenerator : IImageHashGenerator
{
public string GetHashFromImage(ImageSource imageSource)
{
if (!(imageSource is BitmapSource image))
{
return null;
}
try
{
using (var outStream = new MemoryStream())
{
// PngBitmapEncoder enc2 = new PngBitmapEncoder();
// enc2.Frames.Add(BitmapFrame.Create(tt));
var enc = new JpegBitmapEncoder();
var bitmapFrame = BitmapFrame.Create(image);
bitmapFrame.Freeze();
enc.Frames.Add(bitmapFrame);
enc.Save(outStream);
var byteArray = outStream.GetBuffer();
using (var sha1 = new SHA1CryptoServiceProvider())
{
var hash = Convert.ToBase64String(sha1.ComputeHash(byteArray));
return hash;
}
}
}
catch
{
return null;
}
}
}
}

View file

@ -14,6 +14,8 @@ namespace Wox.Infrastructure.Image
{ {
private static readonly ImageCache ImageCache = new ImageCache(); private static readonly ImageCache ImageCache = new ImageCache();
private static BinaryStorage<ConcurrentDictionary<string, int>> _storage; private static BinaryStorage<ConcurrentDictionary<string, int>> _storage;
private static readonly ConcurrentDictionary<string, string> GuidToKey = new ConcurrentDictionary<string, string>();
private static IImageHashGenerator _hashGenerator;
private static readonly string[] ImageExtensions = private static readonly string[] ImageExtensions =
@ -30,7 +32,8 @@ namespace Wox.Infrastructure.Image
public static void Initialize() public static void Initialize()
{ {
_storage = new BinaryStorage<ConcurrentDictionary<string, int>> ("Image"); _storage = new BinaryStorage<ConcurrentDictionary<string, int>>("Image");
_hashGenerator = new ImageHashGenerator();
ImageCache.Usage = _storage.TryLoad(new ConcurrentDictionary<string, int>()); ImageCache.Usage = _storage.TryLoad(new ConcurrentDictionary<string, int>());
foreach (var icon in new[] { Constant.DefaultIcon, Constant.ErrorIcon }) foreach (var icon in new[] { Constant.DefaultIcon, Constant.ErrorIcon })
@ -43,16 +46,12 @@ namespace Wox.Infrastructure.Image
{ {
Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () => Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () =>
{ {
ImageCache.Usage.AsParallel().Where(i => !ImageCache.ContainsKey(i.Key)).ForAll(i => ImageCache.Usage.AsParallel().ForAll(x =>
{ {
var img = Load(i.Key); Load(x.Key);
if (img != null)
{
ImageCache[i.Key] = img;
}
}); });
}); });
Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.Usage.Count}>"); Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.Usage.Count}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
}); });
} }
@ -61,31 +60,56 @@ namespace Wox.Infrastructure.Image
ImageCache.Cleanup(); ImageCache.Cleanup();
_storage.Save(ImageCache.Usage); _storage.Save(ImageCache.Usage);
} }
public static ImageSource Load(string path, bool loadFullImage = false) private class ImageResult
{
public ImageResult(ImageSource imageSource, ImageType imageType)
{
ImageSource = imageSource;
ImageType = imageType;
}
public ImageType ImageType { get; }
public ImageSource ImageSource { get; }
}
private enum ImageType
{
File,
Folder,
Data,
ImageFile,
Error,
Cache
}
private static ImageResult LoadInternal(string path, bool loadFullImage = false)
{ {
ImageSource image; ImageSource image;
ImageType type = ImageType.Error;
try try
{ {
if (string.IsNullOrEmpty(path)) if (string.IsNullOrEmpty(path))
{ {
return ImageCache[Constant.ErrorIcon]; return new ImageResult(ImageCache[Constant.ErrorIcon], ImageType.Error);
} }
if (ImageCache.ContainsKey(path)) if (ImageCache.ContainsKey(path))
{ {
return ImageCache[path]; return new ImageResult(ImageCache[path], ImageType.Cache);
} }
if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase)) if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
{ {
return new BitmapImage(new Uri(path)); var imageSource = new BitmapImage(new Uri(path));
imageSource.Freeze();
return new ImageResult(imageSource, ImageType.Data);
} }
if (!Path.IsPathRooted(path)) if (!Path.IsPathRooted(path))
{ {
path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path)); path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path));
} }
if (Directory.Exists(path)) if (Directory.Exists(path))
{ {
/* Directories can also have thumbnails instead of shell icons. /* Directories can also have thumbnails instead of shell icons.
@ -94,14 +118,17 @@ namespace Wox.Infrastructure.Image
* Wox responsibility. * Wox responsibility.
* - Solution: just load the icon * - Solution: just load the icon
*/ */
type = ImageType.Folder;
image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
Constant.ThumbnailSize, ThumbnailOptions.IconOnly); Constant.ThumbnailSize, ThumbnailOptions.IconOnly);
} }
else if (File.Exists(path)) else if (File.Exists(path))
{ {
var extension = Path.GetExtension(path).ToLower(); var extension = Path.GetExtension(path).ToLower();
if (ImageExtensions.Contains(extension)) if (ImageExtensions.Contains(extension))
{ {
type = ImageType.ImageFile;
if (loadFullImage) if (loadFullImage)
{ {
image = LoadFullImage(path); image = LoadFullImage(path);
@ -119,6 +146,7 @@ namespace Wox.Infrastructure.Image
} }
else else
{ {
type = ImageType.File;
image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize, image = WindowsThumbnailProvider.GetThumbnail(path, Constant.ThumbnailSize,
Constant.ThumbnailSize, ThumbnailOptions.None); Constant.ThumbnailSize, ThumbnailOptions.None);
} }
@ -128,17 +156,50 @@ namespace Wox.Infrastructure.Image
image = ImageCache[Constant.ErrorIcon]; image = ImageCache[Constant.ErrorIcon];
path = Constant.ErrorIcon; path = Constant.ErrorIcon;
} }
ImageCache[path] = image;
image.Freeze(); if (type != ImageType.Error)
{
image.Freeze();
}
} }
catch (System.Exception e) catch (System.Exception e)
{ {
Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path}", e); Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path}", e);
type = ImageType.Error;
image = ImageCache[Constant.ErrorIcon]; image = ImageCache[Constant.ErrorIcon];
ImageCache[path] = image; ImageCache[path] = image;
} }
return image; return new ImageResult(image, type);
}
private static bool EnableImageHash = true;
public static ImageSource Load(string path, bool loadFullImage = false)
{
var imageResult = LoadInternal(path, loadFullImage);
var img = imageResult.ImageSource;
if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache)
{ // we need to get image hash
string hash = EnableImageHash ? _hashGenerator.GetHashFromImage(img) : null;
if (hash != null)
{
if (GuidToKey.TryGetValue(hash, out string key))
{ // image already exists
img = ImageCache[key];
}
else
{ // new guid
GuidToKey[hash] = path;
}
}
// update cache
ImageCache[path] = img;
}
return img;
} }
private static BitmapImage LoadFullImage(string path) private static BitmapImage LoadFullImage(string path)

View file

@ -110,7 +110,6 @@ namespace Wox.Infrastructure.Image
try try
{ {
return Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); return Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
} }
finally finally

View file

@ -11,18 +11,20 @@ namespace Wox.Infrastructure.Logger
{ {
public const string DirectoryName = "Logs"; public const string DirectoryName = "Logs";
public static string CurrentLogDirectory { get; }
static Log() static Log()
{ {
var path = Path.Combine(Constant.DataDirectory, DirectoryName, Constant.Version); CurrentLogDirectory = Path.Combine(Constant.DataDirectory, DirectoryName, Constant.Version);
if (!Directory.Exists(path)) if (!Directory.Exists(CurrentLogDirectory))
{ {
Directory.CreateDirectory(path); Directory.CreateDirectory(CurrentLogDirectory);
} }
var configuration = new LoggingConfiguration(); var configuration = new LoggingConfiguration();
var target = new FileTarget(); var target = new FileTarget();
configuration.AddTarget("file", target); configuration.AddTarget("file", target);
target.FileName = path.Replace(@"\", "/") + "/${shortdate}.txt"; target.FileName = CurrentLogDirectory.Replace(@"\", "/") + "/${shortdate}.txt";
#if DEBUG #if DEBUG
var rule = new LoggingRule("*", LogLevel.Debug, target); var rule = new LoggingRule("*", LogLevel.Debug, target);
#else #else
@ -47,8 +49,62 @@ namespace Wox.Infrastructure.Logger
return valid; return valid;
} }
/// <param name="message">example: "|prefix|unprefixed" </param>
public static void Error(string message)
[MethodImpl(MethodImplOptions.Synchronized)]
public static void Exception(string className, string message, System.Exception exception, [CallerMemberName] string methodName = "")
{
var classNameWithMethod = CheckClassAndMessageAndReturnFullClassWithMethod(className, message, methodName);
ExceptionInternal(classNameWithMethod, message, exception);
}
private static string CheckClassAndMessageAndReturnFullClassWithMethod(string className, string message,
string methodName)
{
if (string.IsNullOrWhiteSpace(className))
{
LogFaultyFormat($"Fail to specify a class name during logging of message: {message ?? "no message entered"}");
}
if (string.IsNullOrWhiteSpace(message))
{
// todo: not sure we really need that
LogFaultyFormat($"Fail to specify a message during logging");
}
if (!string.IsNullOrWhiteSpace(methodName))
{
return className + "." + methodName;
}
return className;
}
private static void ExceptionInternal(string classAndMethod, string message, System.Exception e)
{
var logger = LogManager.GetLogger(classAndMethod);
System.Diagnostics.Debug.WriteLine($"ERROR|{message}");
logger.Error("-------------------------- Begin exception --------------------------");
logger.Error(message);
do
{
logger.Error($"Exception full name:\n <{e.GetType().FullName}>");
logger.Error($"Exception message:\n <{e.Message}>");
logger.Error($"Exception stack trace:\n <{e.StackTrace}>");
logger.Error($"Exception source:\n <{e.Source}>");
logger.Error($"Exception target site:\n <{e.TargetSite}>");
logger.Error($"Exception HResult:\n <{e.HResult}>");
e = e.InnerException;
} while (e != null);
logger.Error("-------------------------- End exception --------------------------");
}
private static void LogInternal(string message, LogLevel level)
{ {
if (FormatValid(message)) if (FormatValid(message))
{ {
@ -57,8 +113,8 @@ namespace Wox.Infrastructure.Logger
var unprefixed = parts[2]; var unprefixed = parts[2];
var logger = LogManager.GetLogger(prefix); var logger = LogManager.GetLogger(prefix);
System.Diagnostics.Debug.WriteLine($"ERROR|{message}"); System.Diagnostics.Debug.WriteLine($"{level.Name}|{message}");
logger.Error(unprefixed); logger.Log(level, unprefixed);
} }
else else
{ {
@ -78,25 +134,7 @@ namespace Wox.Infrastructure.Logger
var parts = message.Split('|'); var parts = message.Split('|');
var prefix = parts[1]; var prefix = parts[1];
var unprefixed = parts[2]; var unprefixed = parts[2];
var logger = LogManager.GetLogger(prefix); ExceptionInternal(prefix, unprefixed, e);
System.Diagnostics.Debug.WriteLine($"ERROR|{message}");
logger.Error("-------------------------- Begin exception --------------------------");
logger.Error(unprefixed);
do
{
logger.Error($"Exception fulle name:\n <{e.GetType().FullName}>");
logger.Error($"Exception message:\n <{e.Message}>");
logger.Error($"Exception stack trace:\n <{e.StackTrace}>");
logger.Error($"Exception source:\n <{e.Source}>");
logger.Error($"Exception target site:\n <{e.TargetSite}>");
logger.Error($"Exception HResult:\n <{e.HResult}>");
e = e.InnerException;
} while (e != null);
logger.Error("-------------------------- End exception --------------------------");
} }
else else
{ {
@ -104,62 +142,59 @@ namespace Wox.Infrastructure.Logger
} }
#endif #endif
} }
/// <param name="message">example: "|prefix|unprefixed" </param>
public static void Error(string message)
{
LogInternal(message, LogLevel.Error);
}
public static void Error(string className, string message, [CallerMemberName] string methodName = "")
{
LogInternal(LogLevel.Error, className, message, methodName);
}
private static void LogInternal(LogLevel level, string className, string message, [CallerMemberName] string methodName = "")
{
var classNameWithMethod = CheckClassAndMessageAndReturnFullClassWithMethod(className, message, methodName);
var logger = LogManager.GetLogger(classNameWithMethod);
System.Diagnostics.Debug.WriteLine($"{level.Name}|{message}");
logger.Log(level, message);
}
public static void Debug(string className, string message, [CallerMemberName] string methodName = "")
{
LogInternal(LogLevel.Debug, className, message, methodName);
}
/// <param name="message">example: "|prefix|unprefixed" </param> /// <param name="message">example: "|prefix|unprefixed" </param>
public static void Debug(string message) public static void Debug(string message)
{ {
if (FormatValid(message)) LogInternal(message, LogLevel.Debug);
{ }
var parts = message.Split('|');
var prefix = parts[1];
var unprefixed = parts[2];
var logger = LogManager.GetLogger(prefix);
System.Diagnostics.Debug.WriteLine($"DEBUG|{message}"); public static void Info(string className, string message, [CallerMemberName] string methodName = "")
logger.Debug(unprefixed); {
} LogInternal(LogLevel.Info, className, message, methodName);
else
{
LogFaultyFormat(message);
}
} }
/// <param name="message">example: "|prefix|unprefixed" </param> /// <param name="message">example: "|prefix|unprefixed" </param>
public static void Info(string message) public static void Info(string message)
{ {
if (FormatValid(message)) LogInternal(message, LogLevel.Info);
{ }
var parts = message.Split('|');
var prefix = parts[1];
var unprefixed = parts[2];
var logger = LogManager.GetLogger(prefix);
System.Diagnostics.Debug.WriteLine($"INFO|{message}"); public static void Warn(string className, string message, [CallerMemberName] string methodName = "")
logger.Info(unprefixed); {
} LogInternal(LogLevel.Warn, className, message, methodName);
else
{
LogFaultyFormat(message);
}
} }
/// <param name="message">example: "|prefix|unprefixed" </param> /// <param name="message">example: "|prefix|unprefixed" </param>
public static void Warn(string message) public static void Warn(string message)
{ {
if (FormatValid(message)) LogInternal(message, LogLevel.Warn);
{
var parts = message.Split('|');
var prefix = parts[1];
var unprefixed = parts[2];
var logger = LogManager.GetLogger(prefix);
System.Diagnostics.Debug.WriteLine($"WARN|{message}");
logger.Warn(unprefixed);
}
else
{
LogFaultyFormat(message);
}
} }
} }
} }

View file

@ -1,108 +1,220 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Text; using static Wox.Infrastructure.StringMatcher;
using Wox.Infrastructure.Logger;
using Wox.Infrastructure.UserSettings;
namespace Wox.Infrastructure namespace Wox.Infrastructure
{ {
public static class StringMatcher public class StringMatcher
{ {
public static MatchOption DefaultMatchOption = new MatchOption(); private readonly MatchOption _defaultMatchOption = new MatchOption();
public static string UserSettingSearchPrecision { get; set; } public SearchPrecisionScore UserSettingSearchPrecision { get; set; }
private readonly IAlphabet _alphabet;
public StringMatcher(IAlphabet alphabet = null)
{
_alphabet = alphabet;
}
public static StringMatcher Instance { get; internal set; }
[Obsolete("This method is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")] [Obsolete("This method is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")]
public static int Score(string source, string target) public static int Score(string source, string target)
{ {
if (!string.IsNullOrEmpty(source) && !string.IsNullOrEmpty(target)) return FuzzySearch(target, source).Score;
{
return FuzzySearch(target, source, DefaultMatchOption).Score;
}
else
{
return 0;
}
} }
[Obsolete("This method is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")] [Obsolete("This method is obsolete and should not be used. Please use the static function StringMatcher.FuzzySearch")]
public static bool IsMatch(string source, string target) public static bool IsMatch(string source, string target)
{ {
return FuzzySearch(target, source, DefaultMatchOption).Score > 0; return Score(source, target) > 0;
} }
public static MatchResult FuzzySearch(string query, string stringToCompare) public static MatchResult FuzzySearch(string query, string stringToCompare)
{ {
return FuzzySearch(query, stringToCompare, DefaultMatchOption); return Instance.FuzzyMatch(query, stringToCompare);
}
public MatchResult FuzzyMatch(string query, string stringToCompare)
{
return FuzzyMatch(query, stringToCompare, _defaultMatchOption);
} }
/// <summary> /// <summary>
/// refer to https://github.com/mattyork/fuzzy /// Current method:
/// Character matching + substring matching;
/// 1. Query search string is split into substrings, separator is whitespace.
/// 2. Check each query substring's characters against full compare string,
/// 3. if a character in the substring is matched, loop back to verify the previous character.
/// 4. If previous character also matches, and is the start of the substring, update list.
/// 5. Once the previous character is verified, move on to the next character in the query substring.
/// 6. Move onto the next substring's characters until all substrings are checked.
/// 7. Consider success and move onto scoring if every char or substring without whitespaces matched
/// </summary> /// </summary>
public static MatchResult FuzzySearch(string query, string stringToCompare, MatchOption opt) public MatchResult FuzzyMatch(string query, string stringToCompare, MatchOption opt)
{ {
if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query)) return new MatchResult { Success = false }; if (string.IsNullOrEmpty(stringToCompare) || string.IsNullOrEmpty(query)) return new MatchResult (false, UserSettingSearchPrecision);
query = query.Trim(); query = query.Trim();
var len = stringToCompare.Length; if (_alphabet != null)
var compareString = opt.IgnoreCase ? stringToCompare.ToLower() : stringToCompare; {
var pattern = opt.IgnoreCase ? query.ToLower() : query; query = _alphabet.Translate(query);
stringToCompare = _alphabet.Translate(stringToCompare);
}
var fullStringToCompareWithoutCase = opt.IgnoreCase ? stringToCompare.ToLower() : stringToCompare;
var queryWithoutCase = opt.IgnoreCase ? query.ToLower() : query;
var querySubstrings = queryWithoutCase.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
int currentQuerySubstringIndex = 0;
var currentQuerySubstring = querySubstrings[currentQuerySubstringIndex];
var currentQuerySubstringCharacterIndex = 0;
var sb = new StringBuilder(stringToCompare.Length + (query.Length * (opt.Prefix.Length + opt.Suffix.Length)));
var patternIdx = 0;
var firstMatchIndex = -1; var firstMatchIndex = -1;
var firstMatchIndexInWord = -1;
var lastMatchIndex = 0; var lastMatchIndex = 0;
char ch; bool allQuerySubstringsMatched = false;
for (var idx = 0; idx < len; idx++) bool matchFoundInPreviousLoop = false;
bool allSubstringsContainedInCompareString = true;
var indexList = new List<int>();
for (var compareStringIndex = 0; compareStringIndex < fullStringToCompareWithoutCase.Length; compareStringIndex++)
{ {
ch = stringToCompare[idx]; if (fullStringToCompareWithoutCase[compareStringIndex] != currentQuerySubstring[currentQuerySubstringCharacterIndex])
if (compareString[idx] == pattern[patternIdx])
{ {
if (firstMatchIndex < 0) matchFoundInPreviousLoop = false;
firstMatchIndex = idx; continue;
lastMatchIndex = idx + 1;
sb.Append(opt.Prefix + ch + opt.Suffix);
patternIdx += 1;
}
else
{
sb.Append(ch);
} }
// match success, append remain char if (firstMatchIndex < 0)
if (patternIdx == pattern.Length && (idx + 1) != compareString.Length)
{ {
sb.Append(stringToCompare.Substring(idx + 1)); // first matched char will become the start of the compared string
break; firstMatchIndex = compareStringIndex;
}
if (currentQuerySubstringCharacterIndex == 0)
{
// first letter of current word
matchFoundInPreviousLoop = true;
firstMatchIndexInWord = compareStringIndex;
}
else if (!matchFoundInPreviousLoop)
{
// we want to verify that there is not a better match if this is not a full word
// in order to do so we need to verify all previous chars are part of the pattern
var startIndexToVerify = compareStringIndex - currentQuerySubstringCharacterIndex;
if (AllPreviousCharsMatched(startIndexToVerify, currentQuerySubstringCharacterIndex, fullStringToCompareWithoutCase, currentQuerySubstring))
{
matchFoundInPreviousLoop = true;
// if it's the beginning character of the first query substring that is matched then we need to update start index
firstMatchIndex = currentQuerySubstringIndex == 0 ? startIndexToVerify : firstMatchIndex;
indexList = GetUpdatedIndexList(startIndexToVerify, currentQuerySubstringCharacterIndex, firstMatchIndexInWord, indexList);
}
}
lastMatchIndex = compareStringIndex + 1;
indexList.Add(compareStringIndex);
currentQuerySubstringCharacterIndex++;
// if finished looping through every character in the current substring
if (currentQuerySubstringCharacterIndex == currentQuerySubstring.Length)
{
// if any of the substrings was not matched then consider as all are not matched
allSubstringsContainedInCompareString = matchFoundInPreviousLoop && allSubstringsContainedInCompareString;
currentQuerySubstringIndex++;
allQuerySubstringsMatched = AllQuerySubstringsMatched(currentQuerySubstringIndex, querySubstrings.Length);
if (allQuerySubstringsMatched)
break;
// otherwise move to the next query substring
currentQuerySubstring = querySubstrings[currentQuerySubstringIndex];
currentQuerySubstringCharacterIndex = 0;
} }
} }
// return rendered string if we have a match for every char // proceed to calculate score if every char or substring without whitespaces matched
if (patternIdx == pattern.Length) if (allQuerySubstringsMatched)
{ {
return new MatchResult var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex, lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
{
Success = true, return new MatchResult(true, UserSettingSearchPrecision, indexList, score);
Value = sb.ToString(),
Score = CalScore(query, stringToCompare, firstMatchIndex, lastMatchIndex - firstMatchIndex)
};
} }
return new MatchResult { Success = false }; return new MatchResult (false, UserSettingSearchPrecision);
} }
private static int CalScore(string query, string stringToCompare, int firstIndex, int matchLen) private static bool AllPreviousCharsMatched(int startIndexToVerify, int currentQuerySubstringCharacterIndex,
string fullStringToCompareWithoutCase, string currentQuerySubstring)
{ {
//a match found near the beginning of a string is scored more than a match found near the end var allMatch = true;
//a match is scored more if the characters in the patterns are closer to each other, while the score is lower if they are more spread out for (int indexToCheck = 0; indexToCheck < currentQuerySubstringCharacterIndex; indexToCheck++)
{
if (fullStringToCompareWithoutCase[startIndexToVerify + indexToCheck] !=
currentQuerySubstring[indexToCheck])
{
allMatch = false;
}
}
return allMatch;
}
private static List<int> GetUpdatedIndexList(int startIndexToVerify, int currentQuerySubstringCharacterIndex, int firstMatchIndexInWord, List<int> indexList)
{
var updatedList = new List<int>();
indexList.RemoveAll(x => x >= firstMatchIndexInWord);
updatedList.AddRange(indexList);
for (int indexToCheck = 0; indexToCheck < currentQuerySubstringCharacterIndex; indexToCheck++)
{
updatedList.Add(startIndexToVerify + indexToCheck);
}
return updatedList;
}
private static bool AllQuerySubstringsMatched(int currentQuerySubstringIndex, int querySubstringsLength)
{
return currentQuerySubstringIndex >= querySubstringsLength;
}
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen, bool allSubstringsContainedInCompareString)
{
// A match found near the beginning of a string is scored more than a match found near the end
// A match is scored more if the characters in the patterns are closer to each other,
// while the score is lower if they are more spread out
var score = 100 * (query.Length + 1) / ((1 + firstIndex) + (matchLen + 1)); var score = 100 * (query.Length + 1) / ((1 + firstIndex) + (matchLen + 1));
//a match with less characters assigning more weights
// A match with less characters assigning more weights
if (stringToCompare.Length - query.Length < 5) if (stringToCompare.Length - query.Length < 5)
{
score += 20; score += 20;
}
else if (stringToCompare.Length - query.Length < 10) else if (stringToCompare.Length - query.Length < 10)
{
score += 10; score += 10;
}
if (allSubstringsContainedInCompareString)
{
int count = query.Count(c => !char.IsWhiteSpace(c));
int factor = count < 4 ? 10 : 5;
score += factor * count;
}
return score; return score;
} }
@ -113,77 +225,83 @@ namespace Wox.Infrastructure
Low = 20, Low = 20,
None = 0 None = 0
} }
public static bool IsSearchPrecisionScoreMet(this MatchResult matchResult)
{
var precisionScore = (SearchPrecisionScore)Enum.Parse(typeof(SearchPrecisionScore),
UserSettingSearchPrecision ?? SearchPrecisionScore.Regular.ToString());
return matchResult.Score >= (int)precisionScore;
}
public static int ScoreAfterSearchPrecisionFilter(this MatchResult matchResult)
{
return matchResult.IsSearchPrecisionScoreMet() ? matchResult.Score : 0;
}
public static int ScoreForPinyin(string source, string target)
{
if (!string.IsNullOrEmpty(source) && !string.IsNullOrEmpty(target))
{
if (Alphabet.ContainsChinese(source))
{
var combination = Alphabet.PinyinComination(source);
var pinyinScore = combination
.Select(pinyin => FuzzySearch(target, string.Join("", pinyin)).Score)
.Max();
var acronymScore = combination.Select(Alphabet.Acronym)
.Select(pinyin => FuzzySearch(target, pinyin).Score)
.Max();
var score = Math.Max(pinyinScore, acronymScore);
return score;
}
else
{
return 0;
}
}
else
{
return 0;
}
}
} }
public class MatchResult public class MatchResult
{ {
public MatchResult(bool success, SearchPrecisionScore searchPrecision)
{
Success = success;
SearchPrecision = searchPrecision;
}
public MatchResult(bool success, SearchPrecisionScore searchPrecision, List<int> matchData, int rawScore)
{
Success = success;
SearchPrecision = searchPrecision;
MatchData = matchData;
RawScore = rawScore;
}
public bool Success { get; set; } public bool Success { get; set; }
public int Score { get; set; }
/// <summary> /// <summary>
/// highlight string /// The final score of the match result with search precision filters applied.
/// </summary> /// </summary>
public string Value { get; set; } public int Score { get; private set; }
/// <summary>
/// The raw calculated search score without any search precision filtering applied.
/// </summary>
private int _rawScore;
public int RawScore
{
get { return _rawScore; }
set
{
_rawScore = value;
Score = ScoreAfterSearchPrecisionFilter(_rawScore);
}
}
/// <summary>
/// Matched data to highlight.
/// </summary>
public List<int> MatchData { get; set; }
public SearchPrecisionScore SearchPrecision { get; set; }
public bool IsSearchPrecisionScoreMet()
{
return IsSearchPrecisionScoreMet(_rawScore);
}
private bool IsSearchPrecisionScoreMet(int rawScore)
{
return rawScore >= (int)SearchPrecision;
}
private int ScoreAfterSearchPrecisionFilter(int rawScore)
{
return IsSearchPrecisionScoreMet(rawScore) ? rawScore : 0;
}
} }
public class MatchOption public class MatchOption
{ {
public MatchOption()
{
Prefix = "";
Suffix = "";
IgnoreCase = true;
}
/// <summary> /// <summary>
/// prefix of match char, use for hightlight /// prefix of match char, use for hightlight
/// </summary> /// </summary>
public string Prefix { get; set; } [Obsolete("this is never used")]
public string Prefix { get; set; } = "";
/// <summary> /// <summary>
/// suffix of match char, use for hightlight /// suffix of match char, use for hightlight
/// </summary> /// </summary>
public string Suffix { get; set; } [Obsolete("this is never used")]
public string Suffix { get; set; } = "";
public bool IgnoreCase { get; set; } public bool IgnoreCase { get; set; } = true;
} }
} }

View file

@ -21,19 +21,38 @@ namespace Wox.Infrastructure.UserSettings
public string ResultFontWeight { get; set; } public string ResultFontWeight { get; set; }
public string ResultFontStretch { get; set; } public string ResultFontStretch { get; set; }
/// <summary> /// <summary>
/// when false Alphabet static service will always return empty results /// when false Alphabet static service will always return empty results
/// </summary> /// </summary>
public bool ShouldUsePinyin { get; set; } = true; public bool ShouldUsePinyin { get; set; } = true;
private string _querySearchPrecision { get; set; } = StringMatcher.SearchPrecisionScore.Regular.ToString();
public string QuerySearchPrecision internal StringMatcher.SearchPrecisionScore QuerySearchPrecision { get; private set; } = StringMatcher.SearchPrecisionScore.Regular;
[JsonIgnore]
public string QuerySearchPrecisionString
{ {
get { return _querySearchPrecision; } get { return QuerySearchPrecision.ToString(); }
set set
{ {
_querySearchPrecision = value; try
StringMatcher.UserSettingSearchPrecision = value; {
var precisionScore = (StringMatcher.SearchPrecisionScore)Enum
.Parse(typeof(StringMatcher.SearchPrecisionScore), value);
QuerySearchPrecision = precisionScore;
StringMatcher.Instance.UserSettingSearchPrecision = precisionScore;
}
catch (ArgumentException e)
{
Logger.Log.Exception(nameof(Settings), "Failed to load QuerySearchPrecisionString value from Settings file", e);
QuerySearchPrecision = StringMatcher.SearchPrecisionScore.Regular;
StringMatcher.Instance.UserSettingSearchPrecision = StringMatcher.SearchPrecisionScore.Regular;
throw;
}
} }
} }

View file

@ -36,22 +36,6 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="JetBrains.Annotations, Version=10.3.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>..\packages\JetBrains.Annotations.10.3.0\lib\net\JetBrains.Annotations.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.2.0\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Pinyin4Net, Version=2016.4.24.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Pinyin4DotNet.2016.4.23.4\lib\net452\Pinyin4Net.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
<Reference Include="System" /> <Reference Include="System" />
@ -71,6 +55,7 @@
<Compile Include="Hotkey\InterceptKeys.cs" /> <Compile Include="Hotkey\InterceptKeys.cs" />
<Compile Include="Hotkey\KeyEvent.cs" /> <Compile Include="Hotkey\KeyEvent.cs" />
<Compile Include="Image\ImageCache.cs" /> <Compile Include="Image\ImageCache.cs" />
<Compile Include="Image\ImageHashGenerator.cs" />
<Compile Include="Image\ImageLoader.cs" /> <Compile Include="Image\ImageLoader.cs" />
<Compile Include="Image\ThumbnailReader.cs" /> <Compile Include="Image\ThumbnailReader.cs" />
<Compile Include="Logger\Log.cs" /> <Compile Include="Logger\Log.cs" />
@ -101,14 +86,31 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>9.0.1</Version>
</PackageReference>
<PackageReference Include="NLog">
<Version>4.2.0</Version>
</PackageReference>
<PackageReference Include="NLog.Schema" GeneratePathProperty="true">
<Version>4.2.0</Version>
</PackageReference>
<PackageReference Include="Pinyin4DotNet" GeneratePathProperty="true">
<Version>2016.4.23.4</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PreBuildEvent>cd $(SolutionDir)packages\Pinyin4DotNet*\pinyindb\ <PreBuildEvent>
xcopy /Y unicode_to_hanyu_pinyin.txt $(TargetDir)pinyindb\ xcopy /Y $(PkgPinyin4DotNet)\pinyindb\unicode_to_hanyu_pinyin.txt $(TargetDir)pinyindb\
cd $(SolutionDir)packages\NLog.Schema*\content\ xcopy /Y $(PkgNLog_Schema)\content\NLog.xsd $(TargetDir)
xcopy /Y NLog.xsd $(TargetDir)</PreBuildEvent> </PreBuildEvent>
</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.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View file

@ -29,7 +29,6 @@ namespace Wox.Infrastructure
public static readonly string DataDirectory = DetermineDataDirectory(); public static readonly string DataDirectory = DetermineDataDirectory();
public static readonly string PluginsDirectory = Path.Combine(DataDirectory, Plugins); public static readonly string PluginsDirectory = Path.Combine(DataDirectory, Plugins);
public static readonly string PreinstalledDirectory = Path.Combine(ProgramDirectory, Plugins); public static readonly string PreinstalledDirectory = Path.Combine(ProgramDirectory, Plugins);
public const string Repository = "https://github.com/Wox-launcher/Wox";
public const string Issue = "https://github.com/Wox-launcher/Wox/issues/new"; public const string Issue = "https://github.com/Wox-launcher/Wox/issues/new";
public static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location.NonNull()).ProductVersion; public static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location.NonNull()).ProductVersion;

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="NLog" version="4.2.0" targetFramework="net452" />
<package id="NLog.Schema" version="4.2.0" targetFramework="net452" />
<package id="Pinyin4DotNet" version="2016.4.23.4" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -1,11 +1,9 @@
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using JetBrains.Annotations; using JetBrains.Annotations;
using PropertyChanged;
namespace Wox.Plugin namespace Wox.Plugin
{ {
[ImplementPropertyChanged]
public class BaseModel : INotifyPropertyChanged public class BaseModel : INotifyPropertyChanged
{ {
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;

View file

@ -76,7 +76,7 @@ namespace Wox.Plugin
/// <param name="title">Message title</param> /// <param name="title">Message title</param>
/// <param name="subTitle">Message subtitle</param> /// <param name="subTitle">Message subtitle</param>
/// <param name="iconPath">Message icon path (relative path to your plugin folder)</param> /// <param name="iconPath">Message icon path (relative path to your plugin folder)</param>
void ShowMsg(string title, string subTitle = "", string iconPath = ""); void ShowMsg(string title, string subTitle = "", string iconPath = "", bool useMainWindowAsOwner = true);
/// <summary> /// <summary>
/// Open setting dialog /// Open setting dialog

View file

@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Newtonsoft.Json; using Newtonsoft.Json;
using PropertyChanged;
namespace Wox.Plugin namespace Wox.Plugin
{ {
[JsonObject(MemberSerialization.OptOut)] [JsonObject(MemberSerialization.OptOut)]
@ -46,6 +46,9 @@ namespace Wox.Plugin
[Obsolete("Use IcoPath")] [Obsolete("Use IcoPath")]
public string FullIcoPath => IcoPath; public string FullIcoPath => IcoPath;
/// <summary>
/// Init time include both plugin load time and init time
/// </summary>
[JsonIgnore] [JsonIgnore]
public long InitTime { get; set; } public long InitTime { get; set; }
[JsonIgnore] [JsonIgnore]

View file

@ -42,6 +42,16 @@ namespace Wox.Plugin
public int Score { get; set; } public int Score { get; set; }
/// <summary>
/// A list of indexes for the characters to be highlighted in Title
/// </summary>
public IList<int> TitleHighlightData { get; set; }
/// <summary>
/// A list of indexes for the characters to be highlighted in SubTitle
/// </summary>
public IList<int> SubTitleHighlightData { get; set; }
/// <summary> /// <summary>
/// Only resulsts that originQuery match with curren query will be displayed in the panel /// Only resulsts that originQuery match with curren query will be displayed in the panel
/// </summary> /// </summary>
@ -69,7 +79,9 @@ namespace Wox.Plugin
var equality = string.Equals(r?.Title, Title) && var equality = string.Equals(r?.Title, Title) &&
string.Equals(r?.SubTitle, SubTitle) && string.Equals(r?.SubTitle, SubTitle) &&
string.Equals(r?.IcoPath, IcoPath); string.Equals(r?.IcoPath, IcoPath) &&
TitleHighlightData == r.TitleHighlightData &&
SubTitleHighlightData == r.SubTitleHighlightData;
return equality; return equality;
} }

View file

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Wox.Plugin.SharedCommands
{
public static class ShellCommand
{
public delegate bool EnumThreadDelegate(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll")] static extern bool EnumThreadWindows(uint threadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport("user32.dll")] static extern int GetWindowText(IntPtr hwnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")] static extern int GetWindowTextLength(IntPtr hwnd);
private static bool containsSecurityWindow;
public static Process RunAsDifferentUser(ProcessStartInfo processStartInfo)
{
processStartInfo.Verb = "RunAsUser";
var process = Process.Start(processStartInfo);
containsSecurityWindow = false;
while (!containsSecurityWindow) // wait for windows to bring up the "Windows Security" dialog
{
CheckSecurityWindow();
Thread.Sleep(25);
}
while (containsSecurityWindow) // while this process contains a "Windows Security" dialog, stay open
{
containsSecurityWindow = false;
CheckSecurityWindow();
Thread.Sleep(50);
}
return process;
}
private static void CheckSecurityWindow()
{
ProcessThreadCollection ptc = Process.GetCurrentProcess().Threads;
for (int i = 0; i < ptc.Count; i++)
EnumThreadWindows((uint)ptc[i].Id, CheckSecurityThread, IntPtr.Zero);
}
private static bool CheckSecurityThread(IntPtr hwnd, IntPtr lParam)
{
if (GetWindowTitle(hwnd) == "Windows Security")
containsSecurityWindow = true;
return true;
}
private static string GetWindowTitle(IntPtr hwnd)
{
StringBuilder sb = new StringBuilder(GetWindowTextLength(hwnd) + 1);
GetWindowText(hwnd, sb, sb.Capacity);
return sb.ToString();
}
public static ProcessStartInfo SetProcessStartInfo(this string fileName, string workingDirectory = "", string arguments = "", string verb = "")
{
var info = new ProcessStartInfo
{
FileName = fileName,
WorkingDirectory = workingDirectory,
Arguments = arguments,
Verb = verb
};
return info;
}
}
}

View file

@ -36,20 +36,8 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="JetBrains.Annotations, Version=10.3.0.0, Culture=neutral, PublicKeyToken=1010a0d8d6380325, processorArchitecture=MSIL">
<HintPath>..\packages\JetBrains.Annotations.10.3.0\lib\net\JetBrains.Annotations.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
<Reference Include="PropertyChanged, Version=1.51.0.0, Culture=neutral, PublicKeyToken=ee3ee20bcf148ddd, processorArchitecture=MSIL">
<HintPath>..\packages\PropertyChanged.Fody.1.51.0\lib\dotnet\PropertyChanged.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Xaml" /> <Reference Include="System.Xaml" />
@ -78,23 +66,39 @@
<Compile Include="Result.cs" /> <Compile Include="Result.cs" />
<Compile Include="ActionContext.cs" /> <Compile Include="ActionContext.cs" />
<Compile Include="SharedCommands\SearchWeb.cs" /> <Compile Include="SharedCommands\SearchWeb.cs" />
<Compile Include="SharedCommands\ShellCommand.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" />
<None Include="README.md" /> <None Include="README.md" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="FodyWeavers.xml" /> <Content Include="FodyWeavers.xml" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup>
<PackageReference Include="Fody">
<Version>1.29.2</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="Mono.Cecil">
<Version>0.9.6.1</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>9.0.1</Version>
</PackageReference>
<PackageReference Include="PropertyChanged.Fody">
<Version>1.51.0</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Fody.1.29.2\build\dotnet\Fody.targets" Condition="Exists('..\packages\Fody.1.29.2\build\dotnet\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Fody.1.29.2\build\dotnet\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.1.29.2\build\dotnet\Fody.targets'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Fody" version="1.29.2" targetFramework="net452" developmentDependency="true" />
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="PropertyChanged.Fody" version="1.51.0" targetFramework="net452" developmentDependency="true" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

View file

@ -11,17 +11,25 @@ namespace Wox.Test
[TestFixture] [TestFixture]
public class FuzzyMatcherTest public class FuzzyMatcherTest
{ {
public List<string> GetSearchStrings() private const string Chrome = "Chrome";
private const string CandyCrushSagaFromKing = "Candy Crush Saga from King";
private const string HelpCureHopeRaiseOnMindEntityChrome = "Help cure hope raise on mind entity Chrome";
private const string UninstallOrChangeProgramsOnYourComputer = "Uninstall or change programs on your computer";
private const string LastIsChrome = "Last is chrome";
private const string OneOneOneOne = "1111";
private const string MicrosoftSqlServerManagementStudio = "Microsoft SQL Server Management Studio";
public List<string> GetSearchStrings()
=> new List<string> => new List<string>
{ {
"Chrome", Chrome,
"Choose which programs you want Windows to use for activities like web browsing, editing photos, sending e-mail, and playing music.", "Choose which programs you want Windows to use for activities like web browsing, editing photos, sending e-mail, and playing music.",
"Help cure hope raise on mind entity Chrome ", HelpCureHopeRaiseOnMindEntityChrome,
"Candy Crush Saga from King", CandyCrushSagaFromKing,
"Uninstall or change programs on your computer", UninstallOrChangeProgramsOnYourComputer,
"Add, change, and manage fonts on your computer", "Add, change, and manage fonts on your computer",
"Last is chrome", LastIsChrome,
"1111" OneOneOneOne
}; };
public List<int> GetPrecisionScores() public List<int> GetPrecisionScores()
@ -48,14 +56,14 @@ namespace Wox.Test
"aac" "aac"
}; };
var results = new List<Result>(); var results = new List<Result>();
var matcher = new StringMatcher();
foreach (var str in sources) foreach (var str in sources)
{ {
results.Add(new Result results.Add(new Result
{ {
Title = str, Title = str,
Score = StringMatcher.FuzzySearch("inst", str).Score Score = matcher.FuzzyMatch("inst", str).RawScore
}); });
} }
@ -71,30 +79,30 @@ namespace Wox.Test
public void WhenGivenNotAllCharactersFoundInSearchStringThenShouldReturnZeroScore(string searchString) public void WhenGivenNotAllCharactersFoundInSearchStringThenShouldReturnZeroScore(string searchString)
{ {
var compareString = "Can have rum only in my glass"; var compareString = "Can have rum only in my glass";
var matcher = new StringMatcher();
var scoreResult = StringMatcher.FuzzySearch(searchString, compareString).Score; var scoreResult = matcher.FuzzyMatch(searchString, compareString).RawScore;
Assert.True(scoreResult == 0); Assert.True(scoreResult == 0);
} }
[TestCase("chr")] [TestCase("chr")]
[TestCase("chrom")] [TestCase("chrom")]
[TestCase("chrome")] [TestCase("chrome")]
[TestCase("cand")] [TestCase("cand")]
[TestCase("cpywa")] [TestCase("cpywa")]
[TestCase("ccs")] [TestCase("ccs")]
public void WhenGivenStringsAndAppliedPrecisionFilteringThenShouldReturnGreaterThanPrecisionScoreResults(string searchTerm) public void WhenGivenStringsAndAppliedPrecisionFilteringThenShouldReturnGreaterThanPrecisionScoreResults(string searchTerm)
{ {
var results = new List<Result>(); var results = new List<Result>();
var matcher = new StringMatcher();
foreach (var str in GetSearchStrings()) foreach (var str in GetSearchStrings())
{ {
results.Add(new Result results.Add(new Result
{ {
Title = str, Title = str,
Score = StringMatcher.FuzzySearch(searchTerm, str).Score Score = matcher.FuzzyMatch(searchTerm, str).Score
}); });
} }
foreach (var precisionScore in GetPrecisionScores()) foreach (var precisionScore in GetPrecisionScores())
{ {
@ -114,76 +122,104 @@ namespace Wox.Test
} }
} }
[TestCase("chrome")] [TestCase(Chrome, Chrome, 137)]
public void WhenGivenStringsForCalScoreMethodThenShouldReturnCurrentScoring(string searchTerm) [TestCase(Chrome, LastIsChrome, 83)]
[TestCase(Chrome, HelpCureHopeRaiseOnMindEntityChrome, 21)]
[TestCase(Chrome, UninstallOrChangeProgramsOnYourComputer, 15)]
[TestCase(Chrome, CandyCrushSagaFromKing, 0)]
[TestCase("sql", MicrosoftSqlServerManagementStudio, 56)]
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, 79)]//double spacing intended
public void WhenGivenQueryStringThenShouldReturnCurrentScoring(string queryString, string compareString, int expectedScore)
{ {
var searchStrings = new List<string> // When, Given
{ var matcher = new StringMatcher();
"Chrome",//SCORE: 107 var rawScore = matcher.FuzzyMatch(queryString, compareString).RawScore;
"Last is chrome",//SCORE: 53
"Help cure hope raise on mind entity Chrome",//SCORE: 21
"Uninstall or change programs on your computer", //SCORE: 15
"Candy Crush Saga from King"//SCORE: 0
}
.OrderByDescending(x => x)
.ToList();
var results = new List<Result>(); // Should
Assert.AreEqual(expectedScore, rawScore, $"Expected score for compare string '{compareString}': {expectedScore}, Actual: {rawScore}");
foreach (var str in searchStrings)
{
results.Add(new Result
{
Title = str,
Score = StringMatcher.FuzzySearch(searchTerm, str).Score
});
}
var orderedResults = results.OrderByDescending(x => x.Title).ToList();
Debug.WriteLine("");
Debug.WriteLine("###############################################");
Debug.WriteLine("SEARCHTERM: " + searchTerm);
foreach (var item in orderedResults)
{
Debug.WriteLine("SCORE: " + item.Score.ToString() + ", FoundString: " + item.Title);
}
Debug.WriteLine("###############################################");
Debug.WriteLine("");
Assert.IsTrue(orderedResults[0].Score == 15 && orderedResults[0].Title == searchStrings[0]);
Assert.IsTrue(orderedResults[1].Score == 53 && orderedResults[1].Title == searchStrings[1]);
Assert.IsTrue(orderedResults[2].Score == 21 && orderedResults[2].Title == searchStrings[2]);
Assert.IsTrue(orderedResults[3].Score == 107 && orderedResults[3].Title == searchStrings[3]);
Assert.IsTrue(orderedResults[4].Score == 0 && orderedResults[4].Title == searchStrings[4]);
} }
[TestCase("goo", "Google Chrome", (int)StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("goo", "Google Chrome", StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("chr", "Google Chrome", (int)StringMatcher.SearchPrecisionScore.Low, true)] [TestCase("chr", "Google Chrome", StringMatcher.SearchPrecisionScore.Low, true)]
[TestCase("chr", "Chrome", (int)StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("chr", "Chrome", StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("chr", "Help cure hope raise on mind entity Chrome", (int)StringMatcher.SearchPrecisionScore.Regular, false)] [TestCase("chr", "Help cure hope raise on mind entity Chrome", StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("chr", "Help cure hope raise on mind entity Chrome", (int)StringMatcher.SearchPrecisionScore.Low, true)] [TestCase("chr", "Help cure hope raise on mind entity Chrome", StringMatcher.SearchPrecisionScore.Low, true)]
[TestCase("chr", "Candy Crush Saga from King", (int)StringMatcher.SearchPrecisionScore.Regular, false)] [TestCase("chr", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("chr", "Candy Crush Saga from King", (int)StringMatcher.SearchPrecisionScore.None, true)] [TestCase("chr", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.None, true)]
[TestCase("ccs", "Candy Crush Saga from King", (int)StringMatcher.SearchPrecisionScore.Low, true)] [TestCase("ccs", "Candy Crush Saga from King", StringMatcher.SearchPrecisionScore.Low, true)]
[TestCase("cand", "Candy Crush Saga from King", (int)StringMatcher.SearchPrecisionScore.Regular, true)] [TestCase("cand", "Candy Crush Saga from King",StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("cand", "Help cure hope raise on mind entity Chrome", (int)StringMatcher.SearchPrecisionScore.Regular, false)] [TestCase("cand", "Help cure hope raise on mind entity Chrome", StringMatcher.SearchPrecisionScore.Regular, false)]
public void WhenGivenDesiredPrecisionThenShouldReturnAllResultsGreaterOrEqual(string queryString, string compareString, public void WhenGivenDesiredPrecisionThenShouldReturnAllResultsGreaterOrEqual(
int expectedPrecisionScore, bool expectedPrecisionResult) string queryString,
string compareString,
StringMatcher.SearchPrecisionScore expectedPrecisionScore,
bool expectedPrecisionResult)
{ {
var expectedPrecisionString = (StringMatcher.SearchPrecisionScore)expectedPrecisionScore; // When
StringMatcher.UserSettingSearchPrecision = expectedPrecisionString.ToString(); var matcher = new StringMatcher {UserSettingSearchPrecision = expectedPrecisionScore};
var matchResult = StringMatcher.FuzzySearch(queryString, compareString);
// Given
var matchResult = matcher.FuzzyMatch(queryString, compareString);
Debug.WriteLine(""); Debug.WriteLine("");
Debug.WriteLine("###############################################"); Debug.WriteLine("###############################################");
Debug.WriteLine($"SearchTerm: {queryString} PrecisionLevelSetAt: {expectedPrecisionString} ({expectedPrecisionScore})"); Debug.WriteLine($"QueryString: {queryString} CompareString: {compareString}");
Debug.WriteLine($"SCORE: {matchResult.Score.ToString()}, ComparedString: {compareString}"); Debug.WriteLine($"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int)expectedPrecisionScore})");
Debug.WriteLine("###############################################"); Debug.WriteLine("###############################################");
Debug.WriteLine(""); Debug.WriteLine("");
var matchPrecisionResult = matchResult.IsSearchPrecisionScoreMet(); // Should
Assert.IsTrue(matchPrecisionResult == expectedPrecisionResult); Assert.AreEqual(expectedPrecisionResult, matchResult.IsSearchPrecisionScoreMet(),
$"Query:{queryString}{Environment.NewLine} " +
$"Compare:{compareString}{Environment.NewLine}" +
$"Raw Score: {matchResult.RawScore}{Environment.NewLine}" +
$"Precision Score: {(int)expectedPrecisionScore}");
}
[TestCase("exce", "OverLeaf-Latex: An online LaTeX editor", StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("term", "Windows Terminal (Preview)", StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sql s managa", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("sql' s manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("sql s manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sql", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sql serv", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sqlserv", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("sql servman", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("sql serv man", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("sql studio", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("mic", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("chr", "Shutdown", StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("mssms", MicrosoftSqlServerManagementStudio, StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("chr", "Change settings for text-to-speech and for speech recognition (if installed).", StringMatcher.SearchPrecisionScore.Regular, false)]
[TestCase("ch r", "Change settings for text-to-speech and for speech recognition (if installed).", StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("a test", "This is a test", StringMatcher.SearchPrecisionScore.Regular, true)]
[TestCase("test", "This is a test", StringMatcher.SearchPrecisionScore.Regular, true)]
public void WhenGivenQueryShouldReturnResultsContainingAllQuerySubstrings(
string queryString,
string compareString,
StringMatcher.SearchPrecisionScore expectedPrecisionScore,
bool expectedPrecisionResult)
{
// When
var matcher = new StringMatcher { UserSettingSearchPrecision = expectedPrecisionScore };
// Given
var matchResult = matcher.FuzzyMatch(queryString, compareString);
Debug.WriteLine("");
Debug.WriteLine("###############################################");
Debug.WriteLine($"QueryString: {queryString} CompareString: {compareString}");
Debug.WriteLine($"RAW SCORE: {matchResult.RawScore.ToString()}, PrecisionLevelSetAt: {expectedPrecisionScore} ({(int)expectedPrecisionScore})");
Debug.WriteLine("###############################################");
Debug.WriteLine("");
// Should
Assert.AreEqual(expectedPrecisionResult, matchResult.IsSearchPrecisionScoreMet(),
$"Query:{queryString}{Environment.NewLine} " +
$"Compare:{compareString}{Environment.NewLine}" +
$"Raw Score: {matchResult.RawScore}{Environment.NewLine}" +
$"Precision Score: {(int)expectedPrecisionScore}");
} }
} }
} }

View file

@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\NUnit3TestAdapter.3.15.1\build\net35\NUnit3TestAdapter.props" Condition="Exists('..\packages\NUnit3TestAdapter.3.15.1\build\net35\NUnit3TestAdapter.props')" />
<Import Project="..\packages\NUnit.3.12.0\build\NUnit.props" Condition="Exists('..\packages\NUnit.3.12.0\build\NUnit.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -38,13 +36,6 @@
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Moq, Version=4.2.1409.1722, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<HintPath>..\packages\Moq.4.2.1409.1722\lib\net40\Moq.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="nunit.framework, Version=3.12.0.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
<HintPath>..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
</ItemGroup> </ItemGroup>
@ -77,16 +68,17 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <PackageReference Include="Moq">
<Version>4.2.1409.1722</Version>
</PackageReference>
<PackageReference Include="NUnit">
<Version>3.12.0</Version>
</PackageReference>
<PackageReference Include="NUnit3TestAdapter">
<Version>3.15.1</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\NUnit.3.12.0\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit.3.12.0\build\NUnit.props'))" />
<Error Condition="!Exists('..\packages\NUnit3TestAdapter.3.15.1\build\net35\NUnit3TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NUnit3TestAdapter.3.15.1\build\net35\NUnit3TestAdapter.props'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Moq" version="4.2.1409.1722" targetFramework="net452" />
<package id="NUnit" version="3.12.0" targetFramework="net452" />
<package id="NUnit3TestAdapter" version="3.15.1" targetFramework="net452" />
</packages>

View file

@ -1,7 +1,19 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<configuration> <configuration>
<!--https://msdn.microsoft.com/en-us/library/dd409252(v=vs.110).aspx--> <!--https://msdn.microsoft.com/en-us/library/dd409252(v=vs.110).aspx-->
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="Wox.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
</configSections>
<runtime> <runtime>
<loadFromRemoteSources enabled="true"/> <loadFromRemoteSources enabled="true"/>
</runtime> </runtime>
<applicationSettings>
<Wox.Properties.Settings>
<setting name="GithubRepo" serializeAs="String">
<value>https://github.com/Wox-launcher/Wox</value>
</setting>
</Wox.Properties.Settings>
</applicationSettings>
</configuration> </configuration>

View file

@ -1,4 +1,4 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers; using System.Timers;
@ -25,6 +25,9 @@ namespace Wox
private Settings _settings; private Settings _settings;
private MainViewModel _mainVM; private MainViewModel _mainVM;
private SettingWindowViewModel _settingsVM; private SettingWindowViewModel _settingsVM;
private readonly Updater _updater = new Updater(Wox.Properties.Settings.Default.GithubRepo);
private readonly Alphabet _alphabet = new Alphabet();
private StringMatcher _stringMatcher;
[STAThread] [STAThread]
public static void Main() public static void Main()
@ -50,17 +53,18 @@ namespace Wox
ImageLoader.Initialize(); ImageLoader.Initialize();
_settingsVM = new SettingWindowViewModel(); _settingsVM = new SettingWindowViewModel(_updater);
_settings = _settingsVM.Settings; _settings = _settingsVM.Settings;
Alphabet.Initialize(_settings); _alphabet.Initialize(_settings);
_stringMatcher = new StringMatcher(_alphabet);
StringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision; StringMatcher.Instance = _stringMatcher;
_stringMatcher.UserSettingSearchPrecision = _settings.QuerySearchPrecision;
PluginManager.LoadPlugins(_settings.PluginSettings); PluginManager.LoadPlugins(_settings.PluginSettings);
_mainVM = new MainViewModel(_settings); _mainVM = new MainViewModel(_settings);
var window = new MainWindow(_settings, _mainVM); var window = new MainWindow(_settings, _mainVM);
API = new PublicAPIInstance(_settingsVM, _mainVM); API = new PublicAPIInstance(_settingsVM, _mainVM, _alphabet);
PluginManager.InitializePlugins(API); PluginManager.InitializePlugins(API);
Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}"); Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}");
@ -110,12 +114,12 @@ namespace Wox
var timer = new Timer(1000 * 60 * 60 * 5); var timer = new Timer(1000 * 60 * 60 * 5);
timer.Elapsed += async (s, e) => timer.Elapsed += async (s, e) =>
{ {
await Updater.UpdateApp(); await _updater.UpdateApp();
}; };
timer.Start(); timer.Start();
// check updates on startup // check updates on startup
await Updater.UpdateApp(); await _updater.UpdateApp();
} }
}); });
} }
@ -144,10 +148,6 @@ namespace Wox
private static void RegisterAppDomainExceptions() private static void RegisterAppDomainExceptions()
{ {
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.UnhandledExceptionHandle; AppDomain.CurrentDomain.UnhandledException += ErrorReporting.UnhandledExceptionHandle;
AppDomain.CurrentDomain.FirstChanceException += (_, e) =>
{
Log.Exception("|App.RegisterAppDomainExceptions|First Chance Exception:", e.Exception);
};
} }
public void Dispose() public void Dispose()
@ -156,13 +156,7 @@ namespace Wox
// but if sessionending is not called, exit won't be called when log off / shutdown // but if sessionending is not called, exit won't be called when log off / shutdown
if (!_disposed) if (!_disposed)
{ {
_mainVM.Save(); API.SaveAppAllSettings();
_settingsVM.Save();
PluginManager.Save();
ImageLoader.Save();
Alphabet.Save();
_disposed = true; _disposed = true;
} }
} }

View file

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using System.Windows.Documents;
namespace Wox.Converters
{
public class HighlightTextConverter : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, CultureInfo cultureInfo)
{
var text = value[0] as string;
var highlightData = value[1] as List<int>;
var textBlock = new Span();
if (highlightData == null || !highlightData.Any())
{
// No highlight data, just return the text
return new Run(text);
}
for (var i = 0; i < text.Length; i++)
{
var currentCharacter = text.Substring(i, 1);
if (this.ShouldHighlight(highlightData, i))
{
textBlock.Inlines.Add(new Bold(new Run(currentCharacter)));
}
else
{
textBlock.Inlines.Add(new Run(currentCharacter));
}
}
return textBlock;
}
public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
{
return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue };
}
private bool ShouldHighlight(List<int> highlightData, int index)
{
return highlightData.Contains(index);
}
}
}

View file

@ -87,4 +87,4 @@
</ContentControl> </ContentControl>
</StackPanel> </StackPanel>
</Border> </Border>
</Window> </Window>

View file

@ -12,7 +12,7 @@ namespace Wox.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.3.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@ -22,5 +22,14 @@ namespace Wox.Properties {
return defaultInstance; return defaultInstance;
} }
} }
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("https://github.com/Wox-launcher/Wox")]
public string GithubRepo {
get {
return ((string)(this["GithubRepo"]));
}
}
} }
} }

View file

@ -1,7 +1,9 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:_settings" CurrentProfile="(Default)"> <SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="Wox.Properties" GeneratedClassName="Settings">
<Profiles> <Profiles />
<Profile Name="(Default)" /> <Settings>
</Profiles> <Setting Name="GithubRepo" Type="System.String" Scope="Application">
<Settings /> <Value Profile="(Default)">https://github.com/Wox-launcher/Wox</Value>
</Setting>
</Settings>
</SettingsFile> </SettingsFile>

View file

@ -20,16 +20,17 @@ namespace Wox
{ {
private readonly SettingWindowViewModel _settingsVM; private readonly SettingWindowViewModel _settingsVM;
private readonly MainViewModel _mainVM; private readonly MainViewModel _mainVM;
private readonly Alphabet _alphabet;
#region Constructor #region Constructor
public PublicAPIInstance(SettingWindowViewModel settingsVM, MainViewModel mainVM) public PublicAPIInstance(SettingWindowViewModel settingsVM, MainViewModel mainVM, Alphabet alphabet)
{ {
_settingsVM = settingsVM; _settingsVM = settingsVM;
_mainVM = mainVM; _mainVM = mainVM;
_alphabet = alphabet;
GlobalHotkey.Instance.hookedKeyboardCallback += KListener_hookedKeyboardCallback; GlobalHotkey.Instance.hookedKeyboardCallback += KListener_hookedKeyboardCallback;
WebRequest.RegisterPrefix("data", new DataWebRequestFactory()); WebRequest.RegisterPrefix("data", new DataWebRequestFactory());
} }
#endregion #endregion
@ -70,7 +71,7 @@ namespace Wox
_settingsVM.Save(); _settingsVM.Save();
PluginManager.Save(); PluginManager.Save();
ImageLoader.Save(); ImageLoader.Save();
Alphabet.Save(); _alphabet.Save();
} }
public void ReloadAllPluginData() public void ReloadAllPluginData()
@ -90,12 +91,12 @@ namespace Wox
_mainVM.MainWindowVisibility = Visibility.Visible; _mainVM.MainWindowVisibility = Visibility.Visible;
} }
public void ShowMsg(string title, string subTitle = "", string iconPath = "") public void ShowMsg(string title, string subTitle = "", string iconPath = "", bool useMainWindowAsOwner = true)
{ {
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
var m = new Msg { Owner = Application.Current.MainWindow }; var msg = useMainWindowAsOwner ? new Msg {Owner = Application.Current.MainWindow} : new Msg();
m.Show(title, subTitle, iconPath); msg.Show(title, subTitle, iconPath);
}); });
} }

View file

@ -23,7 +23,7 @@ namespace Wox
private void SetException(Exception exception) private void SetException(Exception exception)
{ {
string path = Path.Combine(Constant.DataDirectory, Log.DirectoryName, Constant.Version); string path = Log.CurrentLogDirectory;
var directory = new DirectoryInfo(path); var directory = new DirectoryInfo(path);
var log = directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First(); var log = directory.GetFiles().OrderByDescending(f => f.LastWriteTime).First();

View file

@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:Wox.ViewModel" xmlns:vm="clr-namespace:Wox.ViewModel"
xmlns:converter="clr-namespace:Wox.Converters"
mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="100" mc:Ignorable="d" d:DesignWidth="100" d:DesignHeight="100"
d:DataContext="{d:DesignInstance vm:ResultsViewModel}" d:DataContext="{d:DesignInstance vm:ResultsViewModel}"
MaxHeight="{Binding MaxHeight}" MaxHeight="{Binding MaxHeight}"
@ -30,6 +31,9 @@
<Button.Content> <Button.Content>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5" <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5"
Cursor="Hand" UseLayoutRounding="False"> Cursor="Hand" UseLayoutRounding="False">
<Grid.Resources>
<converter:HighlightTextConverter x:Key="HighlightTextConverter"/>
</Grid.Resources>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="32" /> <ColumnDefinition Width="32" />
<ColumnDefinition /> <ColumnDefinition />
@ -44,9 +48,23 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Style="{DynamicResource ItemTitleStyle}" DockPanel.Dock="Left" <TextBlock Style="{DynamicResource ItemTitleStyle}" DockPanel.Dock="Left"
VerticalAlignment="Center" ToolTip="{Binding Result.Title}" x:Name="Title" VerticalAlignment="Center" ToolTip="{Binding Result.Title}" x:Name="Title"
Text="{Binding Result.Title}" /> Text="{Binding Result.Title}">
<vm:ResultsViewModel.FormattedText>
<MultiBinding Converter="{StaticResource HighlightTextConverter}">
<Binding Path="Result.Title" />
<Binding Path="Result.TitleHighlightData" />
</MultiBinding>
</vm:ResultsViewModel.FormattedText>
</TextBlock>
<TextBlock Style="{DynamicResource ItemSubTitleStyle}" ToolTip="{Binding Result.SubTitle}" <TextBlock Style="{DynamicResource ItemSubTitleStyle}" ToolTip="{Binding Result.SubTitle}"
Grid.Row="1" x:Name="SubTitle" Text="{Binding Result.SubTitle}" /> Grid.Row="1" x:Name="SubTitle" Text="{Binding Result.SubTitle}">
<vm:ResultsViewModel.FormattedText>
<MultiBinding Converter="{StaticResource HighlightTextConverter}">
<Binding Path="Result.SubTitle" />
<Binding Path="Result.SubTitleHighlightData" />
</MultiBinding>
</vm:ResultsViewModel.FormattedText>
</TextBlock>
</Grid> </Grid>
</Grid> </Grid>
</Button.Content> </Button.Content>

View file

@ -62,7 +62,7 @@
<TextBlock Text="{DynamicResource querySearchPrecision}" /> <TextBlock Text="{DynamicResource querySearchPrecision}" />
<ComboBox Margin="10 0 0 0" Width="120" <ComboBox Margin="10 0 0 0" Width="120"
ItemsSource="{Binding QuerySearchPrecisionStrings}" ItemsSource="{Binding QuerySearchPrecisionStrings}"
SelectedItem="{Binding Settings.QuerySearchPrecision}" /> SelectedItem="{Binding Settings.QuerySearchPrecisionString}" />
</StackPanel> </StackPanel>
<StackPanel Margin="10" Orientation="Horizontal"> <StackPanel Margin="10" Orientation="Horizontal">
<TextBlock Text="{DynamicResource lastQueryMode}" /> <TextBlock Text="{DynamicResource lastQueryMode}" />

View file

@ -260,53 +260,16 @@ namespace Wox
#region Proxy #region Proxy
private void OnTestProxyClick(object sender, RoutedEventArgs e) private void OnTestProxyClick(object sender, RoutedEventArgs e)
{ { // TODO: change to command
if (string.IsNullOrEmpty(_settings.Proxy.Server)) var msg = _viewModel.TestProxy();
{ MessageBox.Show(msg); // TODO: add message box service
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("serverCantBeEmpty"));
return;
}
if (_settings.Proxy.Port <= 0)
{
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("portCantBeEmpty"));
return;
}
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Infrastructure.Constant.Repository);
if (string.IsNullOrEmpty(_settings.Proxy.UserName) || string.IsNullOrEmpty(_settings.Proxy.Password))
{
request.Proxy = new WebProxy(_settings.Proxy.Server, _settings.Proxy.Port);
}
else
{
request.Proxy = new WebProxy(_settings.Proxy.Server, _settings.Proxy.Port)
{
Credentials = new NetworkCredential(_settings.Proxy.UserName, _settings.Proxy.Password)
};
}
try
{
var response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK)
{
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("proxyIsCorrect"));
}
else
{
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("proxyConnectFailed"));
}
}
catch
{
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("proxyConnectFailed"));
}
} }
#endregion #endregion
private async void OnCheckUpdates(object sender, RoutedEventArgs e) private async void OnCheckUpdates(object sender, RoutedEventArgs e)
{ {
await Updater.UpdateApp(); _viewModel.UpdateApp(); // TODO: change to command
} }
private void OnRequestNavigate(object sender, RequestNavigateEventArgs e) private void OnRequestNavigate(object sender, RequestNavigateEventArgs e)

28
Wox/Settings.cs Normal file
View file

@ -0,0 +1,28 @@
namespace Wox.Properties {
// This class allows you to handle specific events on the settings class:
// The SettingChanging event is raised before a setting's value is changed.
// The PropertyChanged event is raised after a setting's value is changed.
// The SettingsLoaded event is raised after the setting values are loaded.
// The SettingsSaving event is raised before the setting values are saved.
internal sealed partial class Settings {
public Settings() {
// // To add event handlers for saving and changing settings, uncomment the lines below:
//
// this.SettingChanging += this.SettingChangingEventHandler;
//
// this.SettingsSaving += this.SettingsSavingEventHandler;
//
}
private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) {
// Add code to handle the SettingChangingEvent event here.
}
private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) {
// Add code to handle the SettingsSaving event here.
}
}
}

View file

@ -13,7 +13,6 @@ using Wox.Core.Resource;
using Wox.Helper; using Wox.Helper;
using Wox.Infrastructure; using Wox.Infrastructure;
using Wox.Infrastructure.Hotkey; using Wox.Infrastructure.Hotkey;
using Wox.Infrastructure.Image;
using Wox.Infrastructure.Storage; using Wox.Infrastructure.Storage;
using Wox.Infrastructure.UserSettings; using Wox.Infrastructure.UserSettings;
using Wox.Plugin; using Wox.Plugin;
@ -25,7 +24,7 @@ namespace Wox.ViewModel
{ {
#region Private Fields #region Private Fields
private bool _queryHasReturn; private bool _isQueryRunning;
private Query _lastQuery; private Query _lastQuery;
private string _queryTextBeforeLeaveResults; private string _queryTextBeforeLeaveResults;
@ -41,7 +40,7 @@ namespace Wox.ViewModel
private CancellationToken _updateToken; private CancellationToken _updateToken;
private bool _saved; private bool _saved;
private Internationalization _translator = InternationalizationManager.Instance; private readonly Internationalization _translator = InternationalizationManager.Instance;
#endregion #endregion
@ -312,7 +311,7 @@ namespace Wox.ViewModel
{ {
var filtered = results.Where var filtered = results.Where
( (
r => StringMatcher.FuzzySearch(query, r.Title).IsSearchPrecisionScoreMet() r => StringMatcher.FuzzySearch(query, r.Title).IsSearchPrecisionScoreMet()
|| StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet() || StringMatcher.FuzzySearch(query, r.SubTitle).IsSearchPrecisionScoreMet()
).ToList(); ).ToList();
ContextMenu.AddResults(filtered, id); ContextMenu.AddResults(filtered, id);
@ -371,63 +370,59 @@ namespace Wox.ViewModel
if (!string.IsNullOrEmpty(QueryText)) if (!string.IsNullOrEmpty(QueryText))
{ {
_updateSource?.Cancel(); _updateSource?.Cancel();
_updateSource = new CancellationTokenSource(); var currentUpdateSource = new CancellationTokenSource();
_updateToken = _updateSource.Token; _updateSource = currentUpdateSource;
var currentCancellationToken = _updateSource.Token;
_updateToken = currentCancellationToken;
ProgressBarVisibility = Visibility.Hidden; ProgressBarVisibility = Visibility.Hidden;
_queryHasReturn = false; _isQueryRunning = true;
var query = PluginManager.QueryInit(QueryText.Trim()); var query = PluginManager.QueryInit(QueryText.Trim());
if (query != null) if (query != null)
{ {
// handle the exclusiveness of plugin using action keyword // handle the exclusiveness of plugin using action keyword
string lastKeyword = _lastQuery.ActionKeyword; RemoveOldQueryResults(query);
string keyword = query.ActionKeyword;
if (string.IsNullOrEmpty(lastKeyword))
{
if (!string.IsNullOrEmpty(keyword))
{
Results.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata);
}
}
else
{
if (string.IsNullOrEmpty(keyword))
{
Results.RemoveResultsFor(PluginManager.NonGlobalPlugins[lastKeyword].Metadata);
}
else if (lastKeyword != keyword)
{
Results.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata);
}
}
_lastQuery = query; _lastQuery = query;
Task.Delay(200, _updateToken).ContinueWith(_ => Task.Delay(200, currentCancellationToken).ContinueWith(_ =>
{ { // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet
if (query.RawQuery == _lastQuery.RawQuery && !_queryHasReturn) if (currentUpdateSource == _updateSource && _isQueryRunning)
{ {
ProgressBarVisibility = Visibility.Visible; ProgressBarVisibility = Visibility.Visible;
} }
}, _updateToken); }, currentCancellationToken);
var plugins = PluginManager.ValidPluginsForQuery(query); var plugins = PluginManager.ValidPluginsForQuery(query);
Task.Run(() => Task.Run(() =>
{ {
Parallel.ForEach(plugins, plugin => // so looping will stop once it was cancelled
var parallelOptions = new ParallelOptions { CancellationToken = currentCancellationToken };
try
{ {
var config = _settings.PluginSettings.Plugins[plugin.Metadata.ID]; Parallel.ForEach(plugins, parallelOptions, plugin =>
if (!config.Disabled)
{ {
var results = PluginManager.QueryForPlugin(plugin, query); var config = _settings.PluginSettings.Plugins[plugin.Metadata.ID];
UpdateResultView(results, plugin.Metadata, query); if (!config.Disabled)
} {
}); var results = PluginManager.QueryForPlugin(plugin, query);
UpdateResultView(results, plugin.Metadata, query);
}
});
}
catch (OperationCanceledException)
{
// nothing to do here
}
// this should happen once after all queries are done so progress bar should continue // this should happen once after all queries are done so progress bar should continue
// until the end of all querying // until the end of all querying
_queryHasReturn = true; _isQueryRunning = false;
ProgressBarVisibility = Visibility.Hidden; if (currentUpdateSource == _updateSource)
}, _updateToken); { // update to hidden if this is still the current query
ProgressBarVisibility = Visibility.Hidden;
}
}, currentCancellationToken);
} }
} }
else else
@ -437,6 +432,30 @@ namespace Wox.ViewModel
} }
} }
private void RemoveOldQueryResults(Query query)
{
string lastKeyword = _lastQuery.ActionKeyword;
string keyword = query.ActionKeyword;
if (string.IsNullOrEmpty(lastKeyword))
{
if (!string.IsNullOrEmpty(keyword))
{
Results.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata);
}
}
else
{
if (string.IsNullOrEmpty(keyword))
{
Results.RemoveResultsFor(PluginManager.NonGlobalPlugins[lastKeyword].Metadata);
}
else if (lastKeyword != keyword)
{
Results.RemoveResultsExcept(PluginManager.NonGlobalPlugins[keyword].Metadata);
}
}
}
private Result ContextMenuTopMost(Result result) private Result ContextMenuTopMost(Result result)
{ {
@ -660,4 +679,4 @@ namespace Wox.ViewModel
#endregion #endregion
} }
} }

View file

@ -5,7 +5,6 @@ namespace Wox.ViewModel
{ {
public class RelayCommand : ICommand public class RelayCommand : ICommand
{ {
private Action<object> _action; private Action<object> _action;
public RelayCommand(Action<object> action) public RelayCommand(Action<object> action)

View file

@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Documents;
using Wox.Infrastructure.UserSettings; using Wox.Infrastructure.UserSettings;
using Wox.Plugin; using Wox.Plugin;
@ -154,14 +156,14 @@ namespace Wox.ViewModel
private List<ResultViewModel> NewResults(List<Result> newRawResults, string resultId) private List<ResultViewModel> NewResults(List<Result> newRawResults, string resultId)
{ {
var results = Results.ToList(); var results = Results.ToList();
var newResults = newRawResults.Select(r => new ResultViewModel(r)).ToList(); var newResults = newRawResults.Select(r => new ResultViewModel(r)).ToList();
var oldResults = results.Where(r => r.Result.PluginID == resultId).ToList(); var oldResults = results.Where(r => r.Result.PluginID == resultId).ToList();
// Find the same results in A (old results) and B (new newResults) // Find the same results in A (old results) and B (new newResults)
var sameResults = oldResults var sameResults = oldResults
.Where(t1 => newResults.Any(x => x.Result.Equals(t1.Result))) .Where(t1 => newResults.Any(x => x.Result.Equals(t1.Result)))
.ToList(); .ToList();
// remove result of relative complement of B in A // remove result of relative complement of B in A
foreach (var result in oldResults.Except(sameResults)) foreach (var result in oldResults.Except(sameResults))
{ {
@ -197,8 +199,37 @@ namespace Wox.ViewModel
return results; return results;
} }
#endregion
#region FormattedText Dependency Property
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
"FormattedText",
typeof(Inline),
typeof(ResultsViewModel),
new PropertyMetadata(null, FormattedTextPropertyChanged));
public static void SetFormattedText(DependencyObject textBlock, IList<int> value)
{
textBlock.SetValue(FormattedTextProperty, value);
}
public static Inline GetFormattedText(DependencyObject textBlock)
{
return (Inline)textBlock.GetValue(FormattedTextProperty);
}
private static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var textBlock = d as TextBlock;
if (textBlock == null) return;
var inline = (Inline)e.NewValue;
textBlock.Inlines.Clear();
if (inline == null) return;
textBlock.Inlines.Add(inline);
}
#endregion #endregion
public class ResultCollection : ObservableCollection<ResultViewModel> public class ResultCollection : ObservableCollection<ResultViewModel>
@ -217,6 +248,10 @@ namespace Wox.ViewModel
} }
} }
/// <summary>
/// Update the results collection with new results, try to keep identical results
/// </summary>
/// <param name="newItems"></param>
public void Update(List<ResultViewModel> newItems) public void Update(List<ResultViewModel> newItems)
{ {
int newCount = newItems.Count; int newCount = newItems.Count;
@ -228,7 +263,7 @@ namespace Wox.ViewModel
ResultViewModel oldResult = this[i]; ResultViewModel oldResult = this[i];
ResultViewModel newResult = newItems[i]; ResultViewModel newResult = newItems[i];
if (!oldResult.Equals(newResult)) if (!oldResult.Equals(newResult))
{ { // result is not the same update it in the current index
this[i] = newResult; this[i] = newResult;
} }
else if (oldResult.Result.Score != newResult.Result.Score) else if (oldResult.Result.Score != newResult.Result.Score)
@ -255,4 +290,4 @@ namespace Wox.ViewModel
} }
} }
} }
} }

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 System.Net;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
@ -20,10 +21,12 @@ namespace Wox.ViewModel
{ {
public class SettingWindowViewModel : BaseModel public class SettingWindowViewModel : BaseModel
{ {
private readonly Updater _updater;
private readonly WoxJsonStorage<Settings> _storage; private readonly WoxJsonStorage<Settings> _storage;
public SettingWindowViewModel() public SettingWindowViewModel(Updater updater)
{ {
_updater = updater;
_storage = new WoxJsonStorage<Settings>(); _storage = new WoxJsonStorage<Settings>();
Settings = _storage.Load(); Settings = _storage.Load();
Settings.PropertyChanged += (s, e) => Settings.PropertyChanged += (s, e) =>
@ -39,6 +42,10 @@ namespace Wox.ViewModel
public Settings Settings { get; set; } public Settings Settings { get; set; }
public async void UpdateApp()
{
await _updater.UpdateApp();
}
public void Save() public void Save()
{ {
@ -73,7 +80,7 @@ namespace Wox.ViewModel
public List<string> QuerySearchPrecisionStrings public List<string> QuerySearchPrecisionStrings
{ {
get get
{ {
var precisionStrings = new List<string>(); var precisionStrings = new List<string>();
var enumList = Enum.GetValues(typeof(StringMatcher.SearchPrecisionScore)).Cast<StringMatcher.SearchPrecisionScore>().ToList(); var enumList = Enum.GetValues(typeof(StringMatcher.SearchPrecisionScore)).Cast<StringMatcher.SearchPrecisionScore>().ToList();
@ -88,6 +95,50 @@ namespace Wox.ViewModel
public List<Language> Languages => _translater.LoadAvailableLanguages(); public List<Language> Languages => _translater.LoadAvailableLanguages();
public IEnumerable<int> MaxResultsRange => Enumerable.Range(2, 16); public IEnumerable<int> MaxResultsRange => Enumerable.Range(2, 16);
public string TestProxy()
{
var proxyServer = Settings.Proxy.Server;
var proxyUserName = Settings.Proxy.UserName;
if (string.IsNullOrEmpty(proxyServer))
{
return InternationalizationManager.Instance.GetTranslation("serverCantBeEmpty");
}
if (Settings.Proxy.Port <= 0)
{
return InternationalizationManager.Instance.GetTranslation("portCantBeEmpty");
}
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_updater.GitHubRepository);
if (string.IsNullOrEmpty(proxyUserName) || string.IsNullOrEmpty(Settings.Proxy.Password))
{
request.Proxy = new WebProxy(proxyServer, Settings.Proxy.Port);
}
else
{
request.Proxy = new WebProxy(proxyServer, Settings.Proxy.Port)
{
Credentials = new NetworkCredential(proxyUserName, Settings.Proxy.Password)
};
}
try
{
var response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK)
{
return InternationalizationManager.Instance.GetTranslation("proxyIsCorrect");
}
else
{
return InternationalizationManager.Instance.GetTranslation("proxyConnectFailed");
}
}
catch
{
return InternationalizationManager.Instance.GetTranslation("proxyConnectFailed");
}
}
#endregion #endregion
#region plugin #region plugin
@ -220,7 +271,7 @@ namespace Wox.ViewModel
}, },
new Result new Result
{ {
Title = $"Open Source: {Constant.Repository}", Title = $"Open Source: {_updater.GitHubRepository}",
SubTitle = "Please star it!" SubTitle = "Please star it!"
} }
}; };
@ -330,8 +381,8 @@ namespace Wox.ViewModel
#region about #region about
public static string Github => Constant.Repository; public string Github => _updater.GitHubRepository;
public static string ReleaseNotes => @"https://github.com/Wox-launcher/Wox/releases/latest"; public string ReleaseNotes => _updater.GitHubRepository + @"/releases/latest";
public static string Version => Constant.Version; public static string Version => Constant.Version;
public string ActivatedTimes => string.Format(_translater.GetTranslation("about_activate_times"), Settings.ActivateTimes); public string ActivatedTimes => string.Format(_translater.GetTranslation("about_activate_times"), Settings.ActivateTimes);
#endregion #endregion

View file

@ -65,72 +65,8 @@
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="DeltaCompressionDotNet, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1d14d6e5194e7f4a, processorArchitecture=MSIL">
<HintPath>..\packages\DeltaCompressionDotNet.1.0.0\lib\net45\DeltaCompressionDotNet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="DeltaCompressionDotNet.MsDelta, Version=1.0.0.0, Culture=neutral, PublicKeyToken=46b2138a390abf55, processorArchitecture=MSIL">
<HintPath>..\packages\DeltaCompressionDotNet.1.0.0\lib\net45\DeltaCompressionDotNet.MsDelta.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="DeltaCompressionDotNet.PatchApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3e8888ee913ed789, processorArchitecture=MSIL">
<HintPath>..\packages\DeltaCompressionDotNet.1.0.0\lib\net45\DeltaCompressionDotNet.PatchApi.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\squirrel.windows.1.4.0\lib\Net45\ICSharpCode.SharpZipLib.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Mdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Mdb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Pdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Pdb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil.Rocks, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NHotkey, Version=1.2.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\NHotkey.1.2.1\lib\net20\NHotkey.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NHotkey.Wpf, Version=1.2.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\NHotkey.Wpf.1.2.1\lib\net35\NHotkey.Wpf.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.2.0\lib\net45\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\squirrel.windows.1.4.0\lib\Net45\NuGet.Squirrel.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PresentationUI, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" /> <Reference Include="PresentationUI, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="PropertyChanged, Version=1.51.0.0, Culture=neutral, PublicKeyToken=ee3ee20bcf148ddd, processorArchitecture=MSIL">
<HintPath>..\packages\PropertyChanged.Fody.1.51.0\lib\dotnet\PropertyChanged.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="ReachFramework" /> <Reference Include="ReachFramework" />
<Reference Include="Splat, Version=1.6.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Splat.1.6.2\lib\Net45\Splat.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Squirrel, Version=1.5.2.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\squirrel.windows.1.5.2\lib\Net45\Squirrel.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Configuration" /> <Reference Include="System.Configuration" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
@ -149,21 +85,19 @@
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
<Reference Include="WindowsInput, Version=1.0.4.0, Culture=neutral, PublicKeyToken=9b287f7dc5073cad, processorArchitecture=MSIL">
<HintPath>..\packages\InputSimulator.1.0.4.0\lib\net20\WindowsInput.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\SolutionAssemblyInfo.cs"> <Compile Include="..\SolutionAssemblyInfo.cs">
<Link>Properties\SolutionAssemblyInfo.cs</Link> <Link>Properties\SolutionAssemblyInfo.cs</Link>
</Compile> </Compile>
<Compile Include="Converters\HighlightTextConverter.cs" />
<Compile Include="Helper\SingletonWindowOpener.cs" /> <Compile Include="Helper\SingletonWindowOpener.cs" />
<Compile Include="PublicAPIInstance.cs" /> <Compile Include="PublicAPIInstance.cs" />
<Compile Include="ReportWindow.xaml.cs" /> <Compile Include="ReportWindow.xaml.cs" />
<Compile Include="ResultListBox.xaml.cs"> <Compile Include="ResultListBox.xaml.cs">
<DependentUpon>ResultListBox.xaml</DependentUpon> <DependentUpon>ResultListBox.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Settings.cs" />
<Compile Include="Storage\HistoryItem.cs" /> <Compile Include="Storage\HistoryItem.cs" />
<Compile Include="Storage\QueryHistory.cs" /> <Compile Include="Storage\QueryHistory.cs" />
<Compile Include="Storage\TopMostRecord.cs" /> <Compile Include="Storage\TopMostRecord.cs" />
@ -346,9 +280,6 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<None Include="Themes\Light.xaml"> <None Include="Themes\Light.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
@ -469,7 +400,44 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Resource> </Resource>
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup>
<PackageReference Include="Fody">
<Version>1.29.2</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="InputSimulator">
<Version>1.0.4</Version>
</PackageReference>
<PackageReference Include="JetBrains.Annotations">
<Version>10.3.0</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>9.0.1</Version>
</PackageReference>
<PackageReference Include="NHotkey.Wpf">
<Version>1.2.1</Version>
</PackageReference>
<PackageReference Include="NLog">
<Version>4.2.0</Version>
</PackageReference>
<PackageReference Include="NuGet.CommandLine">
<Version>3.4.3</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="PropertyChanged.Fody">
<Version>1.51.0</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="squirrel.windows">
<Version>1.5.2</Version>
</PackageReference>
<PackageReference Include="System.Runtime">
<Version>4.0.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent>powershell.exe -NoProfile -ExecutionPolicy Bypass -File $(SolutionDir)Scripts\post_build.ps1 $(ConfigurationName) $(SolutionDir)</PostBuildEvent> <PostBuildEvent>powershell.exe -NoProfile -ExecutionPolicy Bypass -File $(SolutionDir)Scripts\post_build.ps1 $(ConfigurationName) $(SolutionDir)</PostBuildEvent>
@ -484,11 +452,4 @@
</Target> </Target>
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
<Import Project="..\packages\Fody.1.29.2\build\dotnet\Fody.targets" Condition="Exists('..\packages\Fody.1.29.2\build\dotnet\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Fody.1.29.2\build\dotnet\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.1.29.2\build\dotnet\Fody.targets'))" />
</Target>
</Project> </Project>

View file

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DeltaCompressionDotNet" version="1.0.0" targetFramework="net452" />
<package id="Fody" version="1.29.2" targetFramework="net452" developmentDependency="true" />
<package id="InputSimulator" version="1.0.4.0" targetFramework="net452" />
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
<package id="Mono.Cecil" version="0.9.6.1" targetFramework="net452" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
<package id="NHotkey" version="1.2.1" targetFramework="net452" />
<package id="NHotkey.Wpf" version="1.2.1" targetFramework="net452" />
<package id="NLog" version="4.2.0" targetFramework="net452" />
<package id="NuGet.CommandLine" version="3.4.3" targetFramework="net452" developmentDependency="true" />
<package id="PropertyChanged.Fody" version="1.51.0" targetFramework="net452" developmentDependency="true" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net452" />
<package id="Splat" version="1.6.2" targetFramework="net452" />
<package id="squirrel.windows" version="1.5.2" targetFramework="net452" />
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
</packages>

34
azure-pipelines.yml Normal file
View file

@ -0,0 +1,34 @@
# .NET Desktop
# Build and run tests for .NET Desktop or Windows classic desktop solutions.
# Add steps that publish symbols, save build artifacts, and more:
# https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net
trigger:
- master
- dev
pool:
vmImage: 'vs2017-win2016' #'due to windows SDK dependency for building UWP project'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: NuGetToolInstaller@1
- task: NuGetCommand@2
inputs:
restoreSolution: '$(solution)'
- task: VSBuild@1
inputs:
solution: '$(solution)'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: VSTest@2
inputs:
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'