Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public async Task ReportErrorAsync(LSP.MessageType errorKind, string message, Ca
}
}

private async ValueTask ReloadProjectsAsync(ImmutableSegmentedList<ProjectToLoad> projectPathsToLoadOrReload, CancellationToken cancellationToken)
private async ValueTask ReloadProjectsAsync(ImmutableSegmentedList<ProjectToLoad> projectsToLoadOrReload, CancellationToken cancellationToken)
{
var stopwatch = Stopwatch.StartNew();

Expand All @@ -145,15 +145,15 @@ private async ValueTask ReloadProjectsAsync(ImmutableSegmentedList<ProjectToLoad
try
{
var projectsThatNeedRestore = await ProducerConsumer<string>.RunParallelAsync(
source: projectPathsToLoadOrReload,
produceItems: static async (projectToLoad, callback, args, cancellationToken) =>
source: projectsToLoadOrReload,
produceItems: static async (projectToLoad, produceItem, args, cancellationToken) =>
{
var (@this, toastErrorReporter, buildHostProcessManager) = args;
var projectNeedsRestore = await @this.ReloadProjectAsync(
projectToLoad, toastErrorReporter, buildHostProcessManager, cancellationToken);

if (projectNeedsRestore)
callback(projectToLoad.Path);
produceItem(projectToLoad.Path);
},
args: (@this: this, toastErrorReporter, buildHostProcessManager),
cancellationToken).ConfigureAwait(false);
Expand All @@ -165,7 +165,7 @@ private async ValueTask ReloadProjectsAsync(ImmutableSegmentedList<ProjectToLoad
// Tracking: https://github.com/dotnet/vscode-csharp/issues/6675
//
// The request blocks to ensure we aren't trying to run a design time build at the same time as a restore.
await ProjectDependencyHelper.RestoreProjectsAsync(projectsThatNeedRestore, cancellationToken);
await ProjectDependencyHelper.SendProjectNeedsRestoreRequestAsync(projectsThatNeedRestore, cancellationToken);
}
}
finally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ static bool SatisfiesVersion(VersionRange requestedVersionRange, NuGetVersion pr
}
}

internal static async Task RestoreProjectsAsync(ImmutableArray<string> projectPaths, CancellationToken cancellationToken)
internal static async Task SendProjectNeedsRestoreRequestAsync(ImmutableArray<string> projectPaths, CancellationToken cancellationToken)
{
if (projectPaths.IsEmpty)
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public ProjectInitializationHandler(IServiceBrokerProvider serviceBrokerProvider
_projectInitializationCompleteObserver = new ProjectInitializationCompleteObserver(_logger);
}

public static async Task SendProjectInitializationCompleteNotificationAsync()
public static async ValueTask SendProjectInitializationCompleteNotificationAsync()
{
Contract.ThrowIfNull(LanguageServerHost.Instance, "We don't have an LSP channel yet to send this request through.");
var languageServerManager = LanguageServerHost.Instance.GetRequiredLspService<IClientLanguageServerManager>();
Expand Down Expand Up @@ -113,7 +113,7 @@ public void OnNext(ProjectInitializationCompletionState value)
{
_logger.LogDebug("Devkit project initialization completed");
VSCodeRequestTelemetryLogger.ReportProjectInitializationComplete();
_ = SendProjectInitializationCompleteNotificationAsync().ReportNonFatalErrorAsync();
_ = SendProjectInitializationCompleteNotificationAsync().AsTask().ReportNonFatalErrorAsync();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Composition;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.TestHooks;

namespace Microsoft.CodeAnalysis.LanguageServer.Handler.TestHooks;

/// <summary>
/// Implements an LSP request handler to allow the integration tests on the client side to wait for certain server side operations to complete.
/// This is useful in a few cases where the client makes requests to the server but does not wait for the processing to complete, for example
/// 1. Loading projects
/// 2. Reacting to LSP notifications (which are fire and forget)
///
/// This should generally only be used as a last resort when it is impossible for the client to wait specifically for a result it asked for.
/// </summary>
[ExportCSharpVisualBasicStatelessLspService(typeof(WaitForAsyncOperationsHandler)), Shared]
[Method(MethodName)]
internal class WaitForAsyncOperationsHandler : ILspServiceRequestHandler<WaitForAsyncOperationsParams, WaitForAsyncOperationsResponse>
{
internal const string MethodName = "workspace/waitForAsyncOperations";

private readonly AsynchronousOperationListenerProvider _provider;

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public WaitForAsyncOperationsHandler(AsynchronousOperationListenerProvider listenerProvider)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use primary constructor.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will allow the next round of IDE auto-fixers to address this. Thanks

{
_provider = listenerProvider;
}

public bool MutatesSolutionState => false;

public bool RequiresLSPSolution => true;

public async Task<WaitForAsyncOperationsResponse> HandleRequestAsync(WaitForAsyncOperationsParams request, RequestContext context, CancellationToken _)
{
context.TraceInformation($"Waiting for {string.Join(", ", request.Operations)} to complete");
await _provider.WaitAllAsync(context.Solution!.Workspace, request.Operations).ConfigureAwait(false);
return new WaitForAsyncOperationsResponse();
}
}

internal record WaitForAsyncOperationsParams([property: JsonPropertyName("operations")] string[] Operations);

internal record WaitForAsyncOperationsResponse();
Loading