Extension - Header sections #296

Closed
opened 2026-01-29 14:33:03 +00:00 by claunia · 3 comments
Owner

Originally created by @LukeTOBrien on GitHub (May 25, 2019).

Hello there,

I am currently working on a project using Markdown-it and I am looking to move to .NET and Markdig.
One of the plugins I am using is Markdown-it-header-sections - I think that would be a neat little extension for Markdig.

For the time being I will try and do the same sort of thing with processing the output.

Whatya think?

Luke

Originally created by @LukeTOBrien on GitHub (May 25, 2019). Hello there, I am currently working on a project using [Markdown-it](https://github.com/markdown-it/markdown-it) and I am looking to move to .NET and Markdig. One of the plugins I am using is [Markdown-it-header-sections](https://github.com/arve0/markdown-it-header-sections) - I think that would be a neat little extension for Markdig. For the time being I will try and do the same sort of thing with processing the output. Whatya think? Luke
Author
Owner

@MihaZupan commented on GitHub (May 25, 2019):

I think this should be achievable by a single post-processing sweep over the syntax tree, keeping a Stack<heading level> along the way.

There's no need to even touch the more complicated parsing logic.

@MihaZupan commented on GitHub (May 25, 2019): I think this should be achievable by a single post-processing sweep over the syntax tree, keeping a `Stack<heading level>` along the way. There's no need to even touch the more complicated parsing logic.
Author
Owner

@MihaZupan commented on GitHub (May 26, 2019):

At least for the simple case of section tags without any attributes and such, this is sufficient.

using Markdig;
using Markdig.Helpers;
using Markdig.Renderers;
using Markdig.Syntax;
using System;
using System.Collections.Generic;
using System.IO;

namespace MarkdigTest
{
    class Program
    {
        static void Main()
        {
            const string markdown = @"
# Header 1
Text.
### Header 2
Lorem?
## Header 3
Ipsum.
# Last header
Markdown rules!";

            MarkdownDocument document = Markdown.Parse(markdown);
            AddSections(document);

            StringWriter writer = new StringWriter();
            var renderer = new HtmlRenderer(writer);
            renderer.Render(document);
            writer.Flush();

            Console.WriteLine(writer.ToString());
        }

        static void AddSections(MarkdownDocument document)
        {
            // Create new instances as they cannot be reinserted
            static HtmlBlock openSection() => new HtmlBlock(null) { Lines = new StringLineGroup("<section>") };
            static HtmlBlock closeSection() => new HtmlBlock(null) { Lines = new StringLineGroup("</section>") };

            Stack<int> headingStack = new Stack<int>();

            foreach (var heading in document.Descendants<HeadingBlock>())
            {
                int level = headingStack.Count == 0 ? 0 : headingStack.Peek();
                var parent = heading.Parent;

                if (heading.Level > level)
                {
                    headingStack.Push(heading.Level);
                    parent.Insert(parent.IndexOf(heading), openSection());
                }
                else
                {
                    var index = parent.IndexOf(heading);

                    while (headingStack.TryPeek(out level) && level >= heading.Level)
                    {
                        headingStack.Pop();
                        parent.Insert(index++, closeSection());
                    }

                    headingStack.Push(heading.Level);
                    parent.Insert(index, openSection());
                }
            }

            for (int i = headingStack.Count; i > 0; i--)
                document.Add(closeSection());
        }
    }
}

@MihaZupan commented on GitHub (May 26, 2019): At least for the simple case of section tags without any attributes and such, this is sufficient. ```c# using Markdig; using Markdig.Helpers; using Markdig.Renderers; using Markdig.Syntax; using System; using System.Collections.Generic; using System.IO; namespace MarkdigTest { class Program { static void Main() { const string markdown = @" # Header 1 Text. ### Header 2 Lorem? ## Header 3 Ipsum. # Last header Markdown rules!"; MarkdownDocument document = Markdown.Parse(markdown); AddSections(document); StringWriter writer = new StringWriter(); var renderer = new HtmlRenderer(writer); renderer.Render(document); writer.Flush(); Console.WriteLine(writer.ToString()); } static void AddSections(MarkdownDocument document) { // Create new instances as they cannot be reinserted static HtmlBlock openSection() => new HtmlBlock(null) { Lines = new StringLineGroup("<section>") }; static HtmlBlock closeSection() => new HtmlBlock(null) { Lines = new StringLineGroup("</section>") }; Stack<int> headingStack = new Stack<int>(); foreach (var heading in document.Descendants<HeadingBlock>()) { int level = headingStack.Count == 0 ? 0 : headingStack.Peek(); var parent = heading.Parent; if (heading.Level > level) { headingStack.Push(heading.Level); parent.Insert(parent.IndexOf(heading), openSection()); } else { var index = parent.IndexOf(heading); while (headingStack.TryPeek(out level) && level >= heading.Level) { headingStack.Pop(); parent.Insert(index++, closeSection()); } headingStack.Push(heading.Level); parent.Insert(index, openSection()); } } for (int i = headingStack.Count; i > 0; i--) document.Add(closeSection()); } } } ```
Author
Owner

@LukeTOBrien commented on GitHub (May 26, 2019):

Thanks!
I never expected you to write this for me... Saves me having to do the job.
Perhaps this code snippet could be included in any future documentation encase anyone else has the need to do this?

I have created a Unit Test and here is the output I am getting:
For my purposes I only need two levels, so this test proves it works for me.

<section>
  <h1>Header 1</h1>
  <p>Text.</p>
  <section>
    <h3>Header 2</h3>
    <p>Lorem?</p>
  </section>
  <section>
    <h2>Header 3</h2>
    <p>Ipsum.</p>
  </section>
</section>
<section>
  <h1>Last header</h1>
  <p>Markdown rules!</p>
</section>
@LukeTOBrien commented on GitHub (May 26, 2019): Thanks! I never expected you to write this for me... Saves me having to do the job. Perhaps this code snippet could be included in any future documentation encase anyone else has the need to do this? I have created a Unit Test and here is the output I am getting: For my purposes I only need two levels, so this test proves it works for me. ```HTML <section> <h1>Header 1</h1> <p>Text.</p> <section> <h3>Header 2</h3> <p>Lorem?</p> </section> <section> <h2>Header 3</h2> <p>Ipsum.</p> </section> </section> <section> <h1>Last header</h1> <p>Markdown rules!</p> </section> ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/markdig#296