Rework markdown parser and improve its performance for non-HTML formats

This commit is contained in:
Alexey Golub
2019-09-15 21:24:07 +03:00
parent 533671c59f
commit cd042e5368
20 changed files with 201 additions and 139 deletions

View File

@@ -27,18 +27,21 @@ namespace DiscordChatExporter.Core.Rendering
private string FormatMarkdown(Node node)
{
// Formatted node
if (node is FormattedNode formattedNode)
// Text node
if (node is TextNode textNode)
{
// Recursively get inner text
var innerText = FormatMarkdown(formattedNode.Children);
return $"{formattedNode.Token}{innerText}{formattedNode.Token}";
return textNode.Text;
}
// Non-meta mention node
if (node is MentionNode mentionNode && mentionNode.Type != MentionType.Meta)
// Mention node
if (node is MentionNode mentionNode)
{
// Meta mention node
if (mentionNode.Type == MentionType.Meta)
{
return mentionNode.Id;
}
// User mention node
if (mentionNode.Type == MentionType.User)
{
@@ -61,19 +64,19 @@ namespace DiscordChatExporter.Core.Rendering
}
}
// Custom emoji node
if (node is EmojiNode emojiNode && emojiNode.IsCustomEmoji)
// Emoji node
if (node is EmojiNode emojiNode)
{
return $":{emojiNode.Name}:";
return emojiNode.IsCustomEmoji ? $":{emojiNode.Name}:" : emojiNode.Name;
}
// All other nodes - simply return source
return node.Source;
// Throw on unexpected nodes
throw new InvalidOperationException($"Unexpected node: [{node.GetType()}].");
}
private string FormatMarkdown(IEnumerable<Node> nodes) => nodes.Select(FormatMarkdown).JoinToString("");
private string FormatMarkdown(string markdown) => FormatMarkdown(MarkdownParser.Parse(markdown));
private string FormatMarkdown(string markdown) => FormatMarkdown(MarkdownParser.ParseMinimal(markdown));
private async Task RenderFieldAsync(TextWriter writer, string value)
{

View File

@@ -90,7 +90,7 @@ namespace DiscordChatExporter.Core.Rendering
}
// Multi-line code block node
if (node is MultilineCodeBlockNode multilineCodeBlockNode)
if (node is MultiLineCodeBlockNode multilineCodeBlockNode)
{
// Set CSS class for syntax highlighting
var highlightCssClass = !multilineCodeBlockNode.Language.IsNullOrWhiteSpace()
@@ -154,14 +154,14 @@ namespace DiscordChatExporter.Core.Rendering
: $"<a href=\"{Uri.EscapeUriString(linkNode.Url)}\" onclick=\"scrollToMessage(event, '{linkedMessageId}')\">{HtmlEncode(linkNode.Title)}</a>";
}
// All other nodes - simply return source
return node.Source;
// Throw on unexpected nodes
throw new InvalidOperationException($"Unexpected node: [{node.GetType()}].");
}
private string FormatMarkdown(IReadOnlyList<Node> nodes, bool isTopLevel)
{
// Emojis are jumbo if all top-level nodes are emoji nodes, disregarding whitespace
var isJumbo = isTopLevel && nodes.Where(n => !n.Source.IsNullOrWhiteSpace()).All(n => n is EmojiNode);
// Emojis are jumbo if all top-level nodes are emoji nodes or whitespace text nodes
var isJumbo = isTopLevel && nodes.All(n => n is EmojiNode || n is TextNode textNode && textNode.Text.IsNullOrWhiteSpace());
return nodes.Select(n => FormatMarkdown(n, isJumbo)).JoinToString("");
}

View File

@@ -45,18 +45,21 @@ namespace DiscordChatExporter.Core.Rendering
private string FormatMarkdown(Node node)
{
// Formatted node
if (node is FormattedNode formattedNode)
// Text node
if (node is TextNode textNode)
{
// Recursively get inner text
var innerText = FormatMarkdown(formattedNode.Children);
return $"{formattedNode.Token}{innerText}{formattedNode.Token}";
return textNode.Text;
}
// Non-meta mention node
if (node is MentionNode mentionNode && mentionNode.Type != MentionType.Meta)
// Mention node
if (node is MentionNode mentionNode)
{
// Meta mention node
if (mentionNode.Type == MentionType.Meta)
{
return mentionNode.Id;
}
// User mention node
if (mentionNode.Type == MentionType.User)
{
@@ -79,19 +82,19 @@ namespace DiscordChatExporter.Core.Rendering
}
}
// Custom emoji node
if (node is EmojiNode emojiNode && emojiNode.IsCustomEmoji)
// Emoji node
if (node is EmojiNode emojiNode)
{
return $":{emojiNode.Name}:";
return emojiNode.IsCustomEmoji ? $":{emojiNode.Name}:" : emojiNode.Name;
}
// All other nodes - simply return source
return node.Source;
// Throw on unexpected nodes
throw new InvalidOperationException($"Unexpected node: [{node.GetType()}].");
}
private string FormatMarkdown(IEnumerable<Node> nodes) => nodes.Select(FormatMarkdown).JoinToString("");
private string FormatMarkdown(string markdown) => FormatMarkdown(MarkdownParser.Parse(markdown));
private string FormatMarkdown(string markdown) => FormatMarkdown(MarkdownParser.ParseMinimal(markdown));
private async Task RenderAttachmentsAsync(TextWriter writer, IReadOnlyList<Attachment> attachments)
{

View File

@@ -58,7 +58,7 @@ img {
}
.pre {
font-family: "Consolas", "Courier New", Courier, Monospace;
font-family: "Consolas", "Courier New", Courier, monospace;
}
.pre--multiline {