diff --git a/DiscordChatExporter.Core/Exporting/Writers/CsvMessageWriter.cs b/DiscordChatExporter.Core/Exporting/Writers/CsvMessageWriter.cs index 58c4422..94349f7 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/CsvMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/CsvMessageWriter.cs @@ -19,8 +19,8 @@ internal partial class CsvMessageWriter : MessageWriter _writer = new StreamWriter(stream); } - private string FormatMarkdown(string? markdown) => - PlainTextMarkdownVisitor.Format(Context, markdown ?? ""); + private ValueTask FormatMarkdownAsync(string? markdown) => + PlainTextMarkdownVisitor.FormatAsync(Context, markdown ?? ""); public override async ValueTask WritePreambleAsync(CancellationToken cancellationToken = default) => await _writer.WriteLineAsync("AuthorID,Author,Date,Content,Attachments,Reactions"); @@ -84,7 +84,7 @@ internal partial class CsvMessageWriter : MessageWriter await _writer.WriteAsync(','); // Message content - await _writer.WriteAsync(CsvEncode(FormatMarkdown(message.Content))); + await _writer.WriteAsync(CsvEncode(await FormatMarkdownAsync(message.Content))); await _writer.WriteAsync(','); // Attachments diff --git a/DiscordChatExporter.Core/Exporting/Writers/Html/MessageGroupTemplate.cshtml b/DiscordChatExporter.Core/Exporting/Writers/Html/MessageGroupTemplate.cshtml index 4df865f..f7fa6c4 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/Html/MessageGroupTemplate.cshtml +++ b/DiscordChatExporter.Core/Exporting/Writers/Html/MessageGroupTemplate.cshtml @@ -14,9 +14,9 @@ string FormatDate(DateTimeOffset date) => Model.ExportContext.FormatDate(date); - string FormatMarkdown(string markdown) => Model.FormatMarkdown(markdown); + ValueTask FormatMarkdownAsync(string markdown) => Model.FormatMarkdownAsync(markdown); - string FormatEmbedMarkdown(string markdown) => Model.FormatMarkdown(markdown, false); + ValueTask FormatEmbedMarkdownAsync(string markdown) => Model.FormatMarkdownAsync(markdown, false); var firstMessage = Model.Messages.First(); @@ -125,7 +125,7 @@ @if (!string.IsNullOrWhiteSpace(message.ReferencedMessage.Content) && !message.ReferencedMessage.IsContentHidden()) { - @Raw(FormatEmbedMarkdown(message.ReferencedMessage.Content)) + @Raw(await FormatEmbedMarkdownAsync(message.ReferencedMessage.Content)) } else if (message.ReferencedMessage.Attachments.Any() || message.ReferencedMessage.Embeds.Any()) { @@ -176,7 +176,7 @@ @{/* Text */} @if (!string.IsNullOrWhiteSpace(message.Content) && !message.IsContentHidden()) { - @Raw(FormatMarkdown(message.Content)) + @Raw(await FormatMarkdownAsync(message.Content)) } @{/* Edited timestamp */} @@ -296,12 +296,12 @@ @if (!string.IsNullOrWhiteSpace(embed.Url)) { -
@Raw(FormatEmbedMarkdown(embed.Title))
+
@Raw(await FormatEmbedMarkdownAsync(embed.Title))
} else { -
@Raw(FormatEmbedMarkdown(embed.Title))
+
@Raw(await FormatEmbedMarkdownAsync(embed.Title))
} } @@ -382,12 +382,12 @@ @if (!string.IsNullOrWhiteSpace(embed.Url)) { -
@Raw(FormatEmbedMarkdown(embed.Title))
+
@Raw(await FormatEmbedMarkdownAsync(embed.Title))
} else { -
@Raw(FormatEmbedMarkdown(embed.Title))
+
@Raw(await FormatEmbedMarkdownAsync(embed.Title))
} } @@ -396,7 +396,7 @@ @if (!string.IsNullOrWhiteSpace(embed.Description)) {
-
@Raw(FormatEmbedMarkdown(embed.Description))
+
@Raw(await FormatEmbedMarkdownAsync(embed.Description))
} @@ -410,14 +410,14 @@ @if (!string.IsNullOrWhiteSpace(field.Name)) {
-
@Raw(FormatEmbedMarkdown(field.Name))
+
@Raw(await FormatEmbedMarkdownAsync(field.Name))
} @if (!string.IsNullOrWhiteSpace(field.Value)) {
-
@Raw(FormatEmbedMarkdown(field.Value))
+
@Raw(await FormatEmbedMarkdownAsync(field.Value))
} diff --git a/DiscordChatExporter.Core/Exporting/Writers/Html/MessageGroupTemplateContext.cs b/DiscordChatExporter.Core/Exporting/Writers/Html/MessageGroupTemplateContext.cs index 3395542..8e5943d 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/Html/MessageGroupTemplateContext.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/Html/MessageGroupTemplateContext.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading.Tasks; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Exporting.Writers.MarkdownVisitors; @@ -16,6 +17,6 @@ internal class MessageGroupTemplateContext Messages = messages; } - public string FormatMarkdown(string? markdown, bool isJumboAllowed = true) => - HtmlMarkdownVisitor.Format(ExportContext, markdown ?? "", isJumboAllowed); + public ValueTask FormatMarkdownAsync(string? markdown, bool isJumboAllowed = true) => + HtmlMarkdownVisitor.FormatAsync(ExportContext, markdown ?? "", isJumboAllowed); } \ No newline at end of file diff --git a/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplate.cshtml b/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplate.cshtml index 16fd0b5..311201e 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplate.cshtml +++ b/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplate.cshtml @@ -18,7 +18,7 @@ string FormatDate(DateTimeOffset date) => Model.ExportContext.FormatDate(date); - string FormatMarkdown(string markdown) => Model.FormatMarkdown(markdown); + ValueTask FormatMarkdownAsync(string markdown) => Model.FormatMarkdownAsync(markdown); } @@ -851,7 +851,7 @@ @if (!string.IsNullOrWhiteSpace(Model.ExportContext.Request.Channel.Topic)) { -
@Raw(FormatMarkdown(Model.ExportContext.Request.Channel.Topic))
+
@Raw(await FormatMarkdownAsync(Model.ExportContext.Request.Channel.Topic))
} @if (Model.ExportContext.Request.After is not null || Model.ExportContext.Request.Before is not null) diff --git a/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplateContext.cs b/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplateContext.cs index df6a729..0616548 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplateContext.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/Html/PreambleTemplateContext.cs @@ -1,4 +1,5 @@ -using DiscordChatExporter.Core.Exporting.Writers.MarkdownVisitors; +using System.Threading.Tasks; +using DiscordChatExporter.Core.Exporting.Writers.MarkdownVisitors; namespace DiscordChatExporter.Core.Exporting.Writers.Html; @@ -14,6 +15,6 @@ internal class PreambleTemplateContext ThemeName = themeName; } - public string FormatMarkdown(string? markdown, bool isJumboAllowed = true) => - HtmlMarkdownVisitor.Format(ExportContext, markdown ?? "", isJumboAllowed); + public ValueTask FormatMarkdownAsync(string? markdown, bool isJumboAllowed = true) => + HtmlMarkdownVisitor.FormatAsync(ExportContext, markdown ?? "", isJumboAllowed); } \ No newline at end of file diff --git a/DiscordChatExporter.Core/Exporting/Writers/JsonMessageWriter.cs b/DiscordChatExporter.Core/Exporting/Writers/JsonMessageWriter.cs index c69799b..017af9c 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/JsonMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/JsonMessageWriter.cs @@ -29,8 +29,8 @@ internal class JsonMessageWriter : MessageWriter }); } - private string FormatMarkdown(string? markdown) => - PlainTextMarkdownVisitor.Format(Context, markdown ?? ""); + private ValueTask FormatMarkdownAsync(string? markdown) => + PlainTextMarkdownVisitor.FormatAsync(Context, markdown ?? ""); private async ValueTask WriteAttachmentAsync( Attachment attachment, @@ -100,8 +100,8 @@ internal class JsonMessageWriter : MessageWriter { _writer.WriteStartObject(); - _writer.WriteString("name", FormatMarkdown(embedField.Name)); - _writer.WriteString("value", FormatMarkdown(embedField.Value)); + _writer.WriteString("name", await FormatMarkdownAsync(embedField.Name)); + _writer.WriteString("value", await FormatMarkdownAsync(embedField.Value)); _writer.WriteBoolean("isInline", embedField.IsInline); _writer.WriteEndObject(); @@ -114,10 +114,10 @@ internal class JsonMessageWriter : MessageWriter { _writer.WriteStartObject(); - _writer.WriteString("title", FormatMarkdown(embed.Title)); + _writer.WriteString("title", await FormatMarkdownAsync(embed.Title)); _writer.WriteString("url", embed.Url); _writer.WriteString("timestamp", embed.Timestamp); - _writer.WriteString("description", FormatMarkdown(embed.Description)); + _writer.WriteString("description", await FormatMarkdownAsync(embed.Description)); if (embed.Color is not null) _writer.WriteString("color", embed.Color.Value.ToHex()); @@ -268,7 +268,7 @@ internal class JsonMessageWriter : MessageWriter _writer.WriteBoolean("isPinned", message.IsPinned); // Content - _writer.WriteString("content", FormatMarkdown(message.Content)); + _writer.WriteString("content", await FormatMarkdownAsync(message.Content)); // Author _writer.WriteStartObject("author"); diff --git a/DiscordChatExporter.Core/Exporting/Writers/MarkdownVisitors/HtmlMarkdownVisitor.cs b/DiscordChatExporter.Core/Exporting/Writers/MarkdownVisitors/HtmlMarkdownVisitor.cs index 71942b0..cd95dea 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/MarkdownVisitors/HtmlMarkdownVisitor.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/MarkdownVisitors/HtmlMarkdownVisitor.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Net; using System.Text; using System.Text.RegularExpressions; +using System.Threading.Tasks; using DiscordChatExporter.Core.Discord.Data; using DiscordChatExporter.Core.Markdown; using DiscordChatExporter.Core.Markdown.Parsing; @@ -23,13 +24,13 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor _isJumbo = isJumbo; } - protected override MarkdownNode VisitText(TextNode text) + protected override ValueTask VisitTextAsync(TextNode text) { _buffer.Append(HtmlEncode(text.Text)); - return base.VisitText(text); + return base.VisitTextAsync(text); } - protected override MarkdownNode VisitFormatting(FormattingNode formatting) + protected override ValueTask VisitFormattingAsync(FormattingNode formatting) { var (tagOpen, tagClose) = formatting.Kind switch { @@ -67,23 +68,23 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor }; _buffer.Append(tagOpen); - var result = base.VisitFormatting(formatting); + var result = base.VisitFormattingAsync(formatting); _buffer.Append(tagClose); return result; } - protected override MarkdownNode VisitInlineCodeBlock(InlineCodeBlockNode inlineCodeBlock) + protected override ValueTask VisitInlineCodeBlockAsync(InlineCodeBlockNode inlineCodeBlock) { _buffer .Append("") .Append(HtmlEncode(inlineCodeBlock.Code)) .Append(""); - return base.VisitInlineCodeBlock(inlineCodeBlock); + return base.VisitInlineCodeBlockAsync(inlineCodeBlock); } - protected override MarkdownNode VisitMultiLineCodeBlock(MultiLineCodeBlockNode multiLineCodeBlock) + protected override ValueTask VisitMultiLineCodeBlockAsync(MultiLineCodeBlockNode multiLineCodeBlock) { var highlightCssClass = !string.IsNullOrWhiteSpace(multiLineCodeBlock.Language) ? $"language-{multiLineCodeBlock.Language}" @@ -94,10 +95,10 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor .Append(HtmlEncode(multiLineCodeBlock.Code)) .Append(""); - return base.VisitMultiLineCodeBlock(multiLineCodeBlock); + return base.VisitMultiLineCodeBlockAsync(multiLineCodeBlock); } - protected override MarkdownNode VisitLink(LinkNode link) + protected override ValueTask VisitLinkAsync(LinkNode link) { // Try to extract message ID if the link refers to a Discord message var linkedMessageId = Regex.Match( @@ -111,24 +112,24 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor : $"" ); - var result = base.VisitLink(link); + var result = base.VisitLinkAsync(link); _buffer.Append(""); return result; } - protected override MarkdownNode VisitEmoji(EmojiNode emoji) + protected override async ValueTask VisitEmojiAsync(EmojiNode emoji) { var emojiImageUrl = Emoji.GetImageUrl(emoji.Id, emoji.Name, emoji.IsAnimated); var jumboClass = _isJumbo ? "chatlog__emoji--large" : ""; _buffer - .Append($"\"{emoji.Name}\""); + .Append($"\"{emoji.Name}\""); - return base.VisitEmoji(emoji); + return await base.VisitEmojiAsync(emoji); } - protected override MarkdownNode VisitMention(MentionNode mention) + protected override ValueTask VisitMentionAsync(MentionNode mention) { if (mention.Kind == MentionKind.Everyone) { @@ -183,10 +184,10 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor .Append("
"); } - return base.VisitMention(mention); + return base.VisitMentionAsync(mention); } - protected override MarkdownNode VisitUnixTimestamp(UnixTimestampNode timestamp) + protected override ValueTask VisitUnixTimestampAsync(UnixTimestampNode timestamp) { var dateString = timestamp.Date is not null ? _context.FormatDate(timestamp.Date.Value) @@ -202,7 +203,7 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor .Append(HtmlEncode(dateString)) .Append(""); - return base.VisitUnixTimestamp(timestamp); + return base.VisitUnixTimestampAsync(timestamp); } } @@ -210,7 +211,7 @@ internal partial class HtmlMarkdownVisitor { private static string HtmlEncode(string text) => WebUtility.HtmlEncode(text); - public static string Format(ExportContext context, string markdown, bool isJumboAllowed = true) + public static async ValueTask FormatAsync(ExportContext context, string markdown, bool isJumboAllowed = true) { var nodes = MarkdownParser.Parse(markdown); @@ -220,7 +221,7 @@ internal partial class HtmlMarkdownVisitor var buffer = new StringBuilder(); - new HtmlMarkdownVisitor(context, buffer, isJumbo).Visit(nodes); + await new HtmlMarkdownVisitor(context, buffer, isJumbo).VisitAsync(nodes); return buffer.ToString(); } diff --git a/DiscordChatExporter.Core/Exporting/Writers/MarkdownVisitors/PlainTextMarkdownVisitor.cs b/DiscordChatExporter.Core/Exporting/Writers/MarkdownVisitors/PlainTextMarkdownVisitor.cs index c834dc9..63ad91e 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/MarkdownVisitors/PlainTextMarkdownVisitor.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/MarkdownVisitors/PlainTextMarkdownVisitor.cs @@ -1,4 +1,5 @@ using System.Text; +using System.Threading.Tasks; using DiscordChatExporter.Core.Markdown; using DiscordChatExporter.Core.Markdown.Parsing; using DiscordChatExporter.Core.Utils.Extensions; @@ -16,13 +17,13 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor _buffer = buffer; } - protected override MarkdownNode VisitText(TextNode text) + protected override ValueTask VisitTextAsync(TextNode text) { _buffer.Append(text.Text); - return base.VisitText(text); + return base.VisitTextAsync(text); } - protected override MarkdownNode VisitEmoji(EmojiNode emoji) + protected override ValueTask VisitEmojiAsync(EmojiNode emoji) { _buffer.Append( emoji.IsCustomEmoji @@ -30,10 +31,10 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor : emoji.Name ); - return base.VisitEmoji(emoji); + return base.VisitEmojiAsync(emoji); } - protected override MarkdownNode VisitMention(MentionNode mention) + protected override ValueTask VisitMentionAsync(MentionNode mention) { if (mention.Kind == MentionKind.Everyone) { @@ -69,10 +70,10 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor _buffer.Append($"@{name}"); } - return base.VisitMention(mention); + return base.VisitMentionAsync(mention); } - protected override MarkdownNode VisitUnixTimestamp(UnixTimestampNode timestamp) + protected override ValueTask VisitUnixTimestampAsync(UnixTimestampNode timestamp) { _buffer.Append( timestamp.Date is not null @@ -80,18 +81,18 @@ internal partial class PlainTextMarkdownVisitor : MarkdownVisitor : "Invalid date" ); - return base.VisitUnixTimestamp(timestamp); + return base.VisitUnixTimestampAsync(timestamp); } } internal partial class PlainTextMarkdownVisitor { - public static string Format(ExportContext context, string markdown) + public static async ValueTask FormatAsync(ExportContext context, string markdown) { var nodes = MarkdownParser.ParseMinimal(markdown); var buffer = new StringBuilder(); - new PlainTextMarkdownVisitor(context, buffer).Visit(nodes); + await new PlainTextMarkdownVisitor(context, buffer).VisitAsync(nodes); return buffer.ToString(); } diff --git a/DiscordChatExporter.Core/Exporting/Writers/PlainTextMessageWriter.cs b/DiscordChatExporter.Core/Exporting/Writers/PlainTextMessageWriter.cs index cf24a44..5122fe7 100644 --- a/DiscordChatExporter.Core/Exporting/Writers/PlainTextMessageWriter.cs +++ b/DiscordChatExporter.Core/Exporting/Writers/PlainTextMessageWriter.cs @@ -19,8 +19,8 @@ internal class PlainTextMessageWriter : MessageWriter _writer = new StreamWriter(stream); } - private string FormatMarkdown(string? markdown) => - PlainTextMarkdownVisitor.Format(Context, markdown ?? ""); + private ValueTask FormatMarkdownAsync(string? markdown) => + PlainTextMarkdownVisitor.FormatAsync(Context, markdown ?? ""); private async ValueTask WriteMessageHeaderAsync(Message message) { @@ -71,18 +71,18 @@ internal class PlainTextMessageWriter : MessageWriter await _writer.WriteLineAsync(embed.Url); if (!string.IsNullOrWhiteSpace(embed.Title)) - await _writer.WriteLineAsync(FormatMarkdown(embed.Title)); + await _writer.WriteLineAsync(await FormatMarkdownAsync(embed.Title)); if (!string.IsNullOrWhiteSpace(embed.Description)) - await _writer.WriteLineAsync(FormatMarkdown(embed.Description)); + await _writer.WriteLineAsync(await FormatMarkdownAsync(embed.Description)); foreach (var field in embed.Fields) { if (!string.IsNullOrWhiteSpace(field.Name)) - await _writer.WriteLineAsync(FormatMarkdown(field.Name)); + await _writer.WriteLineAsync(await FormatMarkdownAsync(field.Name)); if (!string.IsNullOrWhiteSpace(field.Value)) - await _writer.WriteLineAsync(FormatMarkdown(field.Value)); + await _writer.WriteLineAsync(await FormatMarkdownAsync(field.Value)); } if (!string.IsNullOrWhiteSpace(embed.Thumbnail?.Url)) @@ -174,7 +174,7 @@ internal class PlainTextMessageWriter : MessageWriter // Content if (!string.IsNullOrWhiteSpace(message.Content)) - await _writer.WriteLineAsync(FormatMarkdown(message.Content)); + await _writer.WriteLineAsync(await FormatMarkdownAsync(message.Content)); await _writer.WriteLineAsync(); diff --git a/DiscordChatExporter.Core/Markdown/Parsing/MarkdownVisitor.cs b/DiscordChatExporter.Core/Markdown/Parsing/MarkdownVisitor.cs index 77750cf..325152b 100644 --- a/DiscordChatExporter.Core/Markdown/Parsing/MarkdownVisitor.cs +++ b/DiscordChatExporter.Core/Markdown/Parsing/MarkdownVisitor.cs @@ -1,56 +1,57 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; namespace DiscordChatExporter.Core.Markdown.Parsing; internal abstract class MarkdownVisitor { - protected virtual MarkdownNode VisitText(TextNode text) => - text; + protected virtual ValueTask VisitTextAsync(TextNode text) => + new(text); - protected virtual MarkdownNode VisitFormatting(FormattingNode formatting) + protected virtual async ValueTask VisitFormattingAsync(FormattingNode formatting) { - Visit(formatting.Children); + await VisitAsync(formatting.Children); return formatting; } - protected virtual MarkdownNode VisitInlineCodeBlock(InlineCodeBlockNode inlineCodeBlock) => - inlineCodeBlock; + protected virtual ValueTask VisitInlineCodeBlockAsync(InlineCodeBlockNode inlineCodeBlock) => + new(inlineCodeBlock); - protected virtual MarkdownNode VisitMultiLineCodeBlock(MultiLineCodeBlockNode multiLineCodeBlock) => - multiLineCodeBlock; + protected virtual ValueTask VisitMultiLineCodeBlockAsync(MultiLineCodeBlockNode multiLineCodeBlock) => + new(multiLineCodeBlock); - protected virtual MarkdownNode VisitLink(LinkNode link) + protected virtual async ValueTask VisitLinkAsync(LinkNode link) { - Visit(link.Children); + await VisitAsync(link.Children); return link; } - protected virtual MarkdownNode VisitEmoji(EmojiNode emoji) => - emoji; + protected virtual ValueTask VisitEmojiAsync(EmojiNode emoji) => + new(emoji); - protected virtual MarkdownNode VisitMention(MentionNode mention) => - mention; + protected virtual ValueTask VisitMentionAsync(MentionNode mention) => + new(mention); - protected virtual MarkdownNode VisitUnixTimestamp(UnixTimestampNode timestamp) => - timestamp; + protected virtual ValueTask VisitUnixTimestampAsync(UnixTimestampNode timestamp) => + new(timestamp); - public MarkdownNode Visit(MarkdownNode node) => node switch + public async ValueTask VisitAsync(MarkdownNode node) => node switch { - TextNode text => VisitText(text), - FormattingNode formatting => VisitFormatting(formatting), - InlineCodeBlockNode inlineCodeBlock => VisitInlineCodeBlock(inlineCodeBlock), - MultiLineCodeBlockNode multiLineCodeBlock => VisitMultiLineCodeBlock(multiLineCodeBlock), - LinkNode link => VisitLink(link), - EmojiNode emoji => VisitEmoji(emoji), - MentionNode mention => VisitMention(mention), - UnixTimestampNode timestamp => VisitUnixTimestamp(timestamp), + TextNode text => await VisitTextAsync(text), + FormattingNode formatting => await VisitFormattingAsync(formatting), + InlineCodeBlockNode inlineCodeBlock => await VisitInlineCodeBlockAsync(inlineCodeBlock), + MultiLineCodeBlockNode multiLineCodeBlock => await VisitMultiLineCodeBlockAsync(multiLineCodeBlock), + LinkNode link => await VisitLinkAsync(link), + EmojiNode emoji => await VisitEmojiAsync(emoji), + MentionNode mention => await VisitMentionAsync(mention), + UnixTimestampNode timestamp => await VisitUnixTimestampAsync(timestamp), _ => throw new ArgumentOutOfRangeException(nameof(node)) }; - public void Visit(IEnumerable nodes) + public async ValueTask VisitAsync(IEnumerable nodes) { foreach (var node in nodes) - Visit(node); + await VisitAsync(node); } } \ No newline at end of file