From 01e9ae4c78de73d384db16010197c6bfff6ecbfa Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Wed, 14 Jan 2026 13:14:18 +1100 Subject: [PATCH 1/6] Use Frozen collections for improved performance --- docs/exclusion.md | 4 +- src/Directory.Packages.props | 1 + src/MarkdownSnippets/ContentValidation.cs | 6 +- .../Downloader/FileNameFromUrl.cs | 2 +- src/MarkdownSnippets/GlobalUsings.cs | 3 +- src/MarkdownSnippets/MarkdownSnippets.csproj | 1 + .../Exclusions/SnippetFileExclusions.cs | 76 +++++++++++++++---- .../Reading/FileSnippetExtractor.cs | 8 +- 8 files changed, 74 insertions(+), 27 deletions(-) diff --git a/docs/exclusion.md b/docs/exclusion.md index 1a907531..b22175f8 100644 --- a/docs/exclusion.md +++ b/docs/exclusion.md @@ -361,7 +361,7 @@ All binary files as defined by https://github.com/sindresorhus/binary-extensions "zip", "zipx" ``` -snippet source | anchor +snippet source | anchor @@ -378,5 +378,5 @@ Files that cannot contain comments are excluded. "geojson", "sln" ``` -snippet source | anchor +snippet source | anchor diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index eff6b0d7..5be8504e 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -12,6 +12,7 @@ + diff --git a/src/MarkdownSnippets/ContentValidation.cs b/src/MarkdownSnippets/ContentValidation.cs index 2d077103..2a9bb608 100644 --- a/src/MarkdownSnippets/ContentValidation.cs +++ b/src/MarkdownSnippets/ContentValidation.cs @@ -1,6 +1,6 @@ static class ContentValidation { - static Dictionary phrases = new() + static FrozenDictionary phrases = new Dictionary { {"a majority of ", "most"}, {"a number of", "some or many"}, @@ -32,7 +32,7 @@ static class ContentValidation {"a lot", "many"}, {"sort of", "similar or approximately"}, {"kind of", "similar or approximately "} - }; + }.ToFrozenDictionary(); static List invalidStrings; @@ -135,4 +135,4 @@ static string Clean(string input) span[index] = ' '; }); } -} \ No newline at end of file +} diff --git a/src/MarkdownSnippets/Downloader/FileNameFromUrl.cs b/src/MarkdownSnippets/Downloader/FileNameFromUrl.cs index 2f8d78a8..f4473ea7 100644 --- a/src/MarkdownSnippets/Downloader/FileNameFromUrl.cs +++ b/src/MarkdownSnippets/Downloader/FileNameFromUrl.cs @@ -1,6 +1,6 @@ static class FileNameFromUrl { - static HashSet invalid = [..Path.GetInvalidFileNameChars().Concat(Path.GetInvalidPathChars())]; + static FrozenSet invalid = Path.GetInvalidFileNameChars().Concat(Path.GetInvalidPathChars()).ToFrozenSet(); public static string ConvertToFileName(string url) { diff --git a/src/MarkdownSnippets/GlobalUsings.cs b/src/MarkdownSnippets/GlobalUsings.cs index 42f8fa5e..85fa0932 100644 --- a/src/MarkdownSnippets/GlobalUsings.cs +++ b/src/MarkdownSnippets/GlobalUsings.cs @@ -1,4 +1,5 @@ -global using System.Diagnostics.CodeAnalysis; +global using System.Collections.Frozen; +global using System.Diagnostics.CodeAnalysis; global using System.Net; global using System.Net.Http; global using System.Text.RegularExpressions; diff --git a/src/MarkdownSnippets/MarkdownSnippets.csproj b/src/MarkdownSnippets/MarkdownSnippets.csproj index b51bbf65..a50b402b 100644 --- a/src/MarkdownSnippets/MarkdownSnippets.csproj +++ b/src/MarkdownSnippets/MarkdownSnippets.csproj @@ -7,6 +7,7 @@ + diff --git a/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs b/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs index de185e6d..9c0e65e7 100644 --- a/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs +++ b/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs @@ -3,28 +3,48 @@ public static class SnippetFileExclusions { public static bool IsBinary(string extension) => - BinaryFileExtensions.Contains(extension); + binaryFileExtensionsFrozen.Contains(extension); public static bool CanContainCommentsExtension(string extension) => - !NoAcceptCommentsExtensions.Contains(extension); + !noAcceptCommentsExtensionsFrozen.Contains(extension); - public static HashSet NoAcceptCommentsExtensions { get; set; } = - new(StringComparer.OrdinalIgnoreCase) - { - //files that dont accept comments hence cant contain snippets + static FrozenSet noAcceptCommentsExtensionsFrozen; + static FrozenSet binaryFileExtensionsFrozen; - #region NoAcceptCommentsExtensions + static HashSet noAcceptCommentsExtensions = new(StringComparer.OrdinalIgnoreCase) + { + //files that dont accept comments hence cant contain snippets - "DotSettings", - "csv", - "json", - "geojson", - "sln" + #region NoAcceptCommentsExtensions - #endregion - }; + "DotSettings", + "csv", + "json", + "geojson", + "sln" + + #endregion + }; + + public static void AddNoAcceptCommentsExtensions(params string[] extensions) + { + foreach (var extension in extensions) + { + noAcceptCommentsExtensions.Add(extension); + } + noAcceptCommentsExtensionsFrozen = noAcceptCommentsExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + } - public static HashSet BinaryFileExtensions { get; set; } = + public static void RemoveNoAcceptCommentsExtensions(params string[] extensions) + { + foreach (var extension in extensions) + { + noAcceptCommentsExtensions.Remove(extension); + } + noAcceptCommentsExtensionsFrozen = noAcceptCommentsExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + } + + static HashSet binaryFileExtensions = new(StringComparer.OrdinalIgnoreCase) { #region BinaryFileExtensions @@ -298,4 +318,28 @@ public static bool CanContainCommentsExtension(string extension) => #endregion }; -} \ No newline at end of file + + public static void AddBinaryFileExtensions(params string[] extensions) + { + foreach (var extension in extensions) + { + binaryFileExtensions.Add(extension); + } + binaryFileExtensionsFrozen = binaryFileExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + } + + public static void RemoveBinaryFileExtensions(params string[] extensions) + { + foreach (var extension in extensions) + { + binaryFileExtensions.Remove(extension); + } + binaryFileExtensionsFrozen = binaryFileExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + } + + static SnippetFileExclusions() + { + noAcceptCommentsExtensionsFrozen = noAcceptCommentsExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + binaryFileExtensionsFrozen = binaryFileExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + } +} diff --git a/src/MarkdownSnippets/Reading/FileSnippetExtractor.cs b/src/MarkdownSnippets/Reading/FileSnippetExtractor.cs index d46ae9a5..b80c8c7c 100644 --- a/src/MarkdownSnippets/Reading/FileSnippetExtractor.cs +++ b/src/MarkdownSnippets/Reading/FileSnippetExtractor.cs @@ -18,7 +18,7 @@ public static Task AppendUrlAsSnippet(this ICollection snippets, string /// Each url will be accessible using the file name as a key. Any snippets within the files will be extracted and accessible as individual keyed snippets. /// public static Task AppendUrlsAsSnippets(this ICollection snippets, params string[] urls) => - AppendUrlsAsSnippets(snippets, (IEnumerable) urls); + snippets.AppendUrlsAsSnippets((IEnumerable) urls); /// /// Each url will be accessible using the file name as a key. Any snippets within the files will be extracted and accessible as individual keyed snippets. @@ -27,7 +27,7 @@ public static async Task AppendUrlsAsSnippets(this ICollection snippets { foreach (var url in urls) { - await AppendUrlAsSnippet(snippets, url); + await snippets.AppendUrlAsSnippet(url); } } @@ -63,7 +63,7 @@ public static void AppendFilesAsSnippets(this ICollection snippets, par { foreach (var filePath in filePaths) { - AppendFileAsSnippet(snippets, filePath); + snippets.AppendFileAsSnippet(filePath); } } @@ -211,4 +211,4 @@ static Snippet BuildSnippet(string path, LoopStack loopStack, string language, i expressiveCode: loopState.ExpressiveCode ); } -} \ No newline at end of file +} From ae71aea8f6a185a6f7d77701ad5234695ac32f23 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Wed, 14 Jan 2026 13:18:54 +1100 Subject: [PATCH 2/6] . --- docs/exclusion.md | 4 +- .../Exclusions/SnippetFileExclusions.cs | 39 ++++++++----------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/docs/exclusion.md b/docs/exclusion.md index b22175f8..175275f4 100644 --- a/docs/exclusion.md +++ b/docs/exclusion.md @@ -361,7 +361,7 @@ All binary files as defined by https://github.com/sindresorhus/binary-extensions "zip", "zipx" ``` -snippet source | anchor +snippet source | anchor @@ -378,5 +378,5 @@ Files that cannot contain comments are excluded. "geojson", "sln" ``` -snippet source | anchor +snippet source | anchor diff --git a/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs b/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs index 9c0e65e7..e43d20c3 100644 --- a/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs +++ b/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs @@ -8,10 +8,7 @@ public static bool IsBinary(string extension) => public static bool CanContainCommentsExtension(string extension) => !noAcceptCommentsExtensionsFrozen.Contains(extension); - static FrozenSet noAcceptCommentsExtensionsFrozen; - static FrozenSet binaryFileExtensionsFrozen; - - static HashSet noAcceptCommentsExtensions = new(StringComparer.OrdinalIgnoreCase) + static FrozenSet noAcceptCommentsExtensionsFrozen = new HashSet(StringComparer.OrdinalIgnoreCase) { //files that dont accept comments hence cant contain snippets @@ -24,28 +21,30 @@ public static bool CanContainCommentsExtension(string extension) => "sln" #endregion - }; + }.ToFrozenSet(StringComparer.OrdinalIgnoreCase); public static void AddNoAcceptCommentsExtensions(params string[] extensions) { + var set = new HashSet(noAcceptCommentsExtensionsFrozen, StringComparer.OrdinalIgnoreCase); foreach (var extension in extensions) { - noAcceptCommentsExtensions.Add(extension); + set.Add(extension); } - noAcceptCommentsExtensionsFrozen = noAcceptCommentsExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + noAcceptCommentsExtensionsFrozen = set.ToFrozenSet(StringComparer.OrdinalIgnoreCase); } public static void RemoveNoAcceptCommentsExtensions(params string[] extensions) { + var set = new HashSet(noAcceptCommentsExtensionsFrozen, StringComparer.OrdinalIgnoreCase); foreach (var extension in extensions) { - noAcceptCommentsExtensions.Remove(extension); + set.Remove(extension); } - noAcceptCommentsExtensionsFrozen = noAcceptCommentsExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + noAcceptCommentsExtensionsFrozen = set.ToFrozenSet(StringComparer.OrdinalIgnoreCase); } - static HashSet binaryFileExtensions = - new(StringComparer.OrdinalIgnoreCase) + static FrozenSet binaryFileExtensionsFrozen = + new HashSet(StringComparer.OrdinalIgnoreCase) { #region BinaryFileExtensions @@ -317,29 +316,25 @@ public static void RemoveNoAcceptCommentsExtensions(params string[] extensions) "zipx" #endregion - }; + }.ToFrozenSet(StringComparer.OrdinalIgnoreCase); public static void AddBinaryFileExtensions(params string[] extensions) { + var set = new HashSet(binaryFileExtensionsFrozen, StringComparer.OrdinalIgnoreCase); foreach (var extension in extensions) { - binaryFileExtensions.Add(extension); + set.Add(extension); } - binaryFileExtensionsFrozen = binaryFileExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + binaryFileExtensionsFrozen = set.ToFrozenSet(StringComparer.OrdinalIgnoreCase); } public static void RemoveBinaryFileExtensions(params string[] extensions) { + var set = new HashSet(binaryFileExtensionsFrozen, StringComparer.OrdinalIgnoreCase); foreach (var extension in extensions) { - binaryFileExtensions.Remove(extension); + set.Remove(extension); } - binaryFileExtensionsFrozen = binaryFileExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); - } - - static SnippetFileExclusions() - { - noAcceptCommentsExtensionsFrozen = noAcceptCommentsExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); - binaryFileExtensionsFrozen = binaryFileExtensions.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + binaryFileExtensionsFrozen = set.ToFrozenSet(StringComparer.OrdinalIgnoreCase); } } From cd4edb9b500288e4c14cb9c72612ea5b4f289ae9 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Wed, 14 Jan 2026 14:33:57 +1100 Subject: [PATCH 3/6] . --- src/Directory.Packages.props | 4 +- src/MarkdownSnippets/ContentValidation.cs | 65 +++++++++++------------ 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 5be8504e..88662a82 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -12,7 +12,7 @@ - + @@ -20,4 +20,4 @@ - \ No newline at end of file + diff --git a/src/MarkdownSnippets/ContentValidation.cs b/src/MarkdownSnippets/ContentValidation.cs index 2a9bb608..3718b34b 100644 --- a/src/MarkdownSnippets/ContentValidation.cs +++ b/src/MarkdownSnippets/ContentValidation.cs @@ -1,38 +1,37 @@ static class ContentValidation { - static FrozenDictionary phrases = new Dictionary - { - {"a majority of ", "most"}, - {"a number of", "some or many"}, - {"at an early date", "soon"}, - {"at the conclusion of", "after or following"}, - {"at the present time", "now"}, - {"at this point in time", "now"}, - {"based on the fact that", "because or since"}, - {"despite the fact that", "although"}, - {"due to the fact that", "because"}, - {"during the course of", "during"}, - {"during the time that", "during or while"}, - {"have the capability to", "can"}, - {"in connection with", "about"}, - {"in order to", "to"}, - {"in regard to ", "regarding or about"}, - {"in the event of", "if"}, - {"in view of the fact that", "because"}, - {"it is often the case that", "often"}, - {"make reference to ", "refer to"}, - {"of the opinion that", "think that "}, - {"on a daily basis", "daily"}, - {"on the grounds that", "because"}, - {"prior to", "before"}, - {"so as to", "to"}, - {"subsequent to", "after"}, - {"take into consideration", "consider"}, - {"until such time as", "until"}, - {"a lot", "many"}, - {"sort of", "similar or approximately"}, - {"kind of", "similar or approximately "} - }.ToFrozenDictionary(); + static FrozenDictionary phrases = FrozenDictionary.Create([ + new("a majority of ", "most"), + new("a number of", "some or many"), + new("at an early date", "soon"), + new("at the conclusion of", "after or following"), + new("at the present time", "now"), + new("at this point in time", "now"), + new("based on the fact that", "because or since"), + new("despite the fact that", "although"), + new("due to the fact that", "because"), + new("during the course of", "during"), + new("during the time that", "during or while"), + new("have the capability to", "can"), + new("in connection with", "about"), + new("in order to", "to"), + new("in regard to ", "regarding or about"), + new("in the event of", "if"), + new("in view of the fact that", "because"), + new("it is often the case that", "often"), + new("make reference to ", "refer to"), + new("of the opinion that", "think that "), + new("on a daily basis", "daily"), + new("on the grounds that", "because"), + new("prior to", "before"), + new("so as to", "to"), + new("subsequent to", "after"), + new("take into consideration", "consider"), + new("until such time as", "until"), + new("a lot", "many"), + new("sort of", "similar or approximately"), + new("kind of", "similar or approximately ") + ]); static List invalidStrings; From 546709d962956c5a098e5712ca2d92796c68c1cf Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Wed, 14 Jan 2026 14:38:52 +1100 Subject: [PATCH 4/6] . --- docs/exclusion.md | 4 ++-- .../Reading/Exclusions/SnippetFileExclusions.cs | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/docs/exclusion.md b/docs/exclusion.md index 175275f4..5cfadf93 100644 --- a/docs/exclusion.md +++ b/docs/exclusion.md @@ -361,7 +361,7 @@ All binary files as defined by https://github.com/sindresorhus/binary-extensions "zip", "zipx" ``` -snippet source | anchor +snippet source | anchor @@ -378,5 +378,5 @@ Files that cannot contain comments are excluded. "geojson", "sln" ``` -snippet source | anchor +snippet source | anchor diff --git a/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs b/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs index e43d20c3..6c6d5504 100644 --- a/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs +++ b/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs @@ -8,8 +8,7 @@ public static bool IsBinary(string extension) => public static bool CanContainCommentsExtension(string extension) => !noAcceptCommentsExtensionsFrozen.Contains(extension); - static FrozenSet noAcceptCommentsExtensionsFrozen = new HashSet(StringComparer.OrdinalIgnoreCase) - { + static FrozenSet noAcceptCommentsExtensionsFrozen = FrozenSet.Create(StringComparer.OrdinalIgnoreCase, [ //files that dont accept comments hence cant contain snippets #region NoAcceptCommentsExtensions @@ -21,7 +20,7 @@ public static bool CanContainCommentsExtension(string extension) => "sln" #endregion - }.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + ]); public static void AddNoAcceptCommentsExtensions(params string[] extensions) { @@ -43,9 +42,8 @@ public static void RemoveNoAcceptCommentsExtensions(params string[] extensions) noAcceptCommentsExtensionsFrozen = set.ToFrozenSet(StringComparer.OrdinalIgnoreCase); } - static FrozenSet binaryFileExtensionsFrozen = - new HashSet(StringComparer.OrdinalIgnoreCase) - { + static FrozenSet binaryFileExtensionsFrozen = FrozenSet.Create(StringComparer.OrdinalIgnoreCase, + [ #region BinaryFileExtensions "user", @@ -316,7 +314,7 @@ public static void RemoveNoAcceptCommentsExtensions(params string[] extensions) "zipx" #endregion - }.ToFrozenSet(StringComparer.OrdinalIgnoreCase); + ]); public static void AddBinaryFileExtensions(params string[] extensions) { From ee2b044d75b69a0a4c3fafd4f9b15be824027cde Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Wed, 14 Jan 2026 14:40:15 +1100 Subject: [PATCH 5/6] Update SnippetFileExclusions.cs --- .../Exclusions/SnippetFileExclusions.cs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs b/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs index 6c6d5504..14e6cf06 100644 --- a/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs +++ b/src/MarkdownSnippets/Reading/Exclusions/SnippetFileExclusions.cs @@ -8,19 +8,22 @@ public static bool IsBinary(string extension) => public static bool CanContainCommentsExtension(string extension) => !noAcceptCommentsExtensionsFrozen.Contains(extension); - static FrozenSet noAcceptCommentsExtensionsFrozen = FrozenSet.Create(StringComparer.OrdinalIgnoreCase, [ - //files that dont accept comments hence cant contain snippets + static FrozenSet noAcceptCommentsExtensionsFrozen = FrozenSet.Create( + StringComparer.OrdinalIgnoreCase, + source: + [ + //files that dont accept comments hence cant contain snippets - #region NoAcceptCommentsExtensions + #region NoAcceptCommentsExtensions - "DotSettings", - "csv", - "json", - "geojson", - "sln" + "DotSettings", + "csv", + "json", + "geojson", + "sln" - #endregion - ]); + #endregion + ]); public static void AddNoAcceptCommentsExtensions(params string[] extensions) { @@ -29,6 +32,7 @@ public static void AddNoAcceptCommentsExtensions(params string[] extensions) { set.Add(extension); } + noAcceptCommentsExtensionsFrozen = set.ToFrozenSet(StringComparer.OrdinalIgnoreCase); } @@ -39,10 +43,13 @@ public static void RemoveNoAcceptCommentsExtensions(params string[] extensions) { set.Remove(extension); } + noAcceptCommentsExtensionsFrozen = set.ToFrozenSet(StringComparer.OrdinalIgnoreCase); } - static FrozenSet binaryFileExtensionsFrozen = FrozenSet.Create(StringComparer.OrdinalIgnoreCase, + static FrozenSet binaryFileExtensionsFrozen = FrozenSet.Create( + StringComparer.OrdinalIgnoreCase, + source: [ #region BinaryFileExtensions @@ -323,6 +330,7 @@ public static void AddBinaryFileExtensions(params string[] extensions) { set.Add(extension); } + binaryFileExtensionsFrozen = set.ToFrozenSet(StringComparer.OrdinalIgnoreCase); } @@ -333,6 +341,7 @@ public static void RemoveBinaryFileExtensions(params string[] extensions) { set.Remove(extension); } + binaryFileExtensionsFrozen = set.ToFrozenSet(StringComparer.OrdinalIgnoreCase); } } From bf8ba744b06da7f5dacaf9ac37961db6c6cbd7cf Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Wed, 14 Jan 2026 14:52:42 +1100 Subject: [PATCH 6/6] Update MarkdownSnippets.csproj --- src/MarkdownSnippets/MarkdownSnippets.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MarkdownSnippets/MarkdownSnippets.csproj b/src/MarkdownSnippets/MarkdownSnippets.csproj index a50b402b..2828b7d3 100644 --- a/src/MarkdownSnippets/MarkdownSnippets.csproj +++ b/src/MarkdownSnippets/MarkdownSnippets.csproj @@ -7,8 +7,8 @@ - + - \ No newline at end of file +