89 lines
2.7 KiB
C#
89 lines
2.7 KiB
C#
using FluentValidation;
|
|
using Markdig.Renderers.Roundtrip;
|
|
using Markdig.Syntax;
|
|
using MfGames.Gallium;
|
|
using MfGames.Nitride.Contents;
|
|
using MfGames.Nitride.Generators;
|
|
|
|
namespace MfGames.Nitride.Markdown;
|
|
|
|
/// <summary>
|
|
/// An operation that parses the Markdown and converts the first heading one
|
|
/// into a model to include in the component. The rest of the Markdown is put
|
|
/// back as the text content of the entity.
|
|
/// </summary>
|
|
[WithProperties]
|
|
public partial class ParseMarkdownHeadingOne : IOperation
|
|
{
|
|
private readonly IValidator<ParseMarkdownHeadingOne> validator;
|
|
|
|
public ParseMarkdownHeadingOne(IValidator<ParseMarkdownHeadingOne> validator)
|
|
{
|
|
this.validator = validator;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets a callback for adding a heading to a given entity.
|
|
/// </summary>
|
|
public Func<Entity, string?, Entity>? AddModelCallback { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<Entity> Run(
|
|
IEnumerable<Entity> input,
|
|
CancellationToken cancellationToken = default
|
|
)
|
|
{
|
|
this.validator.ValidateAndThrow(this);
|
|
|
|
return input.SelectManyEntity<IsMarkdown>(x => x.Select(this.Parse));
|
|
}
|
|
|
|
private string? GetText(MarkdownObject? block)
|
|
{
|
|
if (block == null)
|
|
return null;
|
|
|
|
var writer = new StringWriter();
|
|
var renderer = new RoundtripRenderer(writer);
|
|
|
|
renderer.Write(block);
|
|
|
|
return writer.ToString();
|
|
}
|
|
|
|
private Entity Parse(Entity entity)
|
|
{
|
|
// Get the text content of the file. No text, we don't do anything (but
|
|
// there is going to be text since we filtered on IsMarkdown).
|
|
string? oldText = entity.GetTextContentString();
|
|
|
|
if (oldText == null)
|
|
{
|
|
return entity;
|
|
}
|
|
|
|
// Parse the result as Markdown and pull out the heading. If we can't
|
|
// find one, then we just return the entity. We need to track trivia
|
|
// because we are round-tripping back to Markdown.
|
|
MarkdownDocument document = Markdig.Markdown.Parse(oldText, true);
|
|
Block? block = document.FirstOrDefault(block => block is HeadingBlock);
|
|
|
|
if (block is not HeadingBlock { Level: 1 } heading)
|
|
{
|
|
return entity;
|
|
}
|
|
|
|
string? headingText = this.GetText(heading.Inline);
|
|
|
|
// Convert the heading into the model.
|
|
// Pull out the heading so we can write the rest back.
|
|
document.Remove(heading);
|
|
|
|
string newText = this.GetText(document)!;
|
|
|
|
// Allow the extending class to add the model to the entity and then
|
|
// set the text content to the new value before returning the results.
|
|
return this.AddModelCallback!.Invoke(entity, headingText).SetTextContent(newText);
|
|
}
|
|
}
|