Skip to content

Commit

Permalink
Add an LSP notification for loading projects
Browse files Browse the repository at this point in the history
This is the server side portion of
dotnet/vscode-csharp#6062
  • Loading branch information
jasonmalinowski committed Aug 7, 2023
1 parent 05bdea6 commit 278a2c2
Show file tree
Hide file tree
Showing 16 changed files with 135 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,16 @@
using System.Composition;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.MSBuild.Build;
using Microsoft.CodeAnalysis.MSBuild.Logging;
using Microsoft.CodeAnalysis.ProjectSystem;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Workspaces.ProjectSystem;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.Composition;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
using Roslyn.Utilities;
using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;

Expand Down Expand Up @@ -77,14 +74,8 @@ public LanguageServerProjectSystem(

public async Task OpenSolutionAsync(string solutionFilePath)
{
var result = await TryEnsureMSBuildLoadedAsync(Path.GetDirectoryName(solutionFilePath)!);
if (!result)
{
var message = string.Format(LanguageServerResources.There_were_errors_loading_solution_0_See_log_for_details, Path.GetFileName(solutionFilePath));
await ShowToastNotification.ShowToastNotificationAsync(LSP.MessageType.Error, message, CancellationToken.None, ShowToastNotification.ShowCSharpLogsCommand);
}

await OpenSolutionCoreAsync(solutionFilePath);
if (await TryEnsureMSBuildLoadedAsync(Path.GetDirectoryName(solutionFilePath)!))
await OpenSolutionCoreAsync(solutionFilePath);
}

[MethodImpl(MethodImplOptions.NoInlining)] // Don't inline; the caller needs to ensure MSBuild is loaded before we can use MSBuild types here
Expand Down Expand Up @@ -112,6 +103,28 @@ private async Task OpenSolutionCoreAsync(string solutionFilePath)
}
}

public async Task OpenProjectsAsync(ImmutableArray<string> projectFilePaths)
{
if (!projectFilePaths.Any())
return;

if (await TryEnsureMSBuildLoadedAsync(Path.GetDirectoryName(projectFilePaths.First())!))
await OpenProjectsCoreAsync(projectFilePaths);
}

[MethodImpl(MethodImplOptions.NoInlining)] // Don't inline; the caller needs to ensure MSBuild is loaded before we can use MSBuild types here
private async Task OpenProjectsCoreAsync(ImmutableArray<string> projectFilePaths)
{
using (await _gate.DisposableWaitAsync())
{
_projectsToLoadAndReload.AddWork(projectFilePaths);

// Wait for the in progress batch to complete and send a project initialized notification to the client.
await _projectsToLoadAndReload.WaitUntilCurrentBatchCompletesAsync();
await ProjectInitializationHandler.SendProjectInitializationCompleteNotificationAsync();
}
}

private async Task<bool> TryEnsureMSBuildLoadedAsync(string workingDirectory)
{
using (await _gate.DisposableWaitAsync())
Expand All @@ -137,6 +150,8 @@ private async Task<bool> TryEnsureMSBuildLoadedAsync(string workingDirectory)
else
{
_logger.LogError($"Unable to find a MSBuild to use to load {workingDirectory}.");
await ShowToastNotification.ShowToastNotificationAsync(LSP.MessageType.Error, LanguageServerResources.There_were_problems_loading_your_projects_See_log_for_details, CancellationToken.None, ShowToastNotification.ShowCSharpLogsCommand);

return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// 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.Composition;
using System.Runtime.Serialization;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer.Handler;
using Microsoft.CommonLanguageServerProtocol.Framework;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace;

[ExportCSharpVisualBasicStatelessLspService(typeof(OpenProjectHandler)), Shared]
[Method("project/open")]
internal class OpenProjectHandler : ILspServiceNotificationHandler<OpenProjectHandler.NotificationParams>
{
private readonly LanguageServerProjectSystem _projectSystem;

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public OpenProjectHandler(LanguageServerProjectSystem projectSystem)
{
_projectSystem = projectSystem;
}

public bool MutatesSolutionState => false;
public bool RequiresLSPSolution => false;

Task INotificationHandler<NotificationParams, RequestContext>.HandleNotificationAsync(NotificationParams request, RequestContext requestContext, CancellationToken cancellationToken)
{
return _projectSystem.OpenProjectsAsync(request.Projects.SelectAsArray(p => p.LocalPath));
}

[DataContract]
private class NotificationParams
{
[DataMember(Name = "projects")]
public required Uri[] Projects { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@
<data name="There_were_errors_loading_solution_0_See_log_for_details" xml:space="preserve">
<value>There were errors loading solutution {0}. See log for details.</value>
</data>
<data name="There_were_problems_loading_your_projects_See_log_for_details" xml:space="preserve">
<value>There were problems loading your projects. See log for details.</value>
</data>
<data name="There_were_warnings_loading_project_0_See_log_for_details" xml:space="preserve">
<value>There were warnings loading project {0}. See log for details.</value>
</data>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 278a2c2

Please sign in to comment.