diff --git a/src/MarkdownParser/IViewSupplier.cs b/src/MarkdownParser/IViewSupplier.cs
index 2c7e231..522bb28 100644
--- a/src/MarkdownParser/IViewSupplier.cs
+++ b/src/MarkdownParser/IViewSupplier.cs
@@ -4,6 +4,12 @@ namespace MarkdownParser
{
public interface IViewSupplier
{
+ ///
+ /// get a textual line break
+ ///
+ ///
+ string GetTextualLineBreak();
+
///
/// a default text view
///
@@ -72,5 +78,30 @@ public interface IViewSupplier
/// placeholder string
///
T GetPlaceholder(string placeholderName);
+
+ ///
+ /// a view that shows fenced code (found in MD blocks starting with ```cs )
+ ///
+ ///
+ T GetFencedCodeBlock(string content, string codeInfo);
+
+ ///
+ /// a view that shows indented code (found in MD lines starting with at least 4 spaces)
+ ///
+ ///
+ T GetIndentedCodeBlock(string content);
+
+ ///
+ /// a view that shows html content
+ ///
+ ///
+ T GetHtmlBlock(string content);
+
+ ///
+ /// a view that shows reference definitions ([link]s usually at the end of the document)
+ ///
+ /// collection of Reference Definitions
+ ///
+ T GetReferenceDefinitions(IEnumerable markdownReferenceDefinitions);
}
}
diff --git a/src/MarkdownParser/ListBulletFormatter.cs b/src/MarkdownParser/ListBulletFormatter.cs
deleted file mode 100644
index 6a96d6a..0000000
--- a/src/MarkdownParser/ListBulletFormatter.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-using System;
-using System.Globalization;
-
-namespace MarkdownParser
-{
- public static class ListBulletFormatter
- {
- public static string GetListItemBullet(bool isOrderedList, int sequenceNumber, int listLevel, string listItemBulletOddCharacter, string listItemBulletEvenCharacter)
- {
- // 'listlevel' even or odd (list start at level 1 == odd)
- var isListOddLeveled = (listLevel % 2) != 0;
-
- if (!isOrderedList)
- {
- return (isListOddLeveled)
- ? listItemBulletOddCharacter
- : listItemBulletEvenCharacter;
- }
-
- const int aCharacterPosition = 97; // character 'a' position in ASCI table
- var sequenceNumberCharacterPosition = -1 + aCharacterPosition + sequenceNumber;
-
- var bullet = (isListOddLeveled)
- ? sequenceNumber.ToString()
- : Convert.ToChar(sequenceNumberCharacterPosition, CultureInfo.InvariantCulture).ToString();
-
- return $"{bullet}.";
- }
- }
-}
diff --git a/src/MarkdownParser/MarkdownParser.csproj b/src/MarkdownParser/MarkdownParser.csproj
index dc60480..5e485e8 100644
--- a/src/MarkdownParser/MarkdownParser.csproj
+++ b/src/MarkdownParser/MarkdownParser.csproj
@@ -49,4 +49,10 @@
+
+
+ <_Parameter1>MarkdownParser.Test
+
+
+
diff --git a/src/MarkdownParser/MarkdownReferenceDefinition.cs b/src/MarkdownParser/MarkdownReferenceDefinition.cs
new file mode 100644
index 0000000..5ab3892
--- /dev/null
+++ b/src/MarkdownParser/MarkdownReferenceDefinition.cs
@@ -0,0 +1,10 @@
+namespace MarkdownParser
+{
+ public class MarkdownReferenceDefinition
+ {
+ public string Label { get; set; }
+ public string Url { get; set; }
+ public string Title { get; set; }
+ public bool IsPlaceholder { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/MarkdownParser/ViewFormatter.cs b/src/MarkdownParser/ViewFormatter.cs
index af12363..851e480 100644
--- a/src/MarkdownParser/ViewFormatter.cs
+++ b/src/MarkdownParser/ViewFormatter.cs
@@ -17,12 +17,15 @@ public ViewFormatter(IViewSupplier viewSupplier)
public List Format(Block markdownBlock)
{
WriteBlockToView(markdownBlock, _writer);
+ _writer.StartAndFinalizeReferenceDefinitions();
+
return _writer.Flush();
}
public List FormatSingleBlock(Block markdownBlock)
{
WriteBlockToView(markdownBlock, _writer, false);
+
return _writer.Flush();
}
@@ -36,6 +39,7 @@ private void WriteBlockToView(Block block, ViewWriter writer, bool continueWi
switch (block.Tag)
{
case BlockTag.Document:
+ _writer.RegisterReferenceDefinitions(block.Document.ReferenceMap);
WriteBlockToView(block.FirstChild, writer);
break;
case BlockTag.Paragraph:
@@ -69,17 +73,16 @@ private void WriteBlockToView(Block block, ViewWriter writer, bool continueWi
writer.StartAndFinalizeThematicBreak();
break;
case BlockTag.FencedCode:
+ writer.StartAndFinalizeFencedCodeBlock(block.StringContent, block.FencedCodeData.Info);
+ break;
case BlockTag.IndentedCode:
- // not supported
+ writer.StartAndFinalizeIndentedCodeBlock(block.StringContent);
break;
case BlockTag.HtmlBlock:
- // TODO.....if needed
- //writer.StartBlock(BlockTag.Paragraph, block.StringContent.ToString());
- //WriteBlockToView(block.FirstChild, writer);
- //writer.FinalizeParagraphBlock();
+ writer.StartAndFinalizeHtmlBlock(block.StringContent);
break;
case BlockTag.ReferenceDefinition:
- // not supported
+ // ignore, handled at the end of document by _writer.StartAndFinalizeReferenceDefinitions()
break;
default:
throw new CommonMarkException("Block type " + block.Tag + " is not supported.", block);
@@ -114,7 +117,7 @@ private void WriteInlineToView(Inline inline, ViewWriter writer)
break;
case InlineTag.SoftBreak:
case InlineTag.LineBreak:
- writer.AddText(Environment.NewLine);
+ writer.AddText(writer.GetTextualLineBreak());
break;
case InlineTag.Placeholder:
writer.StartAndFinalizePlaceholderBlock(inline.TargetUrl);
diff --git a/src/MarkdownParser/ViewWriter.cs b/src/MarkdownParser/ViewWriter.cs
index 6eb147d..2578803 100644
--- a/src/MarkdownParser/ViewWriter.cs
+++ b/src/MarkdownParser/ViewWriter.cs
@@ -1,6 +1,6 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Diagnostics;
+using System.IO;
using System.Linq;
using CommonMark.Syntax;
@@ -10,8 +10,14 @@ public class ViewWriter
{
private IViewSupplier ViewSupplier { get; }
private List WrittenViews { get; set; } = new List();
-
private Stack> Workbench { get; } = new Stack>();
+ private Dictionary _referenceDefinitions;
+
+ public ViewWriter(IViewSupplier viewSupplier)
+ {
+ ViewSupplier = viewSupplier;
+ }
+
private ViewWriterCache GetWorkbenchItem()
{
if (Workbench.Count == 0)
@@ -22,11 +28,6 @@ private ViewWriterCache GetWorkbenchItem()
return Workbench.Peek();
}
- public ViewWriter(IViewSupplier viewSupplier)
- {
- ViewSupplier = viewSupplier;
- }
-
public List Flush()
{
var collectedViews = WrittenViews;
@@ -35,6 +36,11 @@ public List Flush()
return collectedViews;
}
+ public void RegisterReferenceDefinitions(Dictionary referenceDefinitions)
+ {
+ _referenceDefinitions = referenceDefinitions;
+ }
+
public void StartBlock(BlockTag blockType, string content = "")
{
Workbench.Push(new ViewWriterCache { ComponentType = blockType });
@@ -57,8 +63,9 @@ public void FinalizeParagraphBlock()
foreach (var itemsCacheTuple in itemsCache)
{
- var view = !string.IsNullOrEmpty(itemsCacheTuple.Item1) ?
- ViewSupplier.GetTextView(itemsCacheTuple.Item1) : itemsCacheTuple.Item2;
+ var view = !string.IsNullOrEmpty(itemsCacheTuple.Item1)
+ ? ViewSupplier.GetTextView(itemsCacheTuple.Item1)
+ : itemsCacheTuple.Item2;
if (view != null)
{
@@ -94,7 +101,6 @@ public void FinalizeHeaderBlock(int headerLevel)
var wbi = GetWorkbenchItem();
if (wbi.ComponentType != BlockTag.AtxHeading
&& wbi.ComponentType != BlockTag.SetextHeading)
-
{
Debug.WriteLine($"Finalizing Header can not finalize {wbi.ComponentType}");
return;
@@ -107,8 +113,9 @@ public void FinalizeHeaderBlock(int headerLevel)
foreach (var itemsCacheTuple in itemsCache)
{
- var view = !string.IsNullOrEmpty(itemsCacheTuple.Item1) ?
- ViewSupplier.GetHeaderView(itemsCacheTuple.Item1, headerLevel) : itemsCacheTuple.Item2;
+ var view = !string.IsNullOrEmpty(itemsCacheTuple.Item1)
+ ? ViewSupplier.GetHeaderView(itemsCacheTuple.Item1, headerLevel)
+ : itemsCacheTuple.Item2;
views.Add(view);
}
@@ -155,8 +162,9 @@ public void FinalizeListItemBlock(ListData listData)
foreach (var itemsCacheTuple in itemsCache)
{
- var view = !string.IsNullOrEmpty(itemsCacheTuple.Item1) ?
- ViewSupplier.GetTextView(itemsCacheTuple.Item1) : itemsCacheTuple.Item2;
+ var view = !string.IsNullOrEmpty(itemsCacheTuple.Item1)
+ ? ViewSupplier.GetTextView(itemsCacheTuple.Item1)
+ : itemsCacheTuple.Item2;
if (view != null)
{
@@ -164,9 +172,9 @@ public void FinalizeListItemBlock(ListData listData)
}
}
- var flattendView = StackViews(views);
+ var flattenedView = StackViews(views);
- var listItemView = ViewSupplier.GetListItemView(flattendView, isOrderedList, sequenceNumber, depthLevel);
+ var listItemView = ViewSupplier.GetListItemView(flattenedView, isOrderedList, sequenceNumber, depthLevel);
StoreView(listItemView);
}
@@ -182,10 +190,62 @@ public void StartAndFinalizeImageBlock(string targetUrl, string subscription, st
StoreView(imageView);
}
+ public void StartAndFinalizeFencedCodeBlock(StringContent content, string blockInfo)
+ {
+ var parsedContent = StringContentToStringWithLineBreaks(content);
+
+ var blockView = ViewSupplier.GetFencedCodeBlock(parsedContent, blockInfo);
+ StoreView(blockView);
+ }
+
+ public void StartAndFinalizeIndentedCodeBlock(StringContent content)
+ {
+ var parsedContent = StringContentToStringWithLineBreaks(content);
+
+ var blockView = ViewSupplier.GetIndentedCodeBlock(parsedContent);
+ StoreView(blockView);
+ }
+
+ public void StartAndFinalizeHtmlBlock(StringContent content)
+ {
+ var parsedContent = StringContentToStringWithLineBreaks(content);
+
+ var blockView = ViewSupplier.GetHtmlBlock(parsedContent);
+ StoreView(blockView);
+ }
+
+ public void StartAndFinalizeReferenceDefinitions()
+ {
+ if (_referenceDefinitions == null || _referenceDefinitions.Count == 0)
+ {
+ return;
+ }
+
+ var markdownReferenceDefinition = new List();
+ foreach (var referenceDefinition in _referenceDefinitions)
+ {
+ if (referenceDefinition.Value == null)
+ {
+ continue;
+ }
+
+ markdownReferenceDefinition.Add(new MarkdownReferenceDefinition()
+ {
+ IsPlaceholder = referenceDefinition.Value.IsPlaceholder,
+ Label = referenceDefinition.Value.Label,
+ Title = referenceDefinition.Value.Title,
+ Url = referenceDefinition.Value.Url
+ });
+ }
+
+ var view = ViewSupplier.GetReferenceDefinitions(markdownReferenceDefinition);
+ StoreView(view);
+ }
+
public void StartAndFinalizeThematicBreak()
{
- var seperator = ViewSupplier.GetThematicBreak();
- StoreView(seperator);
+ var separator = ViewSupplier.GetThematicBreak();
+ StoreView(separator);
}
public void StartAndFinalizePlaceholderBlock(string placeholderName)
@@ -194,16 +254,23 @@ public void StartAndFinalizePlaceholderBlock(string placeholderName)
StoreView(placeholderView);
}
+ public string GetTextualLineBreak()
+ {
+ return ViewSupplier.GetTextualLineBreak();
+ }
+
private T StackViews(List views)
{
- if (views == null || views.Count == 0)
+ if (views == null
+ || views.Count == 0)
{
return default(T);
}
// multiple views combine a single stack layout
- var viewToStore = views.Count == 1 ?
- views[0] : ViewSupplier.GetStackLayoutView(views);
+ var viewToStore = views.Count == 1
+ ? views[0]
+ : ViewSupplier.GetStackLayoutView(views);
return viewToStore;
}
@@ -226,6 +293,18 @@ private void StoreView(T view)
WrittenViews.Add(view);
}
}
+
+ private string StringContentToStringWithLineBreaks(StringContent content)
+ {
+ var stringWriter = new StringWriter();
+ content.WriteTo(stringWriter);
+ var contentLines = stringWriter.ToString();
+
+ contentLines = contentLines.Replace("\r", "");
+ contentLines = contentLines.TrimEnd('\n');
+ contentLines = contentLines.Replace("\n", GetTextualLineBreak());
+ return contentLines;
+ }
}
}
diff --git a/test/MarkdownParser.Test/MarkdownParser.Test.csproj b/test/MarkdownParser.Test/MarkdownParser.Test.csproj
index f93842d..9637277 100644
--- a/test/MarkdownParser.Test/MarkdownParser.Test.csproj
+++ b/test/MarkdownParser.Test/MarkdownParser.Test.csproj
@@ -13,20 +13,26 @@
+
+
+
+
+
+
diff --git a/test/MarkdownParser.Test/MarkdownParserSectionsSpecs.cs b/test/MarkdownParser.Test/MarkdownParserSectionsSpecs.cs
index 5617526..8500dba 100644
--- a/test/MarkdownParser.Test/MarkdownParserSectionsSpecs.cs
+++ b/test/MarkdownParser.Test/MarkdownParserSectionsSpecs.cs
@@ -98,4 +98,132 @@ public void When_parsing_nested_list_it_should_output_nesting_by_level()
splittedViews[12].Should().Be("_False.2.1_textview");
splittedViews[13].Should().Be("item2-3(mockComponentSupplier);
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Act
+ //-----------------------------------------------------------------------------------------------------------
+ var parseResult = parser.Parse(markdown);
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Assert
+ //-----------------------------------------------------------------------------------------------------------
+ parseResult.Count.Should().Be(2);
+
+ var codeViewComponentsGroup0 = parseResult[0].Split('|');
+ codeViewComponentsGroup0.Length.Should().Be(4);
+ codeViewComponentsGroup0[0].Should().Be("fencedcodeview>");
+ codeViewComponentsGroup0[1].Should().Be("(cs)");
+ codeViewComponentsGroup0[2].Should().Be("var myNumber = 1;\r\nmyNumber++;");
+ codeViewComponentsGroup0[3].Should().Be("");
+ codeViewComponentsGroup1[1].Should().Be("the first line for IndentedCode code block\r\nthe second line for IndentedCode code block\r\nthe third line for IndentedCode code block");
+ codeViewComponentsGroup1[2].Should().Be("(mockComponentSupplier);
+
+ var newLineIndicator = mockComponentSupplier.GetTextualLineBreak();
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Act
+ //-----------------------------------------------------------------------------------------------------------
+ var parseResult = parser.Parse(markdown);
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Assert
+ //-----------------------------------------------------------------------------------------------------------
+ parseResult.Count.Should().Be(2);
+
+ var htmlViewComponentsGroup0 = parseResult[0].Split('|');
+ htmlViewComponentsGroup0.Length.Should().Be(3);
+ htmlViewComponentsGroup0.First().Should().Be("htmlview>");
+ htmlViewComponentsGroup0.Last().Should().Be("First text in block
");
+ firstHtmlViewContentGroup[1].Trim().Should().Be("");
+ firstHtmlViewContentGroup[2].Trim().Should().Be("
Header
");
+ firstHtmlViewContentGroup[3].Trim().Should().Be("
Same block but nested element
");
+ firstHtmlViewContentGroup[4].Trim().Should().Be("
");
+
+ var htmlViewComponentsGroup1 = parseResult[1].Split('|');
+ htmlViewComponentsGroup1.Length.Should().Be(3);
+ htmlViewComponentsGroup1.First().Should().Be("htmlview>");
+ htmlViewComponentsGroup1.Last().Should().Be("");
+ secondHtmlViewContentGroup[1].Trim().Should().Be("");
+ secondHtmlViewContentGroup[6].Trim().Should().Be("Lorem Ipsum...
");
+ secondHtmlViewContentGroup[7].Trim().Should().Be("");
+ }
+
+ [TestMethod]
+ public void When_parsing_reference_definitions_it_should_output_specific_views()
+ {
+ //-----------------------------------------------------------------------------------------------------------
+ // Arrange
+ //-----------------------------------------------------------------------------------------------------------
+ var markdown = FileReader.ReadFile("Sections.referencedefinitions.md");
+
+ var mockComponentSupplier = new StringComponentSupplier();
+ var parser = new MarkdownParser(mockComponentSupplier);
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Act
+ //-----------------------------------------------------------------------------------------------------------
+ var parseResult = parser.Parse(markdown);
+
+ //-----------------------------------------------------------------------------------------------------------
+ // Assert
+ //-----------------------------------------------------------------------------------------------------------
+ parseResult.Count.Should().Be(2);
+ parseResult[0].Should().StartWith("stackview>:+textview");
+
+ var referenceDefinitionsViewGroup = parseResult[1].Split('|');
+ referenceDefinitionsViewGroup.Length.Should().Be(4);
+ referenceDefinitionsViewGroup.First().Should().Be("referencedefinitions>");
+ referenceDefinitionsViewGroup.Last().Should().Be("|({codeInfo})|{content}||{content}||{content}| markdownReferenceDefinitions)
+ {
+ var content = "referencedefinitions>";
+ foreach (var markdownReferenceDefinition in markdownReferenceDefinitions)
+ {
+ content += $"|{markdownReferenceDefinition.IsPlaceholder}";
+ content += $"*{markdownReferenceDefinition.Label}";
+ content += $"*{markdownReferenceDefinition.Title}";
+ content += $"*{markdownReferenceDefinition.Url}";
+ }
+
+ content += "|First text in block
+
+
Header
+
Same block but nested element
+
+
+
+
+ Lorem Ipsum...
+
\ No newline at end of file
diff --git a/test/MarkdownParser.Test/Resources/Examples/Sections/referencedefinitions.md b/test/MarkdownParser.Test/Resources/Examples/Sections/referencedefinitions.md
new file mode 100644
index 0000000..75933d9
--- /dev/null
+++ b/test/MarkdownParser.Test/Resources/Examples/Sections/referencedefinitions.md
@@ -0,0 +1,7 @@
+[link]: /uri "title"
+
+Vestibulum dictum lacinia lacus, at ornare quam consequat ultrices.
+Nam quam leo, aliquet in luctus in, [porttitor non quam]. Donec tincidunt augue nisi,
+sed pellentesque nisl porttitor vel.
+
+[porttitor non quam]: https://lipsum.com/
\ No newline at end of file