79539494

Date: 2025-03-27 16:34:09
Score: 0.5
Natty:
Report link

To expand on @e-shcherbo's dotnetfiddle, and for anyone wanting to leverage more functionality provided by Markdig's header parsing, the following query enables the generation of a table of contents using the headers parsed by Markdig.

The presentation result would be similar to the 'In this article' list provided in the sidebar of a MDN article.

Add the.UseAutoIdentifiers() extension to the Markdig pipeline so that an id attribute is assigned to each HeadingBlock AST node during parsing. The ID is generated using the text of the header.

IEnumerable<Header> headers = document
    .Descendants<HeadingBlock>()
    .Where(hb => hb?.Level != null && !string.IsNullOrWhiteSpace(hb?.Inline?.FirstChild?.ToString()))
    .Select(hb => new Header()
    {
        Id = hb?.GetAttributes().Id,
        Level = hb?.Level,
        Text = hb?.Inline?.FirstChild?.ToString(),
    });

The following is the result of the above LINQ query on the Markdown sample listed in the original post:

IEnumerable result for the LINQ query

Details of the LINQ query

The ID value is retrieved using the GetAttributes() method on a HeadingBlock node.

The Level property on the HeadingBlock node is the key value that enables a branching table of contents sidebar.

You can extend the Where() predicate to retrieve only level 2 (h2) headers (e.g. .Where(hb => hb?.Level == 2)), similar to the list provided in the 'In this article' MDN article sidebar.

The .Where() predicate skips HeadingBlock nodes that have an empty string.

The Header type in the .Select() method is defined as:

public class Header
{
    public string? Id { get; set; }
    public string? Text { get; set; }
    public int? Level { get; set; }
}
Reasons:
  • Blacklisted phrase (1): this article
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @e-shcherbo's
Posted by: Dave B