Skip to content

Commit

Permalink
feat: Update regex implementations
Browse files Browse the repository at this point in the history
Updated all regex implementations to use .NET 7+ GeneratedRegex sourcegen-based implementations.
  • Loading branch information
SakuraIsayeki committed Aug 25, 2024
1 parent f4cff33 commit eb22c5e
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 25 deletions.
11 changes: 7 additions & 4 deletions Nodsoft.MoltenObsidian.Blazor/Helpers/VaultComponentHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
using System.Reflection;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Components;

namespace Nodsoft.MoltenObsidian.Blazor.Helpers;

/// <summary>
/// Provides helper methods for working with Vault implementation components.
/// </summary>
public static class VaultComponentHelpers
[PublicAPI]
public static partial class VaultComponentHelpers
{
/// <summary>
/// Regex for matching a slug segment at the end of a string. (e.g. "/{*slugName}")
/// This is the same regex used by the Blazor Router to match slug segments.
/// </summary>
private static readonly Regex SlugRegex = new(@"\/\{\*[\w\d]+\}$", RegexOptions.Compiled);
[GeneratedRegex(@"\/\{\*[\w\d]+\}$", RegexOptions.Compiled)]
private static partial Regex SlugRegex();

/// <summary>
/// A dictionary containing the base vault path for a component type.
Expand Down Expand Up @@ -56,8 +59,8 @@ private static string GetCallingBaseVaultPath(Type componentType)

// Second. Does the RouteAttribute have a slug segment at the end?
// If so, remove it.
string basePath = SlugRegex.IsMatch(routeAttribute.Template)
? SlugRegex.Replace(routeAttribute.Template, string.Empty)
string basePath = SlugRegex().IsMatch(routeAttribute.Template)
? SlugRegex().Replace(routeAttribute.Template, string.Empty)
: routeAttribute.Template;

// Third. Does the base path end with slashes?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Nodsoft.MoltenObsidian.Infrastructure.Markdown.InternalLinks;
/// Syntax: <c>[[link]] | [[link|display]] | [[link|display|tooltip]]</c>
/// The link can be a relative path, an absolute path, and/or a section anchor.
/// </example>
public sealed class InternalLink : LinkInline
public sealed partial class InternalLink : LinkInline
{
/// <summary>
/// Initializes a new instance of the <see cref="InternalLink"/> class.
Expand All @@ -24,7 +24,8 @@ public InternalLink()
/// <summary>
/// Regex substitution pattern for internal link anchors, to replace spaces with dashes and remove all other invalid characters.
/// </summary>
private static readonly Regex AnchorRegex = new(@"[^\w.]+", RegexOptions.Compiled);
[GeneratedRegex(@"[^\w.]+", RegexOptions.Compiled | RegexOptions.NonBacktracking)]
private static partial Regex AnchorRegex();


/// <summary>
Expand All @@ -44,7 +45,7 @@ public InternalLink()
/// <summary>
/// The fragment of the section anchor, suitable for use in a URL.
/// </summary>
public string? TargetSectionLinkFragment => TargetSection is null ? null : AnchorRegex.Replace(TargetSection, "-").Trim('-').ToLowerInvariant();
public string? TargetSectionLinkFragment => TargetSection is null ? null : AnchorRegex().Replace(TargetSection, "-").Trim('-').ToLowerInvariant();

/// <summary>
/// The display text of the internal link, if any.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Nodsoft.MoltenObsidian.Infrastructure.Markdown.InternalLinks;
/// Provides parsing for Obsidian internal links for Markdig.
/// </summary>
/// <seealso cref="InternalLink" />
public sealed class ObsidianInternalLinksParser : InlineParser
public sealed partial class ObsidianInternalLinksParser : InlineParser
{
/// <summary>
/// Regex pattern with named variables for internal links.
Expand All @@ -23,22 +23,20 @@ public sealed class ObsidianInternalLinksParser : InlineParser
/// [[note_one|not note one]]
/// [[note#section|display|tooltip]]
/// </example>
private static readonly Regex InternalLinkRegex = new(
[GeneratedRegex(
"""
^\[\[(
# link, and optional anchor
(?<link>[^\|\#\]]+)?(\#(?<anchor>[^\|\]]+))?
# display title (optional)
(\|(?<title>[^|\]]+))?
# tooltip (optional)
(\|(?<tooltip>[^|\]]+))?
)\]\]
""",
RegexOptions.Compiled
| RegexOptions.IgnorePatternWhitespace
| RegexOptions.ExplicitCapture
| RegexOptions.Multiline
);
^\[\[(
# link, and optional anchor
(?<link>[^\|\#\]]+)?(\#(?<anchor>[^\|\]]+))?
# display title (optional)
(\|(?<title>[^|\]]+))?
# tooltip (optional)
(\|(?<tooltip>[^|\]]+))?
)\]\]
""", RegexOptions.Multiline | RegexOptions.ExplicitCapture | RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.NonBacktracking)]
private static partial Regex InternalLinkRegex();

/// <summary>
/// Initializes a new instance of the <see cref="ObsidianInternalLinksParser"/> class.
Expand All @@ -49,7 +47,7 @@ public ObsidianInternalLinksParser()
}

/// <inheritdoc />
public override bool Match(InlineProcessor processor, ref StringSlice slice)
public override bool Match(InlineProcessor processor, scoped ref StringSlice slice)
{
// Seek to the first two opening brackets
if (slice.CurrentChar is not '[' && slice.PeekChar() is not '[')
Expand All @@ -58,7 +56,7 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice)
}

// Grab the remainder of the slice, and check if it matches the internal link pattern.
Match match = InternalLinkRegex.Match(slice.Text[slice.Start..slice.End]);
Match match = InternalLinkRegex().Match(slice.Text[slice.Start..slice.End]);

if (match is { Groups: [{ Name: "0" }]})
{
Expand Down

0 comments on commit eb22c5e

Please sign in to comment.