Make StringPart readonly struct

This commit is contained in:
Alexey Golub 2019-12-07 20:16:53 +02:00
parent 2a223599f9
commit 964317dac0
5 changed files with 27 additions and 29 deletions

View file

@ -1,20 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace DiscordChatExporter.Core.Markdown.Internal namespace DiscordChatExporter.Core.Markdown.Internal
{ {
internal static class Extensions internal static class Extensions
{ {
public static StringPart Shrink(this StringPart stringPart, int newStartIndex, int newLength) =>
new StringPart(stringPart.Target, newStartIndex, newLength);
public static StringPart Shrink(this StringPart stringPart, int newStartIndex) =>
stringPart.Shrink(newStartIndex, stringPart.EndIndex - newStartIndex);
public static StringPart Shrink(this StringPart stringPart, Capture capture) =>
stringPart.Shrink(capture.Index, capture.Length);
public static IEnumerable<ParsedMatch<T>> MatchAll<T>(this IMatcher<T> matcher, StringPart stringPart, public static IEnumerable<ParsedMatch<T>> MatchAll<T>(this IMatcher<T> matcher, StringPart stringPart,
Func<StringPart, T> fallbackTransform) Func<StringPart, T> fallbackTransform)
{ {
@ -23,7 +13,7 @@ namespace DiscordChatExporter.Core.Markdown.Internal
while (currentIndex < stringPart.EndIndex) while (currentIndex < stringPart.EndIndex)
{ {
// Find a match within this segment // Find a match within this segment
var match = matcher.Match(stringPart.Shrink(currentIndex, stringPart.EndIndex - currentIndex)); var match = matcher.Match(stringPart.Slice(currentIndex, stringPart.EndIndex - currentIndex));
// If there's no match - break // If there's no match - break
if (match == null) if (match == null)
@ -32,7 +22,7 @@ namespace DiscordChatExporter.Core.Markdown.Internal
// If this match doesn't start immediately at current index - transform and yield fallback first // If this match doesn't start immediately at current index - transform and yield fallback first
if (match.StringPart.StartIndex > currentIndex) if (match.StringPart.StartIndex > currentIndex)
{ {
var fallbackPart = stringPart.Shrink(currentIndex, match.StringPart.StartIndex - currentIndex); var fallbackPart = stringPart.Slice(currentIndex, match.StringPart.StartIndex - currentIndex);
yield return new ParsedMatch<T>(fallbackPart, fallbackTransform(fallbackPart)); yield return new ParsedMatch<T>(fallbackPart, fallbackTransform(fallbackPart));
} }
@ -46,7 +36,7 @@ namespace DiscordChatExporter.Core.Markdown.Internal
// If EOL wasn't reached - transform and yield remaining part as fallback // If EOL wasn't reached - transform and yield remaining part as fallback
if (currentIndex < stringPart.EndIndex) if (currentIndex < stringPart.EndIndex)
{ {
var fallbackPart = stringPart.Shrink(currentIndex); var fallbackPart = stringPart.Slice(currentIndex);
yield return new ParsedMatch<T>(fallbackPart, fallbackTransform(fallbackPart)); yield return new ParsedMatch<T>(fallbackPart, fallbackTransform(fallbackPart));
} }
} }

View file

@ -33,8 +33,8 @@ namespace DiscordChatExporter.Core.Markdown.Internal
if (!_regex.IsMatch(stringPart.Target.Substring(0, stringPart.EndIndex), stringPart.StartIndex)) if (!_regex.IsMatch(stringPart.Target.Substring(0, stringPart.EndIndex), stringPart.StartIndex))
return null; return null;
var stringPartShrunk = stringPart.Shrink(match.Index, match.Length); var stringPartMatch = stringPart.Slice(match.Index, match.Length);
return new ParsedMatch<T>(stringPartShrunk, _transform(stringPartShrunk, match)); return new ParsedMatch<T>(stringPartMatch, _transform(stringPartMatch, match));
} }
} }
} }

View file

@ -26,8 +26,8 @@ namespace DiscordChatExporter.Core.Markdown.Internal
if (index >= 0) if (index >= 0)
{ {
var stringPartShrunk = stringPart.Shrink(index, _needle.Length); var stringPartMatch = stringPart.Slice(index, _needle.Length);
return new ParsedMatch<T>(stringPartShrunk, _transform(stringPartShrunk)); return new ParsedMatch<T>(stringPartMatch, _transform(stringPartMatch));
} }
return null; return null;

View file

@ -1,6 +1,8 @@
namespace DiscordChatExporter.Core.Markdown.Internal using System.Text.RegularExpressions;
namespace DiscordChatExporter.Core.Markdown.Internal
{ {
internal class StringPart internal readonly struct StringPart
{ {
public string Target { get; } public string Target { get; }
@ -23,6 +25,12 @@
{ {
} }
public StringPart Slice(int newStartIndex, int newLength) => new StringPart(Target, newStartIndex, newLength);
public StringPart Slice(int newStartIndex) => Slice(newStartIndex, EndIndex - newStartIndex);
public StringPart Slice(Capture capture) => Slice(capture.Index, capture.Length);
public override string ToString() => Target.Substring(StartIndex, Length); public override string ToString() => Target.Substring(StartIndex, Length);
} }
} }

View file

@ -16,57 +16,57 @@ namespace DiscordChatExporter.Core.Markdown
// Capture any character until the earliest double asterisk not followed by an asterisk // Capture any character until the earliest double asterisk not followed by an asterisk
private static readonly IMatcher<Node> BoldFormattedNodeMatcher = new RegexMatcher<Node>( private static readonly IMatcher<Node> BoldFormattedNodeMatcher = new RegexMatcher<Node>(
new Regex("\\*\\*(.+?)\\*\\*(?!\\*)", DefaultRegexOptions | RegexOptions.Singleline), new Regex("\\*\\*(.+?)\\*\\*(?!\\*)", DefaultRegexOptions | RegexOptions.Singleline),
(p, m) => new FormattedNode(TextFormatting.Bold, Parse(p.Shrink(m.Groups[1])))); (p, m) => new FormattedNode(TextFormatting.Bold, Parse(p.Slice(m.Groups[1]))));
// Capture any character until the earliest single asterisk not preceded or followed by an asterisk // Capture any character until the earliest single asterisk not preceded or followed by an asterisk
// Opening asterisk must not be followed by whitespace // Opening asterisk must not be followed by whitespace
// Closing asterisk must not be preceded by whitespace // Closing asterisk must not be preceded by whitespace
private static readonly IMatcher<Node> ItalicFormattedNodeMatcher = new RegexMatcher<Node>( private static readonly IMatcher<Node> ItalicFormattedNodeMatcher = new RegexMatcher<Node>(
new Regex("\\*(?!\\s)(.+?)(?<!\\s|\\*)\\*(?!\\*)", DefaultRegexOptions | RegexOptions.Singleline), new Regex("\\*(?!\\s)(.+?)(?<!\\s|\\*)\\*(?!\\*)", DefaultRegexOptions | RegexOptions.Singleline),
(p, m) => new FormattedNode(TextFormatting.Italic, Parse(p.Shrink(m.Groups[1])))); (p, m) => new FormattedNode(TextFormatting.Italic, Parse(p.Slice(m.Groups[1]))));
// Capture any character until the earliest triple asterisk not followed by an asterisk // Capture any character until the earliest triple asterisk not followed by an asterisk
private static readonly IMatcher<Node> ItalicBoldFormattedNodeMatcher = new RegexMatcher<Node>( private static readonly IMatcher<Node> ItalicBoldFormattedNodeMatcher = new RegexMatcher<Node>(
new Regex("\\*(\\*\\*.+?\\*\\*)\\*(?!\\*)", DefaultRegexOptions | RegexOptions.Singleline), new Regex("\\*(\\*\\*.+?\\*\\*)\\*(?!\\*)", DefaultRegexOptions | RegexOptions.Singleline),
(p, m) => new FormattedNode(TextFormatting.Italic, Parse(p.Shrink(m.Groups[1]), BoldFormattedNodeMatcher))); (p, m) => new FormattedNode(TextFormatting.Italic, Parse(p.Slice(m.Groups[1]), BoldFormattedNodeMatcher)));
// Capture any character except underscore until an underscore // Capture any character except underscore until an underscore
// Closing underscore must not be followed by a word character // Closing underscore must not be followed by a word character
private static readonly IMatcher<Node> ItalicAltFormattedNodeMatcher = new RegexMatcher<Node>( private static readonly IMatcher<Node> ItalicAltFormattedNodeMatcher = new RegexMatcher<Node>(
new Regex("_([^_]+)_(?!\\w)", DefaultRegexOptions | RegexOptions.Singleline), new Regex("_([^_]+)_(?!\\w)", DefaultRegexOptions | RegexOptions.Singleline),
(p, m) => new FormattedNode(TextFormatting.Italic, Parse(p.Shrink(m.Groups[1])))); (p, m) => new FormattedNode(TextFormatting.Italic, Parse(p.Slice(m.Groups[1]))));
// Capture any character until the earliest double underscore not followed by an underscore // Capture any character until the earliest double underscore not followed by an underscore
private static readonly IMatcher<Node> UnderlineFormattedNodeMatcher = new RegexMatcher<Node>( private static readonly IMatcher<Node> UnderlineFormattedNodeMatcher = new RegexMatcher<Node>(
new Regex("__(.+?)__(?!_)", DefaultRegexOptions | RegexOptions.Singleline), new Regex("__(.+?)__(?!_)", DefaultRegexOptions | RegexOptions.Singleline),
(p, m) => new FormattedNode(TextFormatting.Underline, Parse(p.Shrink(m.Groups[1])))); (p, m) => new FormattedNode(TextFormatting.Underline, Parse(p.Slice(m.Groups[1]))));
// Capture any character until the earliest triple underscore not followed by an underscore // Capture any character until the earliest triple underscore not followed by an underscore
private static readonly IMatcher<Node> ItalicUnderlineFormattedNodeMatcher = new RegexMatcher<Node>( private static readonly IMatcher<Node> ItalicUnderlineFormattedNodeMatcher = new RegexMatcher<Node>(
new Regex("_(__.+?__)_(?!_)", DefaultRegexOptions | RegexOptions.Singleline), new Regex("_(__.+?__)_(?!_)", DefaultRegexOptions | RegexOptions.Singleline),
(p, m) => new FormattedNode(TextFormatting.Italic, Parse(p.Shrink(m.Groups[1]), UnderlineFormattedNodeMatcher))); (p, m) => new FormattedNode(TextFormatting.Italic, Parse(p.Slice(m.Groups[1]), UnderlineFormattedNodeMatcher)));
// Capture any character until the earliest double tilde // Capture any character until the earliest double tilde
private static readonly IMatcher<Node> StrikethroughFormattedNodeMatcher = new RegexMatcher<Node>( private static readonly IMatcher<Node> StrikethroughFormattedNodeMatcher = new RegexMatcher<Node>(
new Regex("~~(.+?)~~", DefaultRegexOptions | RegexOptions.Singleline), new Regex("~~(.+?)~~", DefaultRegexOptions | RegexOptions.Singleline),
(p, m) => new FormattedNode(TextFormatting.Strikethrough, Parse(p.Shrink(m.Groups[1])))); (p, m) => new FormattedNode(TextFormatting.Strikethrough, Parse(p.Slice(m.Groups[1]))));
// Capture any character until the earliest double pipe // Capture any character until the earliest double pipe
private static readonly IMatcher<Node> SpoilerFormattedNodeMatcher = new RegexMatcher<Node>( private static readonly IMatcher<Node> SpoilerFormattedNodeMatcher = new RegexMatcher<Node>(
new Regex("\\|\\|(.+?)\\|\\|", DefaultRegexOptions | RegexOptions.Singleline), new Regex("\\|\\|(.+?)\\|\\|", DefaultRegexOptions | RegexOptions.Singleline),
(p, m) => new FormattedNode(TextFormatting.Spoiler, Parse(p.Shrink(m.Groups[1])))); (p, m) => new FormattedNode(TextFormatting.Spoiler, Parse(p.Slice(m.Groups[1]))));
// Capture any character until the end of the line // Capture any character until the end of the line
// Opening 'greater than' character must be followed by whitespace // Opening 'greater than' character must be followed by whitespace
private static readonly IMatcher<Node> SingleLineQuoteNodeMatcher = new RegexMatcher<Node>( private static readonly IMatcher<Node> SingleLineQuoteNodeMatcher = new RegexMatcher<Node>(
new Regex("^>\\s(.+)\r?\n?", DefaultRegexOptions), new Regex("^>\\s(.+)\r?\n?", DefaultRegexOptions),
(p, m) => new FormattedNode(TextFormatting.Quote, Parse(p.Shrink(m.Groups[1])))); (p, m) => new FormattedNode(TextFormatting.Quote, Parse(p.Slice(m.Groups[1]))));
// Capture any character until the end of the input // Capture any character until the end of the input
// Opening 'greater than' characters must be followed by whitespace // Opening 'greater than' characters must be followed by whitespace
private static readonly IMatcher<Node> MultiLineQuoteNodeMatcher = new RegexMatcher<Node>( private static readonly IMatcher<Node> MultiLineQuoteNodeMatcher = new RegexMatcher<Node>(
new Regex("^>>>\\s(.+)", DefaultRegexOptions | RegexOptions.Singleline), new Regex("^>>>\\s(.+)", DefaultRegexOptions | RegexOptions.Singleline),
(p, m) => new FormattedNode(TextFormatting.Quote, Parse(p.Shrink(m.Groups[1])))); (p, m) => new FormattedNode(TextFormatting.Quote, Parse(p.Slice(m.Groups[1]))));
/* Code blocks */ /* Code blocks */