diff --git a/src/Markdig.Tests/Specs/DefinitionListSpecs.generated.cs b/src/Markdig.Tests/Specs/DefinitionListSpecs.generated.cs
new file mode 100644
index 0000000..b76cf50
--- /dev/null
+++ b/src/Markdig.Tests/Specs/DefinitionListSpecs.generated.cs
@@ -0,0 +1,195 @@
+
+// --------------------------------
+// Definition Lists
+// --------------------------------
+
+using System;
+using NUnit.Framework;
+
+namespace Markdig.Tests.Specs.DefinitionLists
+{
+ [TestFixture]
+ public class TestExtensionsDefinitionLists
+ {
+ // # Extensions
+ //
+ // This section describes the different extensions supported:
+ //
+ // ## Definition lists
+ //
+ // A custom container is similar to a fenced code block, but it is using the character `:` to declare a block (with at least 3 characters), and instead of generating a `
...
` it will generate a `...
` block.
+ [Test]
+ public void ExtensionsDefinitionLists_Example001()
+ {
+ // Example 1
+ // Section: Extensions / Definition lists
+ //
+ // The following Markdown:
+ //
+ // Term 1
+ // : This is a definition item
+ // With a paragraph
+ // > This is a block quote
+ //
+ // - This is a list
+ // - with an item2
+ //
+ // ```java
+ // Test
+ //
+ //
+ // ```
+ //
+ // And a last line
+ // : This ia another definition item
+ //
+ // Term2
+ // Term3 *with some inline*
+ // : This is another definition for term2
+ //
+ // Should be rendered as:
+ //
+ // - Term 1
+ // This is a definition item
+ // With a paragraph
+ //
+ // This is a block quote
+ //
+ //
+ // - This is a list
+ // - with an item2
+ //
+ // Test
+ //
+ //
+ //
+ // And a last line
+ //
+ // - This ia another definition item
+ // - Term2
+ // - Term3 with some inline
+ // - This is another definition for term2
+ //
+
+ TestParser.TestSpec("\nTerm 1\n: This is a definition item\n With a paragraph\n > This is a block quote\n\n - This is a list\n - with an item2\n\n ```java\n Test\n\n\n ```\n\n And a last line\n: This ia another definition item\n\nTerm2\nTerm3 *with some inline*\n: This is another definition for term2", "\n- Term 1
\nThis is a definition item\nWith a paragraph
\n\nThis is a block quote
\n
\n\n- This is a list
\n- with an item2
\n
\nTest\n\n\n
\nAnd a last line
\n \n- This ia another definition item
\n- Term2
\n- Term3 with some inline
\n- This is another definition for term2
\n
", "definitionlists+attributes|advanced", context: "Example 1\nSection Extensions / Definition lists\n");
+ }
+
+ // A definition term can be followed at most by one blank line. Lazy continuations are supported for definitions:
+ [Test]
+ public void ExtensionsDefinitionLists_Example002()
+ {
+ // Example 2
+ // Section: Extensions / Definition lists
+ //
+ // The following Markdown:
+ // Term 1
+ //
+ // : Definition
+ // with lazy continuation.
+ //
+ // Second paragraph of the definition.
+ //
+ // Should be rendered as:
+ //
+ // - Term 1
+ // Definition
+ // with lazy continuation.
+ // Second paragraph of the definition.
+ //
+ //
+
+ TestParser.TestSpec("Term 1\n\n: Definition\nwith lazy continuation.\n\n Second paragraph of the definition.", "\n- Term 1
\nDefinition\nwith lazy continuation.
\nSecond paragraph of the definition.
\n \n
", "definitionlists+attributes|advanced", context: "Example 2\nSection Extensions / Definition lists\n");
+ }
+
+ // The definition must be indented to 4 characters including the `:`.
+ [Test]
+ public void ExtensionsDefinitionLists_Example003()
+ {
+ // Example 3
+ // Section: Extensions / Definition lists
+ //
+ // The following Markdown:
+ // Term 1
+ // : Invalid with less than 3 characters
+ //
+ // Should be rendered as:
+ // Term 1
+ // : Invalid with less than 3 characters
+
+ TestParser.TestSpec("Term 1\n: Invalid with less than 3 characters", "Term 1\n: Invalid with less than 3 characters
", "definitionlists+attributes|advanced", context: "Example 3\nSection Extensions / Definition lists\n");
+ }
+
+ // The `:` can be indented up to 3 spaces:
+ [Test]
+ public void ExtensionsDefinitionLists_Example004()
+ {
+ // Example 4
+ // Section: Extensions / Definition lists
+ //
+ // The following Markdown:
+ // Term 1
+ // : Valid even if `:` starts at most 3 spaces
+ //
+ // Should be rendered as:
+ //
+ // - Term 1
+ // - Valid even if
: starts at most 3 spaces
+ //
+
+ TestParser.TestSpec("Term 1\n : Valid even if `:` starts at most 3 spaces", "\n- Term 1
\n- Valid even if
: starts at most 3 spaces \n
", "definitionlists+attributes|advanced", context: "Example 4\nSection Extensions / Definition lists\n");
+ }
+
+ // But more than 3 spaces before `:` will trigger an indented code block:
+ [Test]
+ public void ExtensionsDefinitionLists_Example005()
+ {
+ // Example 5
+ // Section: Extensions / Definition lists
+ //
+ // The following Markdown:
+ // Term 1
+ //
+ // : Not valid
+ //
+ // Should be rendered as:
+ // Term 1
+ // : Not valid
+ //
+
+ TestParser.TestSpec("Term 1\n\n : Not valid", "Term 1
\n: Not valid\n
", "definitionlists+attributes|advanced", context: "Example 5\nSection Extensions / Definition lists\n");
+ }
+
+ // Definition lists can be nested inside list items
+ [Test]
+ public void ExtensionsDefinitionLists_Example006()
+ {
+ // Example 6
+ // Section: Extensions / Definition lists
+ //
+ // The following Markdown:
+ // 1. First
+ //
+ // 2. Second
+ //
+ // Term 1
+ // : Definition
+ //
+ // Term 2
+ // : Second Definition
+ //
+ // Should be rendered as:
+ //
+ // First
+ // Second
+ //
+ // - Term 1
+ // - Definition
+ // - Term 2
+ // - Second Definition
+ //
+ //
+
+ TestParser.TestSpec("1. First\n \n2. Second\n \n Term 1\n : Definition\n \n Term 2\n : Second Definition", "\nFirst
\nSecond
\n\n- Term 1
\n- Definition
\n- Term 2
\n- Second Definition
\n
\n
", "definitionlists+attributes|advanced", context: "Example 6\nSection Extensions / Definition lists\n");
+ }
+ }
+}
diff --git a/src/Markdig.Tests/Specs/DefinitionListSpecs.md b/src/Markdig.Tests/Specs/DefinitionListSpecs.md
new file mode 100644
index 0000000..b0b1522
--- /dev/null
+++ b/src/Markdig.Tests/Specs/DefinitionListSpecs.md
@@ -0,0 +1,132 @@
+# Extensions
+
+This section describes the different extensions supported:
+
+## Definition lists
+
+A custom container is similar to a fenced code block, but it is using the character `:` to declare a block (with at least 3 characters), and instead of generating a `...
` it will generate a `...
` block.
+
+```````````````````````````````` example
+
+Term 1
+: This is a definition item
+ With a paragraph
+ > This is a block quote
+
+ - This is a list
+ - with an item2
+
+ ```java
+ Test
+
+
+ ```
+
+ And a last line
+: This ia another definition item
+
+Term2
+Term3 *with some inline*
+: This is another definition for term2
+.
+
+- Term 1
+This is a definition item
+With a paragraph
+
+This is a block quote
+
+
+- This is a list
+- with an item2
+
+Test
+
+
+
+And a last line
+
+- This ia another definition item
+- Term2
+- Term3 with some inline
+- This is another definition for term2
+
+````````````````````````````````
+
+A definition term can be followed at most by one blank line. Lazy continuations are supported for definitions:
+
+```````````````````````````````` example
+Term 1
+
+: Definition
+with lazy continuation.
+
+ Second paragraph of the definition.
+.
+
+- Term 1
+Definition
+with lazy continuation.
+Second paragraph of the definition.
+
+
+````````````````````````````````
+
+The definition must be indented to 4 characters including the `:`.
+
+```````````````````````````````` example
+Term 1
+: Invalid with less than 3 characters
+.
+Term 1
+: Invalid with less than 3 characters
+````````````````````````````````
+
+The `:` can be indented up to 3 spaces:
+
+```````````````````````````````` example
+Term 1
+ : Valid even if `:` starts at most 3 spaces
+.
+
+- Term 1
+- Valid even if
: starts at most 3 spaces
+
+````````````````````````````````
+
+But more than 3 spaces before `:` will trigger an indented code block:
+
+```````````````````````````````` example
+Term 1
+
+ : Not valid
+.
+Term 1
+: Not valid
+
+````````````````````````````````
+
+Definition lists can be nested inside list items
+
+```````````````````````````````` example
+1. First
+
+2. Second
+
+ Term 1
+ : Definition
+
+ Term 2
+ : Second Definition
+.
+
+First
+Second
+
+- Term 1
+- Definition
+- Term 2
+- Second Definition
+
+
+````````````````````````````````
diff --git a/src/Markdig/Extensions/DefinitionLists/DefinitionListParser.cs b/src/Markdig/Extensions/DefinitionLists/DefinitionListParser.cs
new file mode 100644
index 0000000..85eb808
--- /dev/null
+++ b/src/Markdig/Extensions/DefinitionLists/DefinitionListParser.cs
@@ -0,0 +1,208 @@
+// Copyright (c) Alexandre Mutel. All rights reserved.
+// This file is licensed under the BSD-Clause 2 license.
+// See the license.txt file in the project root for more information.
+
+using Markdig.Parsers;
+using Markdig.Syntax;
+
+namespace Markdig.Extensions.DefinitionLists;
+
+///
+/// The block parser for a .
+///
+///
+public class DefinitionListParser : BlockParser
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DefinitionListParser()
+ {
+ OpeningCharacters = [':', '~'];
+ }
+
+ ///
+ /// Attempts to open a block at the current parser position.
+ ///
+ public override BlockState TryOpen(BlockProcessor processor)
+ {
+ var paragraphBlock = processor.LastBlock as ParagraphBlock;
+ if (processor.IsCodeIndent || paragraphBlock is null || paragraphBlock.LastLine - processor.LineIndex > 1)
+ {
+ return BlockState.None;
+ }
+
+ var startPosition = processor.Start;
+
+ var column = processor.ColumnBeforeIndent;
+ processor.NextChar();
+ processor.ParseIndent();
+ var delta = processor.Column - column;
+
+ // We expect to have a least
+ if (delta < 4)
+ {
+ // Return back to original position
+ processor.GoToColumn(column);
+ return BlockState.None;
+ }
+
+ if (delta > 4)
+ {
+ processor.GoToColumn(column + 4);
+ }
+
+ var previousParent = paragraphBlock.Parent!;
+ var currentDefinitionList = GetCurrentDefinitionList(paragraphBlock, previousParent);
+
+ processor.Discard(paragraphBlock);
+
+ // If the paragraph block was not part of the opened blocks, we need to remove it manually from its parent container
+ if (paragraphBlock.Parent != null)
+ {
+ paragraphBlock.Parent.Remove(paragraphBlock);
+ }
+
+ if (currentDefinitionList is null)
+ {
+ currentDefinitionList = new DefinitionList(this)
+ {
+ Span = new SourceSpan(paragraphBlock.Span.Start, processor.Line.End),
+ Column = paragraphBlock.Column,
+ Line = paragraphBlock.Line,
+ };
+ previousParent.Add(currentDefinitionList);
+ }
+
+ var definitionItem = new DefinitionItem(this)
+ {
+ Line = processor.LineIndex,
+ Column = column,
+ Span = new SourceSpan(paragraphBlock.Span.Start, processor.Line.End),
+ OpeningCharacter = processor.CurrentChar,
+ };
+
+ for (int i = 0; i < paragraphBlock.Lines.Count; i++)
+ {
+ var line = paragraphBlock.Lines.Lines[i];
+ var term = new DefinitionTerm(this)
+ {
+ Column = paragraphBlock.Column,
+ Line = line.Line,
+ Span = new SourceSpan(paragraphBlock.Span.Start, paragraphBlock.Span.End),
+ IsOpen = false
+ };
+ term.AppendLine(ref line.Slice, line.Column, line.Line, line.Position, processor.TrackTrivia);
+ definitionItem.Add(term);
+ }
+ currentDefinitionList.Add(definitionItem);
+ processor.Open(definitionItem);
+
+ // Update the end position
+ currentDefinitionList.UpdateSpanEnd(processor.Line.End);
+
+ return BlockState.Continue;
+ }
+
+ private static DefinitionList? GetCurrentDefinitionList(ParagraphBlock paragraphBlock, ContainerBlock previousParent)
+ {
+ var index = previousParent.IndexOf(paragraphBlock) - 1;
+ if (index < 0) return null;
+ switch (previousParent[index])
+ {
+ case DefinitionList definitionList:
+ return definitionList;
+
+ case BlankLineBlock:
+ if (index > 0 && previousParent[index - 1] is DefinitionList definitionList2)
+ {
+ previousParent.RemoveAt(index);
+ return definitionList2;
+ }
+ break;
+ }
+ return null;
+ }
+
+ ///
+ /// Attempts to continue parsing the specified block.
+ ///
+ public override BlockState TryContinue(BlockProcessor processor, Block block)
+ {
+ var definitionItem = (DefinitionItem)block;
+ if (processor.IsCodeIndent)
+ {
+ processor.GoToCodeIndent();
+ return BlockState.Continue;
+ }
+
+ var list = (DefinitionList)definitionItem.Parent!;
+ var lastBlankLine = definitionItem.LastChild as BlankLineBlock;
+
+ // Check if we have another definition list
+ if (Array.IndexOf(OpeningCharacters!, processor.CurrentChar) >= 0)
+ {
+ var startPosition = processor.Start;
+ var column = processor.ColumnBeforeIndent;
+ processor.NextChar();
+ processor.ParseIndent();
+ var delta = processor.Column - column;
+
+ // We expect to have a least
+ if (delta < 4)
+ {
+ // Remove the blankline before breaking this definition item
+ if (lastBlankLine != null)
+ {
+ definitionItem.RemoveAt(definitionItem.Count - 1);
+ }
+
+ list.Span.End = list.LastChild!.Span.End;
+ return BlockState.None;
+ }
+
+ if (delta > 4)
+ {
+ processor.GoToColumn(column + 4);
+ }
+
+ processor.Close(definitionItem);
+ var nextDefinitionItem = new DefinitionItem(this)
+ {
+ Span = new SourceSpan(startPosition, processor.Line.End),
+ Line = processor.LineIndex,
+ Column = processor.Column,
+ OpeningCharacter = processor.CurrentChar,
+ };
+ list.Add(nextDefinitionItem);
+ processor.Open(nextDefinitionItem);
+
+ return BlockState.Continue;
+ }
+
+ var isBreakable = definitionItem.LastChild?.IsBreakable ?? true;
+ if (processor.IsBlankLine)
+ {
+ if (lastBlankLine is null && isBreakable)
+ {
+ definitionItem.Add(new BlankLineBlock());
+ }
+ return isBreakable ? BlockState.ContinueDiscard : BlockState.Continue;
+ }
+
+ var paragraphBlock = definitionItem.LastChild as ParagraphBlock;
+ if (lastBlankLine is null && paragraphBlock != null)
+ {
+ return BlockState.Continue;
+ }
+
+ // Remove the blankline before breaking this definition item
+ if (lastBlankLine != null)
+ {
+ definitionItem.RemoveAt(definitionItem.Count - 1);
+ }
+
+ list.Span.End = list.LastChild!.Span.End;
+ return BlockState.Break;
+ }
+}