diff --git a/src/PowerShellEditorServices/Workspace/Workspace.cs b/src/PowerShellEditorServices/Workspace/Workspace.cs index 3b5749f5e..7b192cfed 100644 --- a/src/PowerShellEditorServices/Workspace/Workspace.cs +++ b/src/PowerShellEditorServices/Workspace/Workspace.cs @@ -390,22 +390,30 @@ internal static bool IsPathInMemory(string filePath) // view of the current file or an untitled file. try { - // NotSupportedException or ArgumentException gets thrown when - // given an invalid path. Since any non-standard path will - // trigger this, assume that it means it's an in-memory file - // unless the path starts with file: - Path.GetFullPath(filePath); + // File system absoulute paths will have a URI scheme of file:. + // Other schemes like "untitled:" and "gitlens-git:" will return false for IsFile. + var uri = new Uri(filePath); + isInMemory = !uri.IsFile; } - catch (ArgumentException) + catch (UriFormatException) { - isInMemory = true; - } - catch (NotSupportedException) - { - isInMemory = true; + // Relative file paths cause a UriFormatException. + // In this case, fallback to using Path.GetFullPath(). + try + { + Path.GetFullPath(filePath); + } + catch (Exception ex) when (ex is ArgumentException || ex is NotSupportedException) + { + isInMemory = true; + } + catch (PathTooLongException) + { + // If we ever get here, it should be an actual file so, not in memory + } } - return !filePath.ToLower().StartsWith("file:") && isInMemory; + return isInMemory; } private string GetBaseFilePath(string filePath) diff --git a/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs b/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs index 4dfdabdc9..41679285f 100644 --- a/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs +++ b/test/PowerShellEditorServices.Test/Session/WorkspaceTests.cs @@ -3,12 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -using Microsoft.PowerShell.EditorServices; using System; using System.IO; -using System.Linq; -using Xunit; using Microsoft.PowerShell.EditorServices.Utility; +using Xunit; namespace Microsoft.PowerShell.EditorServices.Test.Session { @@ -35,5 +33,41 @@ public void CanResolveWorkspaceRelativePath() Assert.Equal(@"..\PeerPath\FilePath.ps1", workspace.GetRelativePath(testPathOutside)); Assert.Equal(testPathAnotherDrive, workspace.GetRelativePath(testPathAnotherDrive)); } + + [Fact] + public void CanDetermineIsPathInMemory() + { + var tempDir = Environment.GetEnvironmentVariable("TEMP"); + var shortDirPath = Path.Combine(tempDir, "GitHub", "PowerShellEditorServices"); + var shortFilePath = Path.Combine(shortDirPath, "foo.ps1"); + var shortUriForm = "git:/c%3A/Users/Keith/GitHub/dahlbyk/posh-git/src/PoshGitTypes.ps1?%7B%22path%22%3A%22c%3A%5C%5CUsers%5C%5CKeith%5C%5CGitHub%5C%5Cdahlbyk%5C%5Cposh-git%5C%5Csrc%5C%5CPoshGitTypes.ps1%22%2C%22ref%22%3A%22~%22%7D"; + var longUriForm = "gitlens-git:c%3A%5CUsers%5CKeith%5CGitHub%5Cdahlbyk%5Cposh-git%5Csrc%5CPoshGitTypes%3Ae0022701.ps1?%7B%22fileName%22%3A%22src%2FPoshGitTypes.ps1%22%2C%22repoPath%22%3A%22c%3A%2FUsers%2FKeith%2FGitHub%2Fdahlbyk%2Fposh-git%22%2C%22sha%22%3A%22e0022701fa12e0bc22d0458673d6443c942b974a%22%7D"; + + var testCases = new[] { + // Test short file absolute paths + new { IsInMemory = false, Path = shortDirPath }, + new { IsInMemory = false, Path = shortFilePath }, + new { IsInMemory = false, Path = new Uri(shortDirPath).ToString() }, + new { IsInMemory = false, Path = new Uri(shortFilePath).ToString() }, + + // Test short file relative paths - not sure we'll ever get these but just in case + new { IsInMemory = false, Path = "foo.ps1" }, + new { IsInMemory = false, Path = ".." + Path.DirectorySeparatorChar + "foo.ps1" }, + + // Test short non-file paths + new { IsInMemory = true, Path = "untitled:untitled-1" }, + new { IsInMemory = true, Path = shortUriForm }, + + // Test long non-file path - known to have crashed PSES + new { IsInMemory = true, Path = longUriForm }, + }; + + foreach (var testCase in testCases) + { + Assert.True( + Workspace.IsPathInMemory(testCase.Path) == testCase.IsInMemory, + $"Testing path {testCase.Path}"); + } + } } }