mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2024-09-19 12:18:48 -04:00
Add support for headers in markdown
This commit is contained in:
parent
d8315c7827
commit
469a731892
5 changed files with 86 additions and 4 deletions
|
@ -91,6 +91,25 @@ internal partial class HtmlMarkdownVisitor : MarkdownVisitor
|
|||
return result;
|
||||
}
|
||||
|
||||
protected override async ValueTask<MarkdownNode> VisitHeaderAsync(
|
||||
HeaderNode header,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
_buffer.Append(
|
||||
// lang=html
|
||||
$"<h{header.Level}>"
|
||||
);
|
||||
|
||||
var result = await base.VisitHeaderAsync(header, cancellationToken);
|
||||
|
||||
_buffer.Append(
|
||||
// lang=html
|
||||
$"</h{header.Level}>"
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override async ValueTask<MarkdownNode> VisitInlineCodeBlockAsync(
|
||||
InlineCodeBlockNode inlineCodeBlock,
|
||||
CancellationToken cancellationToken = default)
|
||||
|
|
|
@ -758,6 +758,37 @@
|
|||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.chatlog__markdown h1 {
|
||||
margin-block: 0;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: @Themed("#f2f3f5", "#060607");
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.chatlog__markdown h2 {
|
||||
margin-block: 0;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: @Themed("#f2f3f5", "#060607");
|
||||
font-size: 1.25rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.chatlog__markdown h3 {
|
||||
margin-block: 0;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: @Themed("#f2f3f5", "#060607");
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.chatlog__markdown h1:first-child, h2:first-child, h3:first-child {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.chatlog__markdown-preserve {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
|
8
DiscordChatExporter.Core/Markdown/HeaderNode.cs
Normal file
8
DiscordChatExporter.Core/Markdown/HeaderNode.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace DiscordChatExporter.Core.Markdown;
|
||||
|
||||
internal record HeaderNode(
|
||||
int Level,
|
||||
IReadOnlyList<MarkdownNode> Children
|
||||
) : MarkdownNode, IContainerNode;
|
|
@ -81,15 +81,15 @@ internal static partial class MarkdownParser
|
|||
private static readonly IMatcher<MarkdownNode> SingleLineQuoteNodeMatcher = new RegexMatcher<MarkdownNode>(
|
||||
// Capture any character until the end of the line.
|
||||
// Opening 'greater than' character must be followed by whitespace.
|
||||
// Text content is optional.
|
||||
new Regex(@"^>\s(.*\n?)", DefaultRegexOptions),
|
||||
// Consume the newline character so that it's not included in the content.
|
||||
new Regex(@"^>\s(.+\n?)", DefaultRegexOptions),
|
||||
(s, m) => new FormattingNode(FormattingKind.Quote, Parse(s.Relocate(m.Groups[1])))
|
||||
);
|
||||
|
||||
private static readonly IMatcher<MarkdownNode> RepeatedSingleLineQuoteNodeMatcher = new RegexMatcher<MarkdownNode>(
|
||||
// Repeatedly capture any character until the end of the line.
|
||||
// This one is tricky as it ends up producing multiple separate captures which need to be joined.
|
||||
new Regex(@"(?:^>\s(.*\n?)){2,}", DefaultRegexOptions),
|
||||
// Consume the newline character so that it's not included in the content.
|
||||
new Regex(@"(?:^>\s(.+\n?)){2,}", DefaultRegexOptions),
|
||||
(_, m) => new FormattingNode(
|
||||
FormattingKind.Quote,
|
||||
Parse(
|
||||
|
@ -106,6 +106,16 @@ internal static partial class MarkdownParser
|
|||
(s, m) => new FormattingNode(FormattingKind.Quote, Parse(s.Relocate(m.Groups[1])))
|
||||
);
|
||||
|
||||
/* Headers */
|
||||
|
||||
private static readonly IMatcher<MarkdownNode> HeaderNodeMatcher = new RegexMatcher<MarkdownNode>(
|
||||
// Capture any character until the end of the line.
|
||||
// Opening 'hash' character(s) must be followed by whitespace.
|
||||
// Consume the newline character so that it's not included in the content.
|
||||
new Regex(@"^(\#{1,3})\s(.+\n?)", DefaultRegexOptions),
|
||||
(s, m) => new HeaderNode(m.Groups[1].Length, Parse(s.Relocate(m.Groups[2])))
|
||||
);
|
||||
|
||||
/* Code blocks */
|
||||
|
||||
private static readonly IMatcher<MarkdownNode> InlineCodeBlockNodeMatcher = new RegexMatcher<MarkdownNode>(
|
||||
|
@ -330,6 +340,9 @@ internal static partial class MarkdownParser
|
|||
RepeatedSingleLineQuoteNodeMatcher,
|
||||
SingleLineQuoteNodeMatcher,
|
||||
|
||||
// Headers
|
||||
HeaderNodeMatcher,
|
||||
|
||||
// Code blocks
|
||||
MultiLineCodeBlockNodeMatcher,
|
||||
InlineCodeBlockNodeMatcher,
|
||||
|
|
|
@ -20,6 +20,14 @@ internal abstract class MarkdownVisitor
|
|||
return formatting;
|
||||
}
|
||||
|
||||
protected virtual async ValueTask<MarkdownNode> VisitHeaderAsync(
|
||||
HeaderNode header,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await VisitAsync(header.Children, cancellationToken);
|
||||
return header;
|
||||
}
|
||||
|
||||
protected virtual ValueTask<MarkdownNode> VisitInlineCodeBlockAsync(
|
||||
InlineCodeBlockNode inlineCodeBlock,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
|
@ -63,6 +71,9 @@ internal abstract class MarkdownVisitor
|
|||
FormattingNode formatting =>
|
||||
await VisitFormattingAsync(formatting, cancellationToken),
|
||||
|
||||
HeaderNode header =>
|
||||
await VisitHeaderAsync(header, cancellationToken),
|
||||
|
||||
InlineCodeBlockNode inlineCodeBlock =>
|
||||
await VisitInlineCodeBlockAsync(inlineCodeBlock, cancellationToken),
|
||||
|
||||
|
|
Loading…
Reference in a new issue