From 307ce20f4ab78e43b97f1a0b0b6b288d14256611 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Mon, 3 Feb 2020 12:44:05 -0800 Subject: [PATCH 1/2] fix untitled file references --- .../Services/TextDocument/ScriptFile.cs | 41 +++++-------------- .../Services/Workspace/WorkspaceService.cs | 11 ++--- 2 files changed, 16 insertions(+), 36 deletions(-) diff --git a/src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs b/src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs index 51ddd7f1a..d08cb7197 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs @@ -150,20 +150,20 @@ public string[] ReferencedFiles /// Creates a new ScriptFile instance by reading file contents from /// the given TextReader. /// - /// The path at which the script file resides. - /// The path which the client uses to identify the file. + /// The System.Uri of the file. /// The TextReader to use for reading the file's contents. /// The version of PowerShell for which the script is being parsed. public ScriptFile( - string filePath, - string clientFilePath, + Uri fileUri, TextReader textReader, Version powerShellVersion) { - this.FilePath = filePath; - this.ClientFilePath = clientFilePath; + 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. @@ -173,41 +173,20 @@ public ScriptFile( /// /// Creates a new ScriptFile instance with the specified file contents. /// - /// The path at which the script file resides. - /// The path which the client uses to identify the file. + /// The System.Uri of the file. /// The initial contents of the script file. /// The version of PowerShell for which the script is being parsed. public ScriptFile( - string filePath, - string clientFilePath, + Uri fileUri, string initialBuffer, Version powerShellVersion) : this( - filePath, - clientFilePath, + fileUri, new StringReader(initialBuffer), powerShellVersion) { } - /// - /// Creates a new ScriptFile instance with the specified filepath. - /// - /// The path at which the script file resides. - /// The path which the client uses to identify the file. - /// The version of PowerShell for which the script is being parsed. - public ScriptFile( - string filePath, - string clientFilePath, - Version powerShellVersion) - : this( - filePath, - clientFilePath, - File.ReadAllText(filePath), - powerShellVersion) - { - } - #endregion #region Public Methods diff --git a/src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs b/src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs index dba1362c3..de8d66ca6 100644 --- a/src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs +++ b/src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs @@ -142,8 +142,7 @@ public ScriptFile GetFile(Uri fileUri) { scriptFile = new ScriptFile( - resolvedFileUri.LocalPath, - resolvedFileUri.OriginalString, + resolvedFileUri, streamReader, this.powerShellVersion); @@ -249,8 +248,7 @@ public ScriptFile GetFileBuffer(Uri fileUri, string initialBuffer) { scriptFile = new ScriptFile( - resolvedFileUri.LocalPath, - resolvedFileUri.OriginalString, + resolvedFileUri, initialBuffer, this.powerShellVersion); @@ -296,7 +294,10 @@ public ScriptFile[] ExpandScriptReferences(ScriptFile scriptFile) // add original file so it's not searched for, then find all file references referencedScriptFiles.Add(scriptFile.Id, scriptFile); - RecursivelyFindReferences(scriptFile, referencedScriptFiles); + if (!scriptFile.IsInMemory) + { + RecursivelyFindReferences(scriptFile, referencedScriptFiles); + } // remove original file from referened file and add it as the first element of the // expanded referenced list to maintain order so the original file is always first in the list From da7ded54e2853f1b400ca4f44427806a248970b4 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Mon, 3 Feb 2020 13:01:47 -0800 Subject: [PATCH 2/2] cleanup more code --- .../Services/TextDocument/ScriptFile.cs | 4 + .../Services/Workspace/WorkspaceService.cs | 90 +------------------ 2 files changed, 8 insertions(+), 86 deletions(-) diff --git a/src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs b/src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs index d08cb7197..edc0ac74b 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/ScriptFile.cs @@ -158,6 +158,10 @@ public ScriptFile( TextReader textReader, Version powerShellVersion) { + // 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; diff --git a/src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs b/src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs index de8d66ca6..5da819232 100644 --- a/src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs +++ b/src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs @@ -294,10 +294,7 @@ public ScriptFile[] ExpandScriptReferences(ScriptFile scriptFile) // add original file so it's not searched for, then find all file references referencedScriptFiles.Add(scriptFile.Id, scriptFile); - if (!scriptFile.IsInMemory) - { - RecursivelyFindReferences(scriptFile, referencedScriptFiles); - } + RecursivelyFindReferences(scriptFile, referencedScriptFiles); // remove original file from referened file and add it as the first element of the // expanded referenced list to maintain order so the original file is always first in the list @@ -400,9 +397,9 @@ private void RecursivelyFindReferences( Dictionary 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) { @@ -438,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) @@ -510,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( - 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; @@ -579,39 +530,6 @@ internal string ResolveRelativeScriptPath(string baseFilePath, string relativePa return combinedPath; } - /// - /// 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. - /// - /// The fully-escaped file-scheme URI string. - /// A file-scheme URI string with the drive colon unescaped. - 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))