Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions src/PowerShellEditorServices/Server/PsesLanguageServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
Expand All @@ -16,6 +17,7 @@
using Newtonsoft.Json.Linq;
using OmniSharp.Extensions.JsonRpc;
using OmniSharp.Extensions.LanguageServer.Protocol.General;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
using OmniSharp.Extensions.LanguageServer.Server;

Expand Down Expand Up @@ -138,12 +140,13 @@ public async Task StartAsync()
languageServer.Services.GetService<ILogger<HostLoggerAdapter>>()
));

// Set the workspace path from the parameters.
// Set the workspace path from the parameters. The collection may be
// absent (the field is optional in LSP), uninitialized, or contain
// entries without a URI, so we treat any of those as "no folders yet"
// rather than dereferencing a null and throwing.
WorkspaceService workspaceService = languageServer.Services.GetService<WorkspaceService>();
if (initializeParams.WorkspaceFolders is not null)
{
workspaceService.WorkspaceFolders.AddRange(initializeParams.WorkspaceFolders);
}
workspaceService.WorkspaceFolders.AddRange(
GetValidWorkspaceFolders(initializeParams.WorkspaceFolders));

// Parse initialization options.
JObject initializationOptions = initializeParams.InitializationOptions as JObject;
Expand All @@ -161,7 +164,7 @@ public async Task StartAsync()
// First check the setting, then use the first workspace folder,
// finally fall back to CWD.
InitialWorkingDirectory = initializationOptions?.GetValue("initialWorkingDirectory")?.Value<string>()
?? workspaceService.WorkspaceFolders.FirstOrDefault()?.Uri.GetFileSystemPath()
?? workspaceService.WorkspaceFolders.FirstOrDefault()?.Uri?.GetFileSystemPath()
?? Directory.GetCurrentDirectory(),
// If a shell integration script path is provided, that implies the feature is enabled.
ShellIntegrationScript = initializationOptions?.GetValue("shellIntegrationScript")?.Value<string>()
Expand All @@ -180,6 +183,21 @@ public async Task StartAsync()
_serverStart.SetResult(true);
}

/// <summary>
/// Filters the workspace folders provided on <c>initialize</c> down to the usable ones.
/// </summary>
/// <remarks>
/// The <c>workspaceFolders</c> field is optional in LSP, so the collection may be absent or
/// uninitialized, and individual folders may be sent without a URI. Any of those are treated
/// as "no folder" so that downstream code (which dereferences <see cref="WorkspaceFolder.Uri" />)
/// does not throw a <see cref="NullReferenceException" />.
/// </remarks>
/// <param name="workspaceFolders">The workspace folders from the initialize parameters.</param>
/// <returns>The folders that have a non-null URI, or an empty sequence.</returns>
internal static IEnumerable<WorkspaceFolder> GetValidWorkspaceFolders(IEnumerable<WorkspaceFolder> workspaceFolders)
=> workspaceFolders?.Where(static folder => folder?.Uri is not null)
?? Enumerable.Empty<WorkspaceFolder>();

/// <summary>
/// Get a task that completes when the server is shut down.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Linq;
using Microsoft.PowerShell.EditorServices.Server;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using Xunit;

namespace PowerShellEditorServices.Test.Server
{
[Trait("Category", "Server")]
public class PsesLanguageServerTests
{
[Fact]
public void GetValidWorkspaceFoldersReturnsEmptyWhenNull()
=> Assert.Empty(PsesLanguageServer.GetValidWorkspaceFolders(null));

[Fact]
public void GetValidWorkspaceFoldersReturnsEmptyWhenEmpty()
=> Assert.Empty(PsesLanguageServer.GetValidWorkspaceFolders(new Container<WorkspaceFolder>()));

[Fact]
public void GetValidWorkspaceFoldersSkipsNullFoldersAndNullUris()
{
WorkspaceFolder valid = new()
{
Uri = DocumentUri.FromFileSystemPath("/home/runner/work/example"),
Name = "workspace"
};

Container<WorkspaceFolder> folders = new(
null,
new WorkspaceFolder { Name = "missing-uri" },
valid);

WorkspaceFolder[] result = PsesLanguageServer.GetValidWorkspaceFolders(folders).ToArray();

Assert.Equal(valid, Assert.Single(result));
}
}
}
Loading