Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry pick PR 1750 merge commit to legacy/v1.x, has additional fixes #887

Merged
merged 1 commit into from
Mar 22, 2019
Merged
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
Cherry pick PR 1750 merge commit to legacy/v1.x, has additional fixes
  • Loading branch information
rkeithhill committed Mar 21, 2019
commit 9011fab8bc1234cdcc8ba7e7ffb1f65017e39187
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ private static string GetFileUri(string filePath)
// If the file isn't untitled, return a URI-style path
return
!filePath.StartsWith("untitled") && !filePath.StartsWith("inmemory")
? new Uri("file://" + filePath).AbsoluteUri
? Workspace.ConvertPathToDocumentUri(filePath)
: filePath;
}

Expand Down
66 changes: 33 additions & 33 deletions src/PowerShellEditorServices/Workspace/Workspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -637,55 +637,55 @@ private static string UnescapeDriveColon(string fileUri)
/// A file system path. Note: if the path is already a DocumentUri, it will be returned unmodified.
/// </param>
/// <returns>The file system path encoded as a DocumentUri.</returns>
internal static string ConvertPathToDocumentUri(string path)
public static string ConvertPathToDocumentUri(string path)
TylerLeonhardt marked this conversation as resolved.
Show resolved Hide resolved
{
const string fileUriPrefix = "file:///";
const string untitledUriPrefix = "untitled:";

if (path.StartsWith("untitled:", StringComparison.Ordinal))
// If path is already in document uri form, there is nothing to convert.
if (path.StartsWith(untitledUriPrefix, StringComparison.Ordinal) ||
path.StartsWith(fileUriPrefix, StringComparison.Ordinal))
{
return path;
}

if (path.StartsWith(fileUriPrefix, StringComparison.Ordinal))
{
return path;
}
string escapedPath = Uri.EscapeDataString(path);
var docUriStrBld = new StringBuilder(escapedPath);

if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// On a Linux filesystem, you can have multiple colons in a filename e.g. foo:bar:baz.txt
string absoluteUri = new Uri(path).AbsoluteUri;

// First colon is part of the protocol scheme, see if there are other colons in the path
int firstColonIndex = absoluteUri.IndexOf(':');
if (absoluteUri.IndexOf(':', firstColonIndex + 1) > firstColonIndex)
// VSCode file URIs on Windows need the drive letter lowercase.
// Search original path for colon since a char search (no string culture involved)
// is faster than a string search.
if (path.Contains(':'))
{
absoluteUri = new StringBuilder(absoluteUri)
.Replace(
oldValue: ":",
newValue: "%3A",
startIndex: firstColonIndex + 1,
count: absoluteUri.Length - firstColonIndex - 1)
.ToString();
// Start at index 1 to avoid an index out of range check when accessing index - 1.
// Also, if the colon is at index 0 there is no drive letter before it to lower case.
for (int i = 1; i < docUriStrBld.Length - 2; i++)
{
if ((docUriStrBld[i] == '%') && (docUriStrBld[i + 1] == '3') && (docUriStrBld[i + 2] == 'A'))
{
int driveLetterIndex = i - 1;
char driveLetter = char.ToLowerInvariant(docUriStrBld[driveLetterIndex]);
docUriStrBld.Replace(path[driveLetterIndex], driveLetter, driveLetterIndex, 1);
break;
}
}
}

return absoluteUri;
// Uri.EscapeDataString goes a bit far, encoding \ chars. Also, VSCode wants / instead of \.
docUriStrBld.Replace("%5C", "/");
}

// VSCode file URIs on Windows need the drive letter lowercase, and the colon
// URI encoded. System.Uri won't do that, so we manually create the URI.
var newUri = new StringBuilder(System.Web.HttpUtility.UrlPathEncode(path));
int colonIndex = path.IndexOf(':');
if (colonIndex > 0)
else
{
int driveLetterIndex = colonIndex - 1;
char driveLetter = char.ToLowerInvariant(path[driveLetterIndex]);
newUri
.Replace(path[driveLetterIndex], driveLetter, driveLetterIndex, 1)
.Replace(":", "%3A", colonIndex, 1);
// Because we will prefix later with file:///, remove the initial encoded / if this is an absolute path.
// See https://docs.microsoft.com/en-us/dotnet/api/system.uri?view=netframework-4.7.2#implicit-file-path-support
// Uri.EscapeDataString goes a bit far, encoding / chars.
docUriStrBld.Replace("%2F", string.Empty, 0, 3).Replace("%2F", "/");
}

return newUri.Replace('\\', '/').Insert(0, fileUriPrefix).ToString();
// ' is not always encoded. I've seen this in Windows PowerShell.
return docUriStrBld.Replace("'", "%27").Insert(0, fileUriPrefix).ToString();
}

#endregion
Expand Down
8 changes: 8 additions & 0 deletions test/PowerShellEditorServices.Test/Session/ScriptFileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,10 @@ public void DocumentUriRetunsCorrectStringForAbsolutePath()
path = @"C:\Users\AmosBurton\projects\Rocinate\ProtoMolecule.ps1";
scriptFile = new ScriptFile(path, path, emptyStringReader, PowerShellVersion);
Assert.Equal("file:///c%3A/Users/AmosBurton/projects/Rocinate/ProtoMolecule.ps1", scriptFile.DocumentUri);

path = @"c:\Users\BobbyDraper\projects\Rocinate\foo's_~#-[@] +,;=%.ps1";
scriptFile = new ScriptFile(path, path, emptyStringReader, PowerShellVersion);
Assert.Equal("file:///c%3A/Users/BobbyDraper/projects/Rocinate/foo%27s_~%23-%5B%40%5D%20%2B%2C%3B%3D%25.ps1", scriptFile.DocumentUri);
}
else
{
Expand All @@ -602,6 +606,10 @@ public void DocumentUriRetunsCorrectStringForAbsolutePath()
scriptFile = new ScriptFile(path, path, emptyStringReader, PowerShellVersion);
Assert.Equal("file:///home/AlexKamal/projects/Rocinate/ProtoMolecule.ps1", scriptFile.DocumentUri);

path = "/home/BobbyDraper/projects/Rocinate/foo's_~#-[@] +,;=%.ps1";
scriptFile = new ScriptFile(path, path, emptyStringReader, PowerShellVersion);
Assert.Equal("file:///home/BobbyDraper/projects/Rocinate/foo%27s_~%23-%5B%40%5D%20%2B%2C%3B%3D%25.ps1", scriptFile.DocumentUri);

path = "/home/NaomiNagata/projects/Rocinate/Proto:Mole:cule.ps1";
scriptFile = new ScriptFile(path, path, emptyStringReader, PowerShellVersion);
Assert.Equal("file:///home/NaomiNagata/projects/Rocinate/Proto%3AMole%3Acule.ps1", scriptFile.DocumentUri);
Expand Down