提交 3f86d718 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #17270 from CyrusNajmabadi/packageIndexProgress

Report progress as we update the package index.
......@@ -52,6 +52,7 @@ internal partial class SymbolSearchUpdateEngine
private readonly IDelayService _delayService;
private readonly IIOService _ioService;
private readonly ISymbolSearchLogService _logService;
private readonly ISymbolSearchProgressService _progressService;
private readonly IRemoteControlService _remoteControlService;
private readonly IPatchService _patchService;
private readonly IDatabaseFactoryService _databaseFactoryService;
......@@ -131,17 +132,17 @@ internal async Task UpdateInBackgroundAsync()
private string ConvertToFileName(string source)
{
// Replace all occurrences of a single underscore with a double underscore.
// Now we know any single underscores in the text come from escapaing some
// Now we know any single underscores in the text come from escaping some
// character.
source = source.Replace("_", "__");
var invalidChars = new HashSet<char>(Path.GetInvalidFileNameChars());
var builder = new StringBuilder();
// Excape any character not allowed in a path. Escaping is simple, we just
// use a single underscore followed by the ascii numeric value of the character,
// followed by another undersscoe.
// i.e. ":" is 58 is ascii, and becomes _58_ in the final name.
// Escape any character not allowed in a path. Escaping is simple, we just
// use a single underscore followed by the ASCII numeric value of the character,
// followed by another underscore.
// i.e. ":" is 58 is ASCII, and becomes _58_ in the final name.
foreach (var c in source)
{
if (invalidChars.Contains(c))
......@@ -221,26 +222,61 @@ private async Task CleanCacheDirectoryAsync()
}
private async Task<TimeSpan> DownloadFullDatabaseAsync()
{
try
{
var title = string.Format(WorkspaceDesktopResources.Downloading_IntelliSense_index_for_0, _source);
await _service._progressService.OnDownloadFullDatabaseStartedAsync(title).ConfigureAwait(false);
var (succeeded, delay) = await DownloadFullDatabaseWorkerAsync().ConfigureAwait(false);
if (succeeded)
{
await _service._progressService.OnDownloadFullDatabaseSucceededAsync().ConfigureAwait(false);
}
else
{
await _service._progressService.OnDownloadFullDatabaseFailedAsync(
WorkspaceDesktopResources.Downloading_index_failed).ConfigureAwait(false);
}
return delay;
}
catch (OperationCanceledException)
{
await _service._progressService.OnDownloadFullDatabaseCanceledAsync().ConfigureAwait(false);
throw;
}
catch (Exception e)
{
var message = string.Format(
WorkspaceDesktopResources.Downloading_index_failed_0,
"\r\n" + e.ToString());
await _service._progressService.OnDownloadFullDatabaseFailedAsync(message).ConfigureAwait(false);
throw;
}
}
private async Task<(bool succeeded, TimeSpan delay)> DownloadFullDatabaseWorkerAsync()
{
var serverPath = Invariant($"Elfie_V{AddReferenceDatabase.TextFileFormatVersion}/Latest.xml");
await _service.LogInfoAsync($"Downloading and processing full database: {serverPath}").ConfigureAwait(false);
var element = await DownloadFileAsync(serverPath).ConfigureAwait(false);
var delayUntilNextUpdate = await ProcessFullDatabaseXElementAsync(element).ConfigureAwait(false);
var result = await ProcessFullDatabaseXElementAsync(element).ConfigureAwait(false);
await _service.LogInfoAsync("Downloading and processing full database completed").ConfigureAwait(false);
return delayUntilNextUpdate;
return result;
}
private async Task<TimeSpan> ProcessFullDatabaseXElementAsync(XElement element)
private async Task<(bool succeeded, TimeSpan delay)> ProcessFullDatabaseXElementAsync(XElement element)
{
await _service.LogInfoAsync("Processing full database element").ConfigureAwait(false);
// Convert the database contents in the xml to a byte[].
// Convert the database contents in the XML to a byte[].
var result = await TryParseDatabaseElementAsync(element).ConfigureAwait(false);
if (!result.Item1)
if (!result.succeeded)
{
// Something was wrong with the full database. Trying again soon after won't
// really help. We'll just get the same busted XML from the remote service
......@@ -249,10 +285,10 @@ private async Task<TimeSpan> ProcessFullDatabaseXElementAsync(XElement element)
var failureDelay = _service._delayService.CatastrophicFailureDelay;
await _service.LogInfoAsync($"Unable to parse full database element. Update again in {failureDelay}").ConfigureAwait(false);
return failureDelay;
return (succeeded: false, failureDelay);
}
var bytes = result.Item2;
var bytes = result.contentBytes;
// Make a database out of that and set it to our in memory database that we'll be
// searching.
......@@ -263,12 +299,12 @@ private async Task<TimeSpan> ProcessFullDatabaseXElementAsync(XElement element)
catch (Exception e) when (_service._reportAndSwallowException(e))
{
// We retrieved bytes from the server, but we couldn't make a DB
// out of it. That's very bad. Just trying again one minute later
// out of it. That's very bad. Just trying again one minute later
// isn't going to help. We need to wait until there is good data
// on the server for us to download.
var failureDelay = _service._delayService.CatastrophicFailureDelay;
await _service.LogInfoAsync($"Unable to create database from full database element. Update again in {failureDelay}").ConfigureAwait(false);
return failureDelay;
return (succeeded: false, failureDelay);
}
// Write the file out to disk so we'll have it the next time we launch VS. Do this
......@@ -278,7 +314,7 @@ private async Task<TimeSpan> ProcessFullDatabaseXElementAsync(XElement element)
var delay = _service._delayService.UpdateSucceededDelay;
await _service.LogInfoAsync($"Processing full database element completed. Update again in {delay}").ConfigureAwait(false);
return delay;
return (succeeded: true, delay);
}
private async Task WriteDatabaseFile(byte[] bytes)
......@@ -323,7 +359,7 @@ private async Task WriteDatabaseFile(byte[] bytes)
}
finally
{
// Try to delete the tmp file if it is still around.
// Try to delete the temp file if it is still around.
// If this fails, that's unfortunately, but just proceed.
IOUtilities.PerformIO(() => _service._ioService.Delete(new FileInfo(tempFilePath)));
}
......@@ -537,7 +573,7 @@ private async Task<XElement> TryDownloadFileAsync(IRemoteControlClient client)
// We're reading in our own XML file, but even so, use conservative settings
// just to be on the safe side. First, disallow DTDs entirely (we will never
// have one ourself). And also, prevent any external resolution of files when
// processing the xml.
// processing the XML.
var settings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Prohibit,
......@@ -580,7 +616,7 @@ private async Task RepeatIOAsync(Func<Task> action)
}
}
private async Task<ValueTuple<bool, byte[]>> TryParseDatabaseElementAsync(XElement element)
private async Task<(bool succeeded, byte[] contentBytes)> TryParseDatabaseElementAsync(XElement element)
{
await _service.LogInfoAsync("Parsing database element").ConfigureAwait(false);
var contentsAttribute = element.Attribute(ContentAttributeName);
......@@ -588,7 +624,7 @@ private async Task RepeatIOAsync(Func<Task> action)
{
_service._reportAndSwallowException(new FormatException($"Database element invalid. Missing '{ContentAttributeName}' attribute"));
return ValueTuple.Create(false, (byte[])null);
return (succeeded: false, (byte[])null);
}
var contentBytes = await ConvertContentAttributeAsync(contentsAttribute).ConfigureAwait(false);
......@@ -607,11 +643,11 @@ private async Task RepeatIOAsync(Func<Task> action)
{
_service._reportAndSwallowException(new FormatException($"Checksum mismatch: expected != actual. {expectedChecksum} != {actualChecksum}"));
return ValueTuple.Create(false, (byte[])null);
return (succeeded: false, (byte[])null);
}
}
return ValueTuple.Create(true, contentBytes);
return (succeeded: true, contentBytes);
}
private async Task<byte[]> ConvertContentAttributeAsync(XAttribute contentsAttribute)
......
......@@ -28,15 +28,19 @@ internal partial class SymbolSearchUpdateEngine : ISymbolSearchUpdateEngine
private ConcurrentDictionary<string, IAddReferenceDatabaseWrapper> _sourceToDatabase =
new ConcurrentDictionary<string, IAddReferenceDatabaseWrapper>();
public SymbolSearchUpdateEngine(ISymbolSearchLogService logService)
: this(logService, CancellationToken.None)
public SymbolSearchUpdateEngine(
ISymbolSearchLogService logService,
ISymbolSearchProgressService progressService)
: this(logService, progressService, CancellationToken.None)
{
}
public SymbolSearchUpdateEngine(
ISymbolSearchLogService logService,
ISymbolSearchProgressService progressService,
CancellationToken updateCancellationToken)
: this(logService,
progressService,
new RemoteControlService(),
new DelayService(),
new IOService(),
......@@ -53,6 +57,7 @@ public SymbolSearchUpdateEngine(ISymbolSearchLogService logService)
/// </summary>
internal SymbolSearchUpdateEngine(
ISymbolSearchLogService logService,
ISymbolSearchProgressService progressService,
IRemoteControlService remoteControlService,
IDelayService delayService,
IIOService ioService,
......@@ -64,6 +69,7 @@ public SymbolSearchUpdateEngine(ISymbolSearchLogService logService)
_delayService = delayService;
_ioService = ioService;
_logService = logService;
_progressService = progressService;
_remoteControlService = remoteControlService;
_patchService = patchService;
_databaseFactoryService = databaseFactoryService;
......
......@@ -12,10 +12,13 @@ namespace Microsoft.CodeAnalysis.SymbolSearch
/// implementation produces an engine that will run in-process. Implementations at
/// other layers can behave differently (for example, running the engine out-of-process).
/// </summary>
internal static class SymbolSearchUpdateEngineFactory
internal static partial class SymbolSearchUpdateEngineFactory
{
public static async Task<ISymbolSearchUpdateEngine> CreateEngineAsync(
Workspace workspace, ISymbolSearchLogService logService, CancellationToken cancellationToken)
Workspace workspace,
ISymbolSearchLogService logService,
ISymbolSearchProgressService progressService,
CancellationToken cancellationToken)
{
var client = await workspace.TryGetRemoteHostClientAsync(
RemoteFeatureOptions.SymbolSearchEnabled, cancellationToken).ConfigureAwait(false);
......@@ -24,25 +27,35 @@ internal static class SymbolSearchUpdateEngineFactory
var session = await client.TryCreateKeepAliveSessionAsync(WellKnownServiceHubServices.RemoteSymbolSearchUpdateEngine, logService, cancellationToken).ConfigureAwait(false);
if (session != null)
{
return new RemoteUpdateEngine(workspace, session);
return new RemoteUpdateEngine(workspace, session, logService, progressService);
}
}
// Couldn't go out of proc. Just do everything inside the current process.
return new SymbolSearchUpdateEngine(logService);
return new SymbolSearchUpdateEngine(logService, progressService);
}
private class RemoteUpdateEngine : ISymbolSearchUpdateEngine
private partial class RemoteUpdateEngine : ISymbolSearchUpdateEngine, ISymbolSearchLogService, ISymbolSearchProgressService
{
private readonly SemaphoreSlim _gate = new SemaphoreSlim(initialCount: 1);
private readonly Workspace _workspace;
private readonly ISymbolSearchLogService _logService;
private readonly ISymbolSearchProgressService _progressService;
private readonly KeepAliveSession _session;
public RemoteUpdateEngine(Workspace workspace, KeepAliveSession session)
public RemoteUpdateEngine(
Workspace workspace,
KeepAliveSession session,
ISymbolSearchLogService logService,
ISymbolSearchProgressService progressService)
{
_workspace = workspace;
_session = session;
_logService = logService;
_progressService = progressService;
}
public async Task<ImmutableArray<PackageWithTypeResult>> FindPackagesWithTypeAsync(
......@@ -82,6 +95,28 @@ public RemoteUpdateEngine(Workspace workspace, KeepAliveSession session)
nameof(IRemoteSymbolSearchUpdateEngine.UpdateContinuouslyAsync),
new object[] { sourceName, localSettingsDirectory }, CancellationToken.None).ConfigureAwait(false);
}
#region RPC callbacks
public Task LogExceptionAsync(string exception, string text)
=> _logService.LogExceptionAsync(exception, text);
public Task LogInfoAsync(string text)
=> _logService.LogInfoAsync(text);
public Task OnDownloadFullDatabaseStartedAsync(string title)
=> _progressService.OnDownloadFullDatabaseStartedAsync(title);
public Task OnDownloadFullDatabaseSucceededAsync()
=> _progressService.OnDownloadFullDatabaseSucceededAsync();
public Task OnDownloadFullDatabaseCanceledAsync()
=> _progressService.OnDownloadFullDatabaseCanceledAsync();
public Task OnDownloadFullDatabaseFailedAsync(string message)
=> _progressService.OnDownloadFullDatabaseFailedAsync(message);
#endregion
}
}
}
......@@ -16,6 +16,7 @@
using Microsoft.VisualStudio.Settings;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell.Settings;
using Microsoft.VisualStudio.TaskStatusCenter;
using Roslyn.Utilities;
using VSShell = Microsoft.VisualStudio.Shell;
......@@ -38,6 +39,7 @@ internal partial class VisualStudioSymbolSearchService : AbstractDelayStartedSer
private readonly IPackageInstallerService _installerService;
private readonly string _localSettingsDirectory;
private readonly LogService _logService;
private readonly ISymbolSearchProgressService _progressService;
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
......@@ -54,6 +56,7 @@ internal partial class VisualStudioSymbolSearchService : AbstractDelayStartedSer
_localSettingsDirectory = new ShellSettingsManager(serviceProvider).GetApplicationDataFolder(ApplicationDataFolder.LocalSettings);
_logService = new LogService((IVsActivityLog)serviceProvider.GetService(typeof(SVsActivityLog)));
_progressService = workspace.Services.GetService<ISymbolSearchProgressService>();
}
protected override void EnableService()
......@@ -92,7 +95,7 @@ private async Task<ISymbolSearchUpdateEngine> GetEngine(CancellationToken cancel
if (_updateEngine == null)
{
_updateEngine = await SymbolSearchUpdateEngineFactory.CreateEngineAsync(
_workspace, _logService, cancellationToken).ConfigureAwait(false);
_workspace, _logService, _progressService, cancellationToken).ConfigureAwait(false);
}
return _updateEngine;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Composition;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.SymbolSearch;
using Microsoft.VisualStudio.TaskStatusCenter;
using Roslyn.Utilities;
using VSShell = Microsoft.VisualStudio.Shell;
namespace Microsoft.VisualStudio.LanguageServices.SymbolSearch
{
[ExportWorkspaceService(typeof(ISymbolSearchProgressService), ServiceLayer.Host), Shared]
internal class VisualStudioSymbolSearchProgressService : ISymbolSearchProgressService
{
private readonly object _gate = new object();
private readonly Lazy<IVsTaskStatusCenterService> _taskCenterServiceOpt;
private TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
[ImportingConstructor]
public VisualStudioSymbolSearchProgressService(VSShell.SVsServiceProvider serviceProvider)
{
_taskCenterServiceOpt = new Lazy<IVsTaskStatusCenterService>(() =>
(IVsTaskStatusCenterService)serviceProvider.GetService(typeof(SVsTaskStatusCenterService)));
}
public async Task OnDownloadFullDatabaseStartedAsync(string title)
{
try
{
await OnDownloadFullDatabaseStartedWorkerAsync(title).ConfigureAwait(false);
}
catch (Exception e) when (FatalError.ReportWithoutCrashUnlessCanceled(e))
{
}
}
private Task OnDownloadFullDatabaseStartedWorkerAsync(string title)
{
var options = GetOptions(title);
var data = new TaskProgressData
{
CanBeCanceled = false,
PercentComplete = null,
};
TaskCompletionSource<bool> localTaskCompletionSource;
lock (_gate)
{
// Take any existing tasks and move them to the complete state.
_taskCompletionSource.TrySetResult(true);
// Now create an existing task to track the current download and let
// vs know about it.
_taskCompletionSource = new TaskCompletionSource<bool>();
localTaskCompletionSource = _taskCompletionSource;
}
var handler = _taskCenterServiceOpt.Value?.PreRegister(options, data);
handler?.RegisterTask(localTaskCompletionSource.Task);
return SpecializedTasks.EmptyTask;
}
private static TaskHandlerOptions GetOptions(string title)
{
var options = new TaskHandlerOptions
{
Title = title,
RetentionAfterCompletion = CompletionRetention.Faulted
};
return options;
}
public Task OnDownloadFullDatabaseSucceededAsync()
{
lock (_gate)
{
_taskCompletionSource?.TrySetResult(true);
return SpecializedTasks.EmptyTask;
}
}
public Task OnDownloadFullDatabaseCanceledAsync()
{
lock (_gate)
{
_taskCompletionSource?.TrySetCanceled();
return SpecializedTasks.EmptyTask;
}
}
public Task OnDownloadFullDatabaseFailedAsync(string message)
{
lock (_gate)
{
_taskCompletionSource?.TrySetException(new Exception(message));
return SpecializedTasks.EmptyTask;
}
}
}
}
......@@ -133,7 +133,7 @@ public async Task TestSessionWithNoSolution()
service.Enable();
var mock = new MockLogService();
var mock = new MockLogAndProgressService();
var client = await service.TryGetRemoteHostClientAsync(CancellationToken.None);
var session = await client.TryCreateKeepAliveSessionAsync(WellKnownServiceHubServices.RemoteSymbolSearchUpdateEngine, mock, CancellationToken.None);
......@@ -214,10 +214,15 @@ public Assembly LoadFromPath(string fullPath)
}
}
private class MockLogService : ISymbolSearchLogService
private class MockLogAndProgressService : ISymbolSearchLogService, ISymbolSearchProgressService
{
public Task LogExceptionAsync(string exception, string text) => SpecializedTasks.EmptyTask;
public Task LogInfoAsync(string text) => SpecializedTasks.EmptyTask;
public Task OnDownloadFullDatabaseStartedAsync(string title) => SpecializedTasks.EmptyTask;
public Task OnDownloadFullDatabaseSucceededAsync() => SpecializedTasks.EmptyTask;
public Task OnDownloadFullDatabaseCanceledAsync() => SpecializedTasks.EmptyTask;
public Task OnDownloadFullDatabaseFailedAsync(string message) => SpecializedTasks.EmptyTask;
}
}
}
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.IO
Imports System.IO.Compression
Imports System.Threading
Imports System.Threading.Tasks
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Elfie.Model
Imports Microsoft.CodeAnalysis.Packaging
Imports Microsoft.CodeAnalysis.SymbolSearch
Imports Microsoft.VisualStudio.RemoteControl
Imports Moq
......@@ -39,6 +36,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim service = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=remoteControlService.Object,
delayService:=TestDelayService.Instance,
ioService:=ioMock.Object,
......@@ -69,6 +67,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim service = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=remoteControlService.Object,
delayService:=TestDelayService.Instance,
ioService:=ioMock.Object,
......@@ -106,6 +105,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim searchService = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=serviceMock.Object,
delayService:=TestDelayService.Instance,
ioService:=ioMock.Object,
......@@ -149,6 +149,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim searchService = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=serviceMock.Object,
delayService:=delayMock.Object,
ioService:=ioMock.Object,
......@@ -184,6 +185,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim searchService = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=serviceMock.Object,
delayService:=TestDelayService.Instance,
ioService:=ioMock.Object,
......@@ -232,6 +234,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim searchService = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=remoteControlMock.Object,
delayService:=delayMock.Object,
ioService:=ioMock.Object,
......@@ -275,6 +278,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim searchService = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=remoteControlMock.Object,
delayService:=delayMock.Object,
ioService:=ioMock.Object,
......@@ -322,6 +326,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim searchService = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=remoteControlMock.Object,
delayService:=delayMock.Object,
ioService:=ioMock.Object,
......@@ -380,6 +385,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim searchService = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=remoteControlMock.Object,
delayService:=delayMock.Object,
ioService:=ioMock.Object,
......@@ -425,6 +431,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim searchService = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=remoteControlMock.Object,
delayService:=delayMock.Object,
ioService:=ioMock.Object,
......@@ -478,6 +485,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim searchService = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=remoteControlMock.Object,
delayService:=delayMock.Object,
ioService:=ioMock.Object,
......@@ -537,6 +545,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim searchService = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=remoteControlMock.Object,
delayService:=delayMock.Object,
ioService:=ioMock.Object,
......@@ -592,6 +601,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Dim searchService = New SymbolSearchUpdateEngine(
logService:=TestLogService.Instance,
progressService:=TestProgressService.Instance,
remoteControlService:=remoteControlMock.Object,
delayService:=delayMock.Object,
ioService:=ioMock.Object,
......@@ -748,5 +758,30 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.SymbolSearch
Return SpecializedTasks.EmptyTask
End Function
End Class
Private Class TestProgressService
Implements ISymbolSearchProgressService
Public Shared ReadOnly Instance As TestProgressService = New TestProgressService()
Private Sub New()
End Sub
Public Function OnDownloadFullDatabaseStartedAsync(title As String) As Task Implements ISymbolSearchProgressService.OnDownloadFullDatabaseStartedAsync
Return SpecializedTasks.EmptyTask
End Function
Public Function OnDownloadFullDatabaseSucceededAsync() As Task Implements ISymbolSearchProgressService.OnDownloadFullDatabaseSucceededAsync
Return SpecializedTasks.EmptyTask
End Function
Public Function OnDownloadFullDatabaseCanceledAsync() As Task Implements ISymbolSearchProgressService.OnDownloadFullDatabaseCanceledAsync
Return SpecializedTasks.EmptyTask
End Function
Public Function OnDownloadFullDatabaseFailedAsync(message As String) As Task Implements ISymbolSearchProgressService.OnDownloadFullDatabaseFailedAsync
Return SpecializedTasks.EmptyTask
End Function
End Class
End Class
End Namespace
......@@ -60,6 +60,33 @@ internal class WorkspaceDesktopResources {
}
}
/// <summary>
/// Looks up a localized string similar to Downloading index failed.
/// </summary>
internal static string Downloading_index_failed {
get {
return ResourceManager.GetString("Downloading_index_failed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Downloading index failed:{0}.
/// </summary>
internal static string Downloading_index_failed_0 {
get {
return ResourceManager.GetString("Downloading_index_failed_0", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Downloading IntelliSense index for {0}.
/// </summary>
internal static string Downloading_IntelliSense_index_for_0 {
get {
return ResourceManager.GetString("Downloading_IntelliSense_index_for_0", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid assembly name.
/// </summary>
......
......@@ -117,6 +117,15 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Downloading_index_failed" xml:space="preserve">
<value>Downloading index failed</value>
</data>
<data name="Downloading_index_failed_0" xml:space="preserve">
<value>Downloading index failed:{0}</value>
</data>
<data name="Downloading_IntelliSense_index_for_0" xml:space="preserve">
<value>Downloading IntelliSense index for {0}</value>
</data>
<data name="Invalid_assembly_name" xml:space="preserve">
<value>Invalid assembly name</value>
</data>
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Composition;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.SymbolSearch
{
internal interface ISymbolSearchProgressService : IWorkspaceService
{
Task OnDownloadFullDatabaseStartedAsync(string title);
Task OnDownloadFullDatabaseSucceededAsync();
Task OnDownloadFullDatabaseCanceledAsync();
Task OnDownloadFullDatabaseFailedAsync(string message);
}
[ExportWorkspaceService(typeof(ISymbolSearchProgressService)), Shared]
internal class DefaultSymbolSearchProgressService: ISymbolSearchProgressService
{
public Task OnDownloadFullDatabaseStartedAsync(string title) => SpecializedTasks.EmptyTask;
public Task OnDownloadFullDatabaseSucceededAsync() => SpecializedTasks.EmptyTask;
public Task OnDownloadFullDatabaseCanceledAsync() => SpecializedTasks.EmptyTask;
public Task OnDownloadFullDatabaseFailedAsync(string message) => SpecializedTasks.EmptyTask;
}
}
\ No newline at end of file
......@@ -9,7 +9,8 @@
namespace Microsoft.CodeAnalysis.Remote
{
internal partial class RemoteSymbolSearchUpdateEngine : ServiceHubServiceBase, IRemoteSymbolSearchUpdateEngine
internal partial class RemoteSymbolSearchUpdateEngine :
ServiceHubServiceBase, IRemoteSymbolSearchUpdateEngine, ISymbolSearchLogService, ISymbolSearchProgressService
{
private readonly SymbolSearchUpdateEngine _updateEngine;
......@@ -17,7 +18,7 @@ public RemoteSymbolSearchUpdateEngine(Stream stream, IServiceProvider servicePro
: base(serviceProvider, stream)
{
_updateEngine = new SymbolSearchUpdateEngine(
new LogService(this), updateCancellationToken: ShutdownCancellationToken);
logService: this, progressService: this);
Rpc.StartListening();
}
......@@ -51,20 +52,26 @@ public async Task<ImmutableArray<ReferenceAssemblyWithTypeResult>> FindReference
return results;
}
private class LogService : ISymbolSearchLogService
{
private readonly RemoteSymbolSearchUpdateEngine _service;
#region Messages to forward from here to VS
public LogService(RemoteSymbolSearchUpdateEngine service)
{
_service = service;
}
public Task LogExceptionAsync(string exception, string text)
=> this.Rpc.InvokeAsync(nameof(LogExceptionAsync), exception, text);
public Task LogExceptionAsync(string exception, string text)
=> _service.Rpc.InvokeAsync(nameof(LogExceptionAsync), exception, text);
public Task LogInfoAsync(string text)
=> this.Rpc.InvokeAsync(nameof(LogInfoAsync), text);
public Task LogInfoAsync(string text)
=> _service.Rpc.InvokeAsync(nameof(LogInfoAsync), text);
}
public Task OnDownloadFullDatabaseStartedAsync(string title)
=> this.Rpc.InvokeAsync(nameof(OnDownloadFullDatabaseStartedAsync), title);
public Task OnDownloadFullDatabaseSucceededAsync()
=> this.Rpc.InvokeAsync(nameof(OnDownloadFullDatabaseSucceededAsync));
public Task OnDownloadFullDatabaseCanceledAsync()
=> this.Rpc.InvokeAsync(nameof(OnDownloadFullDatabaseCanceledAsync));
public Task OnDownloadFullDatabaseFailedAsync(string message)
=> this.Rpc.InvokeAsync(nameof(OnDownloadFullDatabaseFailedAsync), message);
#endregion
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册