diff --git a/src/SLCore.UnitTests/Common/Models/FileUriTests.cs b/src/SLCore.UnitTests/Common/Models/FileUriTests.cs
index bde788b15..1c410da28 100644
--- a/src/SLCore.UnitTests/Common/Models/FileUriTests.cs
+++ b/src/SLCore.UnitTests/Common/Models/FileUriTests.cs
@@ -134,6 +134,47 @@ public void LocalPath_ReturnsCorrectPath()
new FileUri(filePath).LocalPath.Should().Be(filePath);
}
+ [TestMethod]
+ [DataRow("[", "%5B")]
+ [DataRow("]", "%5D")]
+ [DataRow("#", "%2523")]
+ [DataRow("@", "%40")]
+ [DataRow(" ", "%20")]
+ [DataRow("`", "%60")]
+ public void LocalPath_UnescapesEncodesCharacters(string reservedChar, string expectedEncoding)
+ {
+ var expectedFilePath = @$"C:\filewithRfc3986ReservedChar{reservedChar}.cs";
+ var encodedFilePath = @$"file:///C:/filewithRfc3986ReservedChar{expectedEncoding}.cs";
+
+ new FileUri(encodedFilePath).LocalPath.Should().Be(expectedFilePath);
+ }
+
+ [TestMethod]
+ [DataRow("[")]
+ [DataRow("]")]
+ [DataRow("@")]
+ [DataRow(" ")]
+ [DataRow("`")]
+ public void LocalPath_PathDoesNotHaveEncodedChars_ReturnsCorrectLocalPath(string reservedChar)
+ {
+ var notEncodedFilePath = @$"file:///C:/filewithRfc3986ReservedChar{reservedChar}.cs";
+ var expectedFilePath = @$"C:\filewithRfc3986ReservedChar{reservedChar}.cs";
+
+ new FileUri(notEncodedFilePath).LocalPath.Should().Be(expectedFilePath);
+ }
+
+ ///
+ /// The # character as the beginning of a fragment, so will return the path without the fragment.
+ ///
+ [TestMethod]
+ public void LocalPath_PathWithHashCharacter_ReturnsLocalPathWithoutHash()
+ {
+ var notEncodedFilePath = @$"file:///C:/filewithRfc3986ReservedChar#.cs";
+ var expectedFilePath = @$"C:\filewithRfc3986ReservedChar";
+
+ new FileUri(notEncodedFilePath).LocalPath.Should().Be(expectedFilePath);
+ }
+
[TestMethod]
public void SerializeDeserializeToEqualObject()
{
diff --git a/src/SLCore/Common/Models/FileUri.cs b/src/SLCore/Common/Models/FileUri.cs
index 775de8ab4..ede896602 100644
--- a/src/SLCore/Common/Models/FileUri.cs
+++ b/src/SLCore/Common/Models/FileUri.cs
@@ -20,8 +20,7 @@
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Linq;
+using System.Text;
using SonarLint.VisualStudio.SLCore.Protocol;
namespace SonarLint.VisualStudio.SLCore.Common.Models;
@@ -30,7 +29,7 @@ namespace SonarLint.VisualStudio.SLCore.Common.Models;
public sealed class FileUri
{
private readonly Uri uri;
- private static readonly char[] Rfc3986ReservedCharsToEncoding = ['?', '#', '[', ']', '@'];
+ private static readonly char[] Rfc3986ReservedCharsToEncode = ['#', '[', ']', '@'];
public FileUri(string uriString)
{
@@ -56,11 +55,11 @@ public override string ToString()
///
private static string EscapeRfc3986ReservedCharacters(string stringToEscape)
{
- var charsToEscape = Rfc3986ReservedCharsToEncoding.Where(stringToEscape.Contains).ToList();
-
- return !charsToEscape.Any()
- ? stringToEscape
- : charsToEscape.Aggregate(stringToEscape, (current, charToEscape) => current.Replace(charToEscape.ToString(), Uri.HexEscape(charToEscape)));
+ var stringBuilderToEscape = new StringBuilder(stringToEscape);
+
+ return Rfc3986ReservedCharsToEncode.Aggregate(stringBuilderToEscape,
+ (current, charToEscape) => current.Replace(charToEscape.ToString(), Uri.HexEscape(charToEscape)))
+ .ToString();
}
[ExcludeFromCodeCoverage]