From fa8a70295186f7a47cb79c26bde35d4e328865d6 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 15 Mar 2024 14:54:57 +1100 Subject: [PATCH] Port case-sensitivity fixes from main --- .../Cohost/CohostProjectSnapshot.cs | 2 +- .../Utilities/FilePathNormalizer.cs | 16 ++++------------ .../Utilities/FilePathNormalizingComparer.cs | 19 +++++++++++++++++++ .../ProjectSystem/ProjectSnapshot.cs | 2 +- .../ProjectSystem/ProjectState.cs | 10 +++++----- .../FilePathNormalizerTest.cs | 19 +++++++++++++++++++ 6 files changed, 49 insertions(+), 19 deletions(-) create mode 100644 src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizingComparer.cs diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Cohost/CohostProjectSnapshot.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Cohost/CohostProjectSnapshot.cs index 6ef36020bde..a9a2fc6e34c 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Cohost/CohostProjectSnapshot.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Cohost/CohostProjectSnapshot.cs @@ -56,7 +56,7 @@ public CohostProjectSnapshot(Project project, DocumentSnapshotFactory documentSn _importsToRelatedDocumentsLazy = new Lazy>>(() => { - var importsToRelatedDocuments = ImmutableDictionary.Create>(FilePathNormalizer.Comparer); + var importsToRelatedDocuments = ImmutableDictionary.Create>(FilePathNormalizingComparer.Instance); foreach (var document in DocumentFilePaths) { var importTargetPaths = ProjectState.GetImportDocumentTargetPaths(document, FileKinds.GetFileKindFromFilePath(document), _lazyProjectEngine.Value); diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs index d434e6cd14d..a3bb45aff51 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizer.cs @@ -3,8 +3,6 @@ using System; using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.Razor; @@ -14,15 +12,9 @@ namespace Microsoft.AspNetCore.Razor.Utilities; internal static class FilePathNormalizer { - private static Lazy> _lazyComparer = new Lazy>(() => new FilePathNormalizingComparer()); - public static IEqualityComparer Comparer => _lazyComparer.Value; - - private class FilePathNormalizingComparer : IEqualityComparer - { - public bool Equals(string? x, string? y) => FilePathNormalizer.FilePathsEquivalent(x, y); - - public int GetHashCode([DisallowNull] string obj) => FilePathNormalizer.GetHashCode(obj); - } + private static readonly Func s_charConverter = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) + ? c => c + : char.ToLowerInvariant; public static string NormalizeDirectory(string? directoryFilePath) { @@ -147,7 +139,7 @@ public static int GetHashCode(string filePath) foreach (var ch in normalizedSpan) { - hashCombiner.Add(ch); + hashCombiner.Add(s_charConverter(ch)); } return hashCombiner.CombinedHash; diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizingComparer.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizingComparer.cs new file mode 100644 index 00000000000..9f672a0e405 --- /dev/null +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.ProjectEngineHost/Utilities/FilePathNormalizingComparer.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.Razor.Utilities; + +internal sealed class FilePathNormalizingComparer : IEqualityComparer +{ + public static readonly FilePathNormalizingComparer Instance = new(); + + private FilePathNormalizingComparer() + { + } + + public bool Equals(string? x, string? y) => FilePathNormalizer.FilePathsEquivalent(x, y); + + public int GetHashCode(string obj) => FilePathNormalizer.GetHashCode(obj); +} diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectSnapshot.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectSnapshot.cs index bb879fe591d..4d20ef59d55 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectSnapshot.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectSnapshot.cs @@ -26,7 +26,7 @@ public ProjectSnapshot(ProjectState state) State = state ?? throw new ArgumentNullException(nameof(state)); _lock = new object(); - _documents = new Dictionary(FilePathNormalizer.Comparer); + _documents = new Dictionary(FilePathNormalizingComparer.Instance); } public ProjectKey Key => State.HostProject.Key; diff --git a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectState.cs b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectState.cs index 586b6dae2e6..e8db1a11848 100644 --- a/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectState.cs +++ b/src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/ProjectSystem/ProjectState.cs @@ -31,8 +31,8 @@ internal class ProjectState ProjectDifference.DocumentAdded | ProjectDifference.DocumentRemoved; - private static readonly ImmutableDictionary s_emptyDocuments = ImmutableDictionary.Create(FilePathNormalizer.Comparer); - private static readonly ImmutableDictionary> s_emptyImportsToRelatedDocuments = ImmutableDictionary.Create>(FilePathNormalizer.Comparer); + private static readonly ImmutableDictionary s_emptyDocuments = ImmutableDictionary.Create(FilePathNormalizingComparer.Instance); + private static readonly ImmutableDictionary> s_emptyImportsToRelatedDocuments = ImmutableDictionary.Create>(FilePathNormalizingComparer.Instance); private readonly object _lock; private readonly IProjectEngineFactoryProvider _projectEngineFactoryProvider; @@ -360,7 +360,7 @@ public ProjectState WithHostProject(HostProject hostProject) return this; } - var documents = Documents.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.WithConfigurationChange(), FilePathNormalizer.Comparer); + var documents = Documents.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.WithConfigurationChange(), FilePathNormalizingComparer.Instance); // If the host project has changed then we need to recompute the imports map var importsToRelatedDocuments = s_emptyImportsToRelatedDocuments; @@ -388,7 +388,7 @@ public ProjectState WithProjectWorkspaceState(ProjectWorkspaceState projectWorks } var difference = ProjectDifference.ProjectWorkspaceStateChanged; - var documents = Documents.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.WithProjectWorkspaceStateChange(), FilePathNormalizer.Comparer); + var documents = Documents.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.WithProjectWorkspaceStateChange(), FilePathNormalizingComparer.Instance); var state = new ProjectState(this, difference, HostProject, projectWorkspaceState, documents, ImportsToRelatedDocuments); return state; } @@ -448,7 +448,7 @@ internal static List GetImportDocumentTargetPaths(string targetPath, str { var itemTargetPath = importItem.FilePath.Replace('/', '\\').TrimStart('\\'); - if (FilePathNormalizer.Comparer.Equals(itemTargetPath, targetPath)) + if (FilePathNormalizingComparer.Instance.Equals(itemTargetPath, targetPath)) { // We've normalized the original importItem.FilePath into the HostDocument.TargetPath. For instance, if the HostDocument.TargetPath // was '/_Imports.razor' it'd be normalized down into '_Imports.razor'. The purpose of this method is to get the associated document diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/FilePathNormalizerTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/FilePathNormalizerTest.cs index 44277dab26e..23c6cc8dfae 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/FilePathNormalizerTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.ProjectEngineHost.Test/FilePathNormalizerTest.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT license. See License.txt in the project root for license information. +using System.Collections.Generic; using Microsoft.AspNetCore.Razor.Test.Common; using Microsoft.AspNetCore.Razor.Utilities; using Xunit; @@ -240,4 +241,22 @@ public void Normalize_ReplacesBackSlashesWithForwardSlashes() // Assert Assert.Equal("C:/path/to/document.cshtml", normalized); } + + [OSSkipConditionTheory(["OSX", "Linux"])] + [InlineData(@"C:\path\to\document.cshtml")] + [InlineData(@"c:\path\to\document.cshtml")] + [InlineData("C:/path/to/document.cshtml")] + [InlineData("c:/path/to/document.cshtml")] + public void Comparer_CaseInsensitiveDictionary(string fileName) + { + var dictionary = new Dictionary(FilePathNormalizingComparer.Instance) + { + { "C:/path/to/document.cshtml", true }, + { "C:/path/to/document1.cshtml", true }, + { "C:/path/to/document2.cshtml", true } + }; + + Assert.True(dictionary.ContainsKey(fileName)); + Assert.True(dictionary.TryGetValue(fileName, out _)); + } }