Skip to content

Commit af3629b

Browse files
committed
Play it safe by adding a new property instead of changing ClientPath
1 parent 3d4c333 commit af3629b

File tree

6 files changed

+69
-21
lines changed

6 files changed

+69
-21
lines changed

src/PowerShellEditorServices.Host/CodeLens/CodeLensFeature.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ private async Task HandleCodeLensRequestAsync(
126126
codeLensResponse[i] = codeLensResults[i].ToProtocolCodeLens(
127127
new CodeLensData
128128
{
129-
Uri = codeLensResults[i].File.ClientFilePath,
129+
Uri = codeLensResults[i].File.ClientPathAsDocumentUri,
130130
ProviderId = codeLensResults[i].Provider.ProviderId
131131
},
132132
_jsonSerializer);

src/PowerShellEditorServices.Host/CodeLens/PesterCodeLensProvider.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@
33
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
44
//
55

6-
using Microsoft.PowerShell.EditorServices.Commands;
7-
using Microsoft.PowerShell.EditorServices.Symbols;
8-
using System;
96
using System.Collections.Generic;
10-
using System.Linq;
117
using System.Threading;
128
using System.Threading.Tasks;
9+
using Microsoft.PowerShell.EditorServices.Commands;
10+
using Microsoft.PowerShell.EditorServices.Symbols;
1311

1412
namespace Microsoft.PowerShell.EditorServices.CodeLenses
1513
{
@@ -54,7 +52,7 @@ private CodeLens[] GetPesterLens(
5452
new ClientCommand(
5553
"PowerShell.RunPesterTests",
5654
"Run tests",
57-
new object[] { scriptFile.ClientFilePath, false /* No debug */, pesterSymbol.TestName })),
55+
new object[] { scriptFile.ClientPathAsDocumentUri, false /* No debug */, pesterSymbol.TestName })),
5856

5957
new CodeLens(
6058
this,
@@ -63,7 +61,7 @@ private CodeLens[] GetPesterLens(
6361
new ClientCommand(
6462
"PowerShell.RunPesterTests",
6563
"Debug tests",
66-
new object[] { scriptFile.ClientFilePath, true /* Run in debugger */, pesterSymbol.TestName })),
64+
new object[] { scriptFile.ClientPathAsDocumentUri, true /* Run in debugger */, pesterSymbol.TestName })),
6765
};
6866

6967
return codeLensResults;

src/PowerShellEditorServices.Host/CodeLens/ReferencesCodeLensProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public async Task<CodeLens> ResolveCodeLensAsync(
118118
GetReferenceCountHeader(referenceLocations.Length),
119119
new object[]
120120
{
121-
codeLens.File.ClientFilePath,
121+
codeLens.File.ClientPathAsDocumentUri,
122122
codeLens.ScriptExtent.ToRange().Start,
123123
referenceLocations,
124124
}

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,15 +1757,15 @@ private static async Task PublishScriptDiagnosticsAsync(
17571757
diagnostics.Add(markerDiagnostic);
17581758
}
17591759

1760-
correctionIndex[scriptFile.ClientFilePath] = fileCorrections;
1760+
correctionIndex[scriptFile.ClientPathAsDocumentUri] = fileCorrections;
17611761

17621762
// Always send syntax and semantic errors. We want to
17631763
// make sure no out-of-date markers are being displayed.
17641764
await eventSender(
17651765
PublishDiagnosticsNotification.Type,
17661766
new PublishDiagnosticsNotification
17671767
{
1768-
Uri = scriptFile.ClientFilePath,
1768+
Uri = scriptFile.ClientPathAsDocumentUri,
17691769
Diagnostics = diagnostics.ToArray()
17701770
});
17711771
}

src/PowerShellEditorServices/Workspace/ScriptFile.cs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ public class ScriptFile
2828
};
2929

3030
private Version powerShellVersion;
31-
private string _clientPath;
3231

3332
#endregion
3433

@@ -52,18 +51,16 @@ public string Id
5251
/// <summary>
5352
/// Gets the path which the editor client uses to identify this file.
5453
/// </summary>
55-
public string ClientFilePath
56-
{
57-
get { return _clientPath; }
54+
public string ClientFilePath { get; private set; }
5855

59-
private set
56+
/// <summary>
57+
/// Gets the ClientPath in LSP DocumentUri form. The ClientPath property must not be null.
58+
/// </summary>
59+
public string ClientPathAsDocumentUri
60+
{
61+
get
6062
{
61-
if (value == null)
62-
{
63-
throw new ArgumentNullException(nameof(value));
64-
}
65-
66-
_clientPath = GetPathAsClientPath(value);
63+
return Workspace.ConvertPathToDocumentUri(this.ClientFilePath);
6764
}
6865
}
6966

src/PowerShellEditorServices/Workspace/Workspace.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,59 @@ private static string UnescapeDriveColon(string fileUri)
624624
return sb.ToString();
625625
}
626626

627+
/// <summary>
628+
/// Converts a file system path into a DocumentUri required by Language Server Protocol.
629+
/// </summary>
630+
/// <remarks>
631+
/// When sending a document path to a LSP client, the path must be provided as a
632+
/// DocumentUri in order to features like the Problems window or peek definition
633+
/// to be able to open the specified file.
634+
/// </remarks>
635+
/// <param name="path">
636+
/// A file system path. Note: if the path is already a DocumentUri, it will be returned unmodified.
637+
/// </param>
638+
/// <returns>The file system path encoded as a DocumentUri.</returns>
639+
internal static string ConvertPathToDocumentUri(string path)
640+
{
641+
const string fileUriPrefix = "file:///";
642+
643+
if (path.StartsWith("untitled:", StringComparison.Ordinal))
644+
{
645+
return path;
646+
}
647+
648+
if (path.StartsWith(fileUriPrefix, StringComparison.Ordinal))
649+
{
650+
return path;
651+
}
652+
653+
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
654+
{
655+
return new Uri(path).AbsoluteUri;
656+
}
657+
658+
// VSCode file URIs on Windows need the drive letter lowercase, and the colon
659+
// URI encoded. System.Uri won't do that, so we manually create the URI.
660+
var newUri = System.Web.HttpUtility.UrlPathEncode(path);
661+
int colonIndex = path.IndexOf(":");
662+
for (var i = colonIndex - 1; i >= 0; i--)
663+
{
664+
newUri.Remove(i, 1);
665+
newUri.Insert(i, char.ToLowerInvariant(path[i]).ToString());
666+
}
667+
668+
// On a Linux filesystem, you can have multiple colons in a filename e.g. foo:bar:baz.txt
669+
if (colonIndex >= 0)
670+
{
671+
newUri = newUri.Replace(":", "%3A");
672+
}
673+
674+
return newUri
675+
.Replace("\\", "/")
676+
.Insert(0, fileUriPrefix)
677+
.ToString();
678+
}
679+
627680
#endregion
628681
}
629682
}

0 commit comments

Comments
 (0)