Skip to content

Commit e9595a5

Browse files
committed
Play it safe by adding a new property instead of changing ClientPath
1 parent c09fa73 commit e9595a5

File tree

6 files changed

+73
-23
lines changed

6 files changed

+73
-23
lines changed

src/PowerShellEditorServices.Host/CodeLens/CodeLensFeature.cs

+1-1
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.DocumentUri,
130130
ProviderId = codeLensResults[i].Provider.ProviderId
131131
},
132132
_jsonSerializer);

src/PowerShellEditorServices.Host/CodeLens/PesterCodeLensProvider.cs

+4-6
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
{
@@ -53,7 +51,7 @@ private CodeLens[] GetPesterLens(PesterSymbolReference pesterSymbol, ScriptFile
5351
"PowerShell.RunPesterTests",
5452
"Run tests",
5553
new object[] {
56-
scriptFile.ClientFilePath,
54+
scriptFile.DocumentUri,
5755
false /* No debug */,
5856
pesterSymbol.TestName,
5957
pesterSymbol.ScriptRegion?.StartLineNumber })),
@@ -66,7 +64,7 @@ private CodeLens[] GetPesterLens(PesterSymbolReference pesterSymbol, ScriptFile
6664
"PowerShell.RunPesterTests",
6765
"Debug tests",
6866
new object[] {
69-
scriptFile.ClientFilePath,
67+
scriptFile.DocumentUri,
7068
true /* Run in the debugger */,
7169
pesterSymbol.TestName,
7270
pesterSymbol.ScriptRegion?.StartLineNumber })),

src/PowerShellEditorServices.Host/CodeLens/ReferencesCodeLensProvider.cs

+1-1
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.DocumentUri,
122122
codeLens.ScriptExtent.ToRange().Start,
123123
referenceLocations,
124124
}

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

+2-2
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.DocumentUri] = 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.DocumentUri,
17691769
Diagnostics = diagnostics.ToArray()
17701770
});
17711771
}

src/PowerShellEditorServices/Workspace/ScriptFile.cs

+10-11
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,18 @@ 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 file path in LSP DocumentUri form. The ClientPath property must not be null.
58+
/// </summary>
59+
public string DocumentUri
60+
{
61+
get
6062
{
61-
if (value == null)
62-
{
63-
throw new ArgumentNullException(nameof(value));
64-
}
65-
66-
_clientPath = GetPathAsClientPath(value);
63+
return (this.ClientFilePath == null )
64+
? string.Empty
65+
: Workspace.ConvertPathToDocumentUri(this.ClientFilePath);
6766
}
6867
}
6968

src/PowerShellEditorServices/Workspace/Workspace.cs

+55-2
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ private void RecursivelyEnumerateFiles(string folderPath, ref List<string> found
351351
this.logger.WriteHandledException(
352352
$"Could not enumerate files in the path '{folderPath}' due to an exception",
353353
e);
354-
354+
355355
continue;
356356
}
357357

@@ -400,7 +400,7 @@ private void RecursivelyEnumerateFiles(string folderPath, ref List<string> found
400400
this.logger.WriteHandledException(
401401
$"Could not enumerate directories in the path '{folderPath}' due to an exception",
402402
e);
403-
403+
404404
return;
405405
}
406406

@@ -625,6 +625,59 @@ private static string UnescapeDriveColon(string fileUri)
625625
return sb.ToString();
626626
}
627627

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

0 commit comments

Comments
 (0)