|
11 | 11 | using System.Security;
|
12 | 12 | using System.Text;
|
13 | 13 |
|
| 14 | +#if CoreCLR |
| 15 | +using System.Runtime.InteropServices; |
| 16 | +#endif |
| 17 | + |
14 | 18 | namespace Microsoft.PowerShell.EditorServices
|
15 | 19 | {
|
16 | 20 | /// <summary>
|
@@ -355,15 +359,15 @@ private void RecursivelyFindReferences(
|
355 | 359 | }
|
356 | 360 | }
|
357 | 361 |
|
358 |
| - private string ResolveFilePath(string filePath) |
| 362 | + internal string ResolveFilePath(string filePath) |
359 | 363 | {
|
360 | 364 | if (!IsPathInMemory(filePath))
|
361 | 365 | {
|
362 | 366 | if (filePath.StartsWith(@"file://"))
|
363 | 367 | {
|
| 368 | + filePath = Workspace.UnescapeDriveColon(filePath); |
364 | 369 | // Client sent the path in URI format, extract the local path
|
365 |
| - Uri fileUri = new Uri(Uri.UnescapeDataString(filePath)); |
366 |
| - filePath = fileUri.LocalPath; |
| 370 | + filePath = new Uri(filePath).LocalPath; |
367 | 371 | }
|
368 | 372 |
|
369 | 373 | // Clients could specify paths with escaped space, [ and ] characters which .NET APIs
|
@@ -486,6 +490,40 @@ private string ResolveRelativeScriptPath(string baseFilePath, string relativePat
|
486 | 490 | return combinedPath;
|
487 | 491 | }
|
488 | 492 |
|
| 493 | + /// <summary> |
| 494 | + /// Takes a file-scheme URI with an escaped colon after the drive letter and unescapes only the colon. |
| 495 | + /// VSCode sends escaped colons after drive letters, but System.Uri expects unescaped. |
| 496 | + /// </summary> |
| 497 | + /// <param name="fileUri">The fully-escaped file-scheme URI string.</param> |
| 498 | + /// <returns>A file-scheme URI string with the drive colon unescaped.</returns> |
| 499 | + private static string UnescapeDriveColon(string fileUri) |
| 500 | + { |
| 501 | +#if CoreCLR |
| 502 | + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
| 503 | + { |
| 504 | + return fileUri; |
| 505 | + } |
| 506 | +#endif |
| 507 | + // Check here that we have something like "file:///C%3A/" as a prefix (caller must check the file:// part) |
| 508 | + if (!(fileUri[7] == '/' && |
| 509 | + char.IsLetter(fileUri[8]) && |
| 510 | + fileUri[9] == '%' && |
| 511 | + fileUri[10] == '3' && |
| 512 | + fileUri[11] == 'A' && |
| 513 | + fileUri[12] == '/')) |
| 514 | + { |
| 515 | + return fileUri; |
| 516 | + } |
| 517 | + |
| 518 | + var sb = new StringBuilder(fileUri.Length - 2); // We lost "%3A" and gained ":", so length - 2 |
| 519 | + sb.Append("file:///"); |
| 520 | + sb.Append(fileUri[8]); // The drive letter |
| 521 | + sb.Append(':'); |
| 522 | + sb.Append(fileUri.Substring(12)); // The rest of the URI after the colon |
| 523 | + |
| 524 | + return sb.ToString(); |
| 525 | + } |
| 526 | + |
489 | 527 | #endregion
|
490 | 528 | }
|
491 | 529 | }
|
0 commit comments