Skip to content

Fix untitled file references #1177

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

Merged
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
45 changes: 14 additions & 31 deletions src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,20 +150,24 @@ public string[] ReferencedFiles
/// Creates a new ScriptFile instance by reading file contents from
/// the given TextReader.
/// </summary>
/// <param name="filePath">The path at which the script file resides.</param>
/// <param name="clientFilePath">The path which the client uses to identify the file.</param>
/// <param name="fileUri">The System.Uri of the file.</param>
/// <param name="textReader">The TextReader to use for reading the file's contents.</param>
/// <param name="powerShellVersion">The version of PowerShell for which the script is being parsed.</param>
public ScriptFile(
string filePath,
string clientFilePath,
Uri fileUri,
TextReader textReader,
Version powerShellVersion)
{
this.FilePath = filePath;
this.ClientFilePath = clientFilePath;
// For non-files, use their URI representation instead
// so that other operations know it's untitled/in-memory
// and don't think that it's a relative path
// on the file system.
this.FilePath = fileUri.IsFile
? fileUri.LocalPath
: fileUri.OriginalString;
this.ClientFilePath = fileUri.OriginalString;
this.IsAnalysisEnabled = true;
this.IsInMemory = WorkspaceService.IsPathInMemory(filePath);
this.IsInMemory = !fileUri.IsFile;
this.powerShellVersion = powerShellVersion;

// SetFileContents() calls ParseFileContents() which initializes the rest of the properties.
Expand All @@ -173,41 +177,20 @@ public ScriptFile(
/// <summary>
/// Creates a new ScriptFile instance with the specified file contents.
/// </summary>
/// <param name="filePath">The path at which the script file resides.</param>
/// <param name="clientFilePath">The path which the client uses to identify the file.</param>
/// <param name="fileUri">The System.Uri of the file.</param>
/// <param name="initialBuffer">The initial contents of the script file.</param>
/// <param name="powerShellVersion">The version of PowerShell for which the script is being parsed.</param>
public ScriptFile(
string filePath,
string clientFilePath,
Uri fileUri,
string initialBuffer,
Version powerShellVersion)
: this(
filePath,
clientFilePath,
fileUri,
new StringReader(initialBuffer),
powerShellVersion)
{
}

/// <summary>
/// Creates a new ScriptFile instance with the specified filepath.
/// </summary>
/// <param name="filePath">The path at which the script file resides.</param>
/// <param name="clientFilePath">The path which the client uses to identify the file.</param>
/// <param name="powerShellVersion">The version of PowerShell for which the script is being parsed.</param>
public ScriptFile(
string filePath,
string clientFilePath,
Version powerShellVersion)
: this(
filePath,
clientFilePath,
File.ReadAllText(filePath),
powerShellVersion)
{
}

#endregion

#region Public Methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ public ScriptFile GetFile(Uri fileUri)
{
scriptFile =
new ScriptFile(
resolvedFileUri.LocalPath,
resolvedFileUri.OriginalString,
resolvedFileUri,
streamReader,
this.powerShellVersion);

Expand Down Expand Up @@ -249,8 +248,7 @@ public ScriptFile GetFileBuffer(Uri fileUri, string initialBuffer)
{
scriptFile =
new ScriptFile(
resolvedFileUri.LocalPath,
resolvedFileUri.OriginalString,
resolvedFileUri,
initialBuffer,
this.powerShellVersion);

Expand Down Expand Up @@ -399,9 +397,9 @@ private void RecursivelyFindReferences(
Dictionary<string, ScriptFile> referencedScriptFiles)
{
// Get the base path of the current script for use in resolving relative paths
string baseFilePath =
GetBaseFilePath(
scriptFile.FilePath);
string baseFilePath = scriptFile.IsInMemory
? WorkspacePath
: Path.GetDirectoryName(scriptFile.FilePath);

foreach (string referencedFileName in scriptFile.ReferencedFiles)
{
Expand Down Expand Up @@ -437,31 +435,6 @@ private void RecursivelyFindReferences(
}
}

internal string ResolveFilePath(string filePath)
{
if (!IsPathInMemory(filePath))
{
if (filePath.StartsWith(@"file://"))
{
filePath = WorkspaceService.UnescapeDriveColon(filePath);
// Client sent the path in URI format, extract the local path
filePath = new Uri(filePath).LocalPath;
}

// Clients could specify paths with escaped space, [ and ] characters which .NET APIs
// will not handle. These paths will get appropriately escaped just before being passed
// into the PowerShell engine.
//filePath = PowerShellContext.UnescapeWildcardEscapedPath(filePath);

// Get the absolute file path
filePath = Path.GetFullPath(filePath);
}

this.logger.LogDebug("Resolved path: " + filePath);

return filePath;
}

internal Uri ResolveFileUri(Uri fileUri)
{
if (fileUri.IsFile)
Expand Down Expand Up @@ -509,27 +482,6 @@ internal static bool IsPathInMemory(string filePath)
return isInMemory;
}

private string GetBaseFilePath(string filePath)
{
if (IsPathInMemory(filePath))
{
// If the file is in memory, use the workspace path
return this.WorkspacePath;
}

if (!Path.IsPathRooted(filePath))
{
// TODO: Assert instead?
throw new InvalidOperationException(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can never happen because the client only ever sends AbsoluteUris

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically a client that isn't VSCode might do this (just realised this now)

string.Format(
"Must provide a full path for originalScriptPath: {0}",
filePath));
}

// Get the directory of the file path
return Path.GetDirectoryName(filePath);
}

internal string ResolveRelativeScriptPath(string baseFilePath, string relativePath)
{
string combinedPath = null;
Expand Down Expand Up @@ -578,39 +530,6 @@ internal string ResolveRelativeScriptPath(string baseFilePath, string relativePa
return combinedPath;
}

/// <summary>
/// Takes a file-scheme URI with an escaped colon after the drive letter and unescapes only the colon.
/// VSCode sends escaped colons after drive letters, but System.Uri expects unescaped.
/// </summary>
/// <param name="fileUri">The fully-escaped file-scheme URI string.</param>
/// <returns>A file-scheme URI string with the drive colon unescaped.</returns>
private static string UnescapeDriveColon(string fileUri)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return fileUri;
}

// Check here that we have something like "file:///C%3A/" as a prefix (caller must check the file:// part)
if (!(fileUri[7] == '/' &&
char.IsLetter(fileUri[8]) &&
fileUri[9] == '%' &&
fileUri[10] == '3' &&
fileUri[11] == 'A' &&
fileUri[12] == '/'))
{
return fileUri;
}

var sb = new StringBuilder(fileUri.Length - 2); // We lost "%3A" and gained ":", so length - 2
sb.Append("file:///");
sb.Append(fileUri[8]); // The drive letter
sb.Append(':');
sb.Append(fileUri.Substring(12)); // The rest of the URI after the colon

return sb.ToString();
}

private static Uri UnescapeDriveColon(Uri fileUri)
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
Expand Down