Move WebSearch to Async model

This commit is contained in:
弘韬 张 2021-01-07 11:09:08 +08:00
parent d0d938b92a
commit a4edbc2cb9
5 changed files with 52 additions and 52 deletions

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Controls; using System.Windows.Controls;
@ -13,14 +14,12 @@ using Flow.Launcher.Plugin.SharedCommands;
namespace Flow.Launcher.Plugin.WebSearch namespace Flow.Launcher.Plugin.WebSearch
{ {
public class Main : IPlugin, ISettingProvider, IPluginI18n, ISavable, IResultUpdated public class Main : IAsyncPlugin, ISettingProvider, IPluginI18n, ISavable, IResultUpdated
{ {
private PluginInitContext _context; private PluginInitContext _context;
private readonly Settings _settings; private readonly Settings _settings;
private readonly SettingsViewModel _viewModel; private readonly SettingsViewModel _viewModel;
private CancellationTokenSource _updateSource;
private CancellationToken _updateToken;
internal const string Images = "Images"; internal const string Images = "Images";
internal static string DefaultImagesDirectory; internal static string DefaultImagesDirectory;
@ -33,7 +32,7 @@ namespace Flow.Launcher.Plugin.WebSearch
_viewModel.Save(); _viewModel.Save();
} }
public List<Result> Query(Query query) public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
{ {
if (FilesFolders.IsLocationPathString(query.Search)) if (FilesFolders.IsLocationPathString(query.Search))
return new List<Result>(); return new List<Result>();
@ -41,10 +40,6 @@ namespace Flow.Launcher.Plugin.WebSearch
var searchSourceList = new List<SearchSource>(); var searchSourceList = new List<SearchSource>();
var results = new List<Result>(); var results = new List<Result>();
_updateSource?.Cancel();
_updateSource = new CancellationTokenSource();
_updateToken = _updateSource.Token;
_settings.SearchSources.Where(o => (o.ActionKeyword == query.ActionKeyword || o.ActionKeyword == SearchSourceGlobalPluginWildCardSign) _settings.SearchSources.Where(o => (o.ActionKeyword == query.ActionKeyword || o.ActionKeyword == SearchSourceGlobalPluginWildCardSign)
&& o.Enabled) && o.Enabled)
.ToList() .ToList()
@ -94,49 +89,45 @@ namespace Flow.Launcher.Plugin.WebSearch
}; };
results.Add(result); results.Add(result);
ResultsUpdated?.Invoke(this, new ResultUpdatedEventArgs
{
Results = results,
Query = query
});
UpdateResultsFromSuggestion(results, keyword, subtitle, searchSource, query);
} }
ResultsUpdated?.Invoke(this, new ResultUpdatedEventArgs
{
Results = results,
Query = query
});
await UpdateResultsFromSuggestionAsync(results, keyword, subtitle, searchSource, query, token).ConfigureAwait(false);
} }
} }
return results; return results;
} }
private void UpdateResultsFromSuggestion(List<Result> results, string keyword, string subtitle, private async Task UpdateResultsFromSuggestionAsync(List<Result> results, string keyword, string subtitle,
SearchSource searchSource, Query query) SearchSource searchSource, Query query, CancellationToken token)
{ {
if (_settings.EnableSuggestion) if (_settings.EnableSuggestion)
{ {
const int waittime = 300; var suggestions = await SuggestionsAsync(keyword, subtitle, searchSource, token).ConfigureAwait(false);
var task = Task.Run(async () => results.AddRange(suggestions);
{
var suggestions = await Suggestions(keyword, subtitle, searchSource);
results.AddRange(suggestions);
}, _updateToken);
if (!task.Wait(waittime)) token.ThrowIfCancellationRequested();
ResultsUpdated?.Invoke(this, new ResultUpdatedEventArgs
{ {
task.ContinueWith(_ => ResultsUpdated?.Invoke(this, new ResultUpdatedEventArgs Results = results,
{ Query = query
Results = results, });
Query = query
}), _updateToken);
}
} }
} }
private async Task<IEnumerable<Result>> Suggestions(string keyword, string subtitle, SearchSource searchSource) private async Task<IEnumerable<Result>> SuggestionsAsync(string keyword, string subtitle, SearchSource searchSource, CancellationToken token)
{ {
var source = _settings.SelectedSuggestion; var source = _settings.SelectedSuggestion;
if (source != null) if (source != null)
{ {
var suggestions = await source.Suggestions(keyword); var suggestions = await source.Suggestions(keyword, token);
var resultsFromSuggestion = suggestions.Select(o => new Result var resultsFromSuggestion = suggestions.Select(o => new Result
{ {
Title = o, Title = o,
@ -169,19 +160,24 @@ namespace Flow.Launcher.Plugin.WebSearch
_settings = _viewModel.Settings; _settings = _viewModel.Settings;
} }
public void Init(PluginInitContext context) public Task InitAsync(PluginInitContext context)
{ {
_context = context; return Task.Run(Init);
var pluginDirectory = _context.CurrentPluginMetadata.PluginDirectory;
var bundledImagesDirectory = Path.Combine(pluginDirectory, Images);
// Default images directory is in the WebSearch's application folder void Init()
DefaultImagesDirectory = Path.Combine(pluginDirectory, Images); {
Helper.ValidateDataDirectory(bundledImagesDirectory, DefaultImagesDirectory); _context = context;
var pluginDirectory = _context.CurrentPluginMetadata.PluginDirectory;
var bundledImagesDirectory = Path.Combine(pluginDirectory, Images);
// Custom images directory is in the WebSearch's data location folder // Default images directory is in the WebSearch's application folder
var name = Path.GetFileNameWithoutExtension(_context.CurrentPluginMetadata.ExecuteFileName); DefaultImagesDirectory = Path.Combine(pluginDirectory, Images);
CustomImagesDirectory = Path.Combine(DataLocation.PluginSettingsDirectory, name, "CustomIcons"); Helper.ValidateDataDirectory(bundledImagesDirectory, DefaultImagesDirectory);
// Custom images directory is in the WebSearch's data location folder
var name = Path.GetFileNameWithoutExtension(_context.CurrentPluginMetadata.ExecuteFileName);
CustomImagesDirectory = Path.Combine(DataLocation.PluginSettingsDirectory, name, "CustomIcons");
};
} }
#region ISettingProvider Members #region ISettingProvider Members

View file

@ -9,6 +9,7 @@ using Newtonsoft.Json.Linq;
using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Logger;
using System.Net.Http; using System.Net.Http;
using System.Threading;
namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{ {
@ -16,14 +17,14 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{ {
private readonly Regex _reg = new Regex("window.baidu.sug\\((.*)\\)"); private readonly Regex _reg = new Regex("window.baidu.sug\\((.*)\\)");
public override async Task<List<string>> Suggestions(string query) public override async Task<List<string>> Suggestions(string query, CancellationToken token)
{ {
string result; string result;
try try
{ {
const string api = "http://suggestion.baidu.com/su?json=1&wd="; const string api = "http://suggestion.baidu.com/su?json=1&wd=";
result = await Http.GetAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); result = await Http.GetAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false);
} }
catch (HttpRequestException e) catch (HttpRequestException e)
{ {

View file

@ -9,19 +9,20 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Text.Json; using System.Text.Json;
using System.Linq; using System.Linq;
using System.Threading;
namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{ {
class Bing : SuggestionSource class Bing : SuggestionSource
{ {
public override async Task<List<string>> Suggestions(string query) public override async Task<List<string>> Suggestions(string query, CancellationToken token)
{ {
Stream resultStream; Stream resultStream;
try try
{ {
const string api = "https://api.bing.com/qsonhs.aspx?q="; const string api = "https://api.bing.com/qsonhs.aspx?q=";
resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); resultStream = await Http.GetStreamAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false);
} }
catch (HttpRequestException e) catch (HttpRequestException e)
{ {
@ -29,7 +30,7 @@ namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
return new List<string>(); return new List<string>();
} }
if (resultStream.Length == 0) return new List<string>(); if (resultStream.Length == 0) return new List<string>(); // this handles the cancellation
JsonElement json; JsonElement json;
try try

View file

@ -8,18 +8,19 @@ using Newtonsoft.Json.Linq;
using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.Logger;
using System.Net.Http; using System.Net.Http;
using System.Threading;
namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{ {
public class Google : SuggestionSource public class Google : SuggestionSource
{ {
public override async Task<List<string>> Suggestions(string query) public override async Task<List<string>> Suggestions(string query, CancellationToken token)
{ {
string result; string result;
try try
{ {
const string api = "https://www.google.com/complete/search?output=chrome&q="; const string api = "https://www.google.com/complete/search?output=chrome&q=";
result = await Http.GetAsync(api + Uri.EscapeUriString(query)).ConfigureAwait(false); result = await Http.GetAsync(api + Uri.EscapeUriString(query), token).ConfigureAwait(false);
} }
catch (HttpRequestException e) catch (HttpRequestException e)
{ {

View file

@ -1,10 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources namespace Flow.Launcher.Plugin.WebSearch.SuggestionSources
{ {
public abstract class SuggestionSource public abstract class SuggestionSource
{ {
public abstract Task<List<string>> Suggestions(string query); public abstract Task<List<string>> Suggestions(string query, CancellationToken token);
} }
} }