diff --git a/src/Plugin.Maui.MarkdownView/IViewSupplier.cs b/src/Plugin.Maui.MarkdownView/IViewSupplier.cs deleted file mode 100644 index 3c631c2..0000000 --- a/src/Plugin.Maui.MarkdownView/IViewSupplier.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace Plugin.Maui.MarkdownView; - -public interface IViewSupplier -{ - /// - /// a default text view - /// - /// - /// - T GetTextView(string content); - - /// - /// a block quote view, where other views can be inserted - /// - /// - /// - T GetBlockquotesView(T childView); - - /// - /// a title, subtitle or header view - /// - /// - /// - /// - T GetHeaderView(string content, int headerLevel); - - /// - /// a image view with a optional subscription text view - /// - /// image location - /// (optional) null or empty when not used - /// (optional) id for image - /// - T GetImageView(string url, string subscription, string imageId); - - /// - /// a view that shows a list of listitems - /// - /// - /// - T GetListView(List items); - - /// - /// a view that shows a single item for a ListView (return View can be used in GetListView) - /// - /// view as childview (or use the content parameter) - /// does the item belong to a ordered (numbered) list - /// number of sequence - /// level depth of the list, root level starting at 1 - /// - T GetListItemView(T childView, bool isOrderedList, int sequenceNumber, int listLevel); - - /// - /// a layout that shows a collection of views - /// - /// collection of views - /// - T GetStackLayoutView(List childViews); - - /// - /// a image view that separates content - /// - /// - T GetThematicBreak(); - - - /// - /// a placeholder for views or other objects - /// - /// placeholder string - /// - T GetPlaceholder(string placeholderName); -} \ No newline at end of file diff --git a/src/Plugin.Maui.MarkdownView/LICENSE-CommonMark.NET.md b/src/Plugin.Maui.MarkdownView/LICENSE-CommonMark.NET.md deleted file mode 100644 index 956e71f..0000000 --- a/src/Plugin.Maui.MarkdownView/LICENSE-CommonMark.NET.md +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2014, Kārlis Gaņģis -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of Kārlis Gaņģis nor the names of other contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/src/Plugin.Maui.MarkdownView/ListBulletFormatter.cs b/src/Plugin.Maui.MarkdownView/ListBulletFormatter.cs deleted file mode 100644 index 0a331cf..0000000 --- a/src/Plugin.Maui.MarkdownView/ListBulletFormatter.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Globalization; - -namespace Plugin.Maui.MarkdownView; - -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}."; - } -} \ No newline at end of file diff --git a/src/Plugin.Maui.MarkdownView/MarkdownParseStream.cs b/src/Plugin.Maui.MarkdownView/MarkdownParseStream.cs deleted file mode 100644 index fed6a15..0000000 --- a/src/Plugin.Maui.MarkdownView/MarkdownParseStream.cs +++ /dev/null @@ -1,74 +0,0 @@ -using CommonMark.Syntax; - -namespace Plugin.Maui.MarkdownView; - -public class MarkdownParseStream : IDisposable -{ - private readonly TextReader _markdownSource; - - private Block _markdownDocument; - private Block _workingBlock; - - // Format\Convert c# objects into UI Components - private ViewFormatter _formatter; - - private readonly Queue _readCache = new Queue(); - - public MarkdownParseStream(IViewSupplier viewSupplier, TextReader markdownSource) - { - _markdownSource = markdownSource; - _formatter = new ViewFormatter(viewSupplier); - } - - /// - /// Read 1 element - /// - /// 1 element or default(T)/null when finished - public T Read() - { - if (_markdownDocument == null) - { - _markdownDocument = MarkdownParser.GetMarkdownDocument(_markdownSource); - - // Set first block to parse - _workingBlock = _markdownDocument.FirstChild; - } - - if (_readCache.Count != 0) - { - return _readCache.Dequeue(); - } - - if (_workingBlock == null) - { - return default(T); - } - - var uiComponents = _formatter.FormatSingleBlock(_workingBlock); - _workingBlock = _workingBlock.NextSibling; - - AddComponentsToCache(uiComponents); - - if (_readCache.Count != 0) - { - return _readCache.Dequeue(); - } - - return Read(); - } - - private void AddComponentsToCache(List components) - { - foreach (var uiComponent in components) - { - _readCache.Enqueue(uiComponent); - } - } - - public void Dispose() - { - _markdownDocument = null; - _formatter = null; - _workingBlock = null; - } -} \ No newline at end of file diff --git a/src/Plugin.Maui.MarkdownView/MarkdownParser.cs b/src/Plugin.Maui.MarkdownView/MarkdownParser.cs deleted file mode 100644 index 8449db5..0000000 --- a/src/Plugin.Maui.MarkdownView/MarkdownParser.cs +++ /dev/null @@ -1,42 +0,0 @@ -using CommonMark; -using CommonMark.Syntax; - -namespace Plugin.Maui.MarkdownView; - -public class MarkdownParser -{ - private readonly IViewSupplier _viewSupplier; - - public MarkdownParser(IViewSupplier viewSupplier) - { - _viewSupplier = viewSupplier; - } - - public List Parse(string markdownSource) - { - using (var reader = new StringReader(markdownSource)) - { - return Parse(reader); - } - } - - public List Parse(TextReader markdownSource) - { - // Parse to usable c# objects - var markdownDocument = GetMarkdownDocument(markdownSource); - - // Format\Convert c# objects into UI Components - var formatter = new ViewFormatter(_viewSupplier); - var uiComponents = formatter.Format(markdownDocument); - - return uiComponents; - } - - public static Block GetMarkdownDocument(TextReader markdownSource) - { - // Parse to usable c# objects - var settings = CommonMarkSettings.Default.Clone(); - settings.AdditionalFeatures |= CommonMarkAdditionalFeatures.PlaceholderBracket; - return CommonMarkConverter.Parse(markdownSource, settings); - } -} \ No newline at end of file diff --git a/src/Plugin.Maui.MarkdownView/MarkdownView.cs b/src/Plugin.Maui.MarkdownView/MarkdownView.cs index 7c0e771..e70faa7 100644 --- a/src/Plugin.Maui.MarkdownView/MarkdownView.cs +++ b/src/Plugin.Maui.MarkdownView/MarkdownView.cs @@ -1,3 +1,5 @@ +using MarkdownParser; + namespace Plugin.Maui.MarkdownView; [ContentProperty(nameof(MarkdownText))] diff --git a/src/Plugin.Maui.MarkdownView/MauiViewSupplier.cs b/src/Plugin.Maui.MarkdownView/MauiViewSupplier.cs index a7a63b3..f8b4dad 100644 --- a/src/Plugin.Maui.MarkdownView/MauiViewSupplier.cs +++ b/src/Plugin.Maui.MarkdownView/MauiViewSupplier.cs @@ -1,4 +1,6 @@ -namespace Plugin.Maui.MarkdownView; +using MarkdownParser; + +namespace Plugin.Maui.MarkdownView; public class MauiViewSupplier : IViewSupplier { diff --git a/src/Plugin.Maui.MarkdownView/Plugin.Maui.MarkdownView.csproj b/src/Plugin.Maui.MarkdownView/Plugin.Maui.MarkdownView.csproj index b6262b2..71fa33e 100644 --- a/src/Plugin.Maui.MarkdownView/Plugin.Maui.MarkdownView.csproj +++ b/src/Plugin.Maui.MarkdownView/Plugin.Maui.MarkdownView.csproj @@ -69,7 +69,7 @@ - + @@ -92,7 +92,7 @@ - + diff --git a/src/Plugin.Maui.MarkdownView/ViewFormatter.cs b/src/Plugin.Maui.MarkdownView/ViewFormatter.cs deleted file mode 100644 index d2c7ad3..0000000 --- a/src/Plugin.Maui.MarkdownView/ViewFormatter.cs +++ /dev/null @@ -1,165 +0,0 @@ -using CommonMark; -using CommonMark.Syntax; - -namespace Plugin.Maui.MarkdownView; - -public class ViewFormatter -{ - private readonly ViewWriter _writer; - - public ViewFormatter(IViewSupplier viewSupplier) - { - _writer = new ViewWriter(viewSupplier); - } - - public List Format(Block markdownBlock) - { - WriteBlockToView(markdownBlock, _writer); - return _writer.Flush(); - } - - public List FormatSingleBlock(Block markdownBlock) - { - WriteBlockToView(markdownBlock, _writer, false); - return _writer.Flush(); - } - - private void WriteBlockToView(Block block, ViewWriter writer, bool continueWithNextSibling = true) - { - if (block == null) - { - return; - } - - switch (block.Tag) - { - case BlockTag.Document: - WriteBlockToView(block.FirstChild, writer); - break; - case BlockTag.Paragraph: - writer.StartBlock(block.Tag); - WriteInlineToView(block.InlineContent, writer); - writer.FinalizeParagraphBlock(); - break; - case BlockTag.BlockQuote: - writer.StartBlock(BlockTag.BlockQuote); - WriteBlockToView(block.FirstChild, writer); - writer.FinalizeBlockquoteBlock(); - break; - case BlockTag.List: - writer.StartBlock(block.Tag); - WriteBlockToView(block.FirstChild, writer); - writer.FinalizeListBlock(); - break; - case BlockTag.ListItem: - writer.StartBlock(block.Tag); - WriteBlockToView(block.FirstChild, writer); - writer.FinalizeListItemBlock(block.ListData); - break; - case BlockTag.AtxHeading: - case BlockTag.SetextHeading: - writer.StartBlock(block.Tag); - WriteInlineToView(block.InlineContent, writer); // needs to be tested - WriteBlockToView(block.FirstChild, writer); - writer.FinalizeHeaderBlock(block.Heading.Level); - break; - case BlockTag.ThematicBreak: - writer.StartAndFinalizeThematicBreak(); - break; - case BlockTag.FencedCode: - case BlockTag.IndentedCode: - // not supported - break; - case BlockTag.HtmlBlock: - // //Its beter to not support Raw HTML - //writer.StartBlock(BlockTag.Paragraph, block.StringContent.ToString()); - //WriteBlockToView(block.FirstChild, writer); - //writer.FinalizeParagraphBlock(); - break; - case BlockTag.ReferenceDefinition: - // not supported - break; - default: - throw new CommonMarkException("Block type " + block.Tag + " is not supported.", block); - } - - if (continueWithNextSibling && block.NextSibling != null) - { - WriteBlockToView(block.NextSibling, writer); - } - } - - private void WriteInlineToView(Inline inline, ViewWriter writer) - { - if (inline == null) - { - return; - } - - switch (inline.Tag) - { - case InlineTag.String: - case InlineTag.Code: - case InlineTag.RawHtml: - writer.AddText(inline.LiteralContent); - break; - case InlineTag.Link: - // TODO; maybe - WriteInlineToView(inline.FirstChild, writer); - break; - case InlineTag.Image: - writer.StartAndFinalizeImageBlock(inline.TargetUrl, inline.LiteralContent, inline.FirstChild?.LiteralContent); - break; - case InlineTag.SoftBreak: - case InlineTag.LineBreak: - writer.AddText(Environment.NewLine); - break; - case InlineTag.Placeholder: - writer.StartAndFinalizePlaceholderBlock(inline.TargetUrl); - break; - case InlineTag.Strikethrough: - case InlineTag.Emphasis: - case InlineTag.Strong: - WriteInlineToView(inline.FirstChild, writer); - break; - - default: - throw new ArgumentOutOfRangeException(); - } - - if (inline.NextSibling != null) - { - WriteInlineToView(inline.NextSibling, writer); - } - } - - // Helper for a bug in CommonMark nested listing - private List TrimNestedListBlocks(Block listBlock) - { - var listBlockChildTree = listBlock.AsEnumerable(); - - var cleanedChildTree = new List(); - var captureEnabled = true; - foreach (var child in listBlockChildTree) - { - if (child.Block == null) - { - continue; - } - - if (child.Block != listBlock - && child.Block.Tag == BlockTag.List) - { - captureEnabled = child.IsClosing; - } - - if (captureEnabled - && child.IsOpening) - { - cleanedChildTree.Add(child.Block); - } - } - - return cleanedChildTree; - } -} \ No newline at end of file diff --git a/src/Plugin.Maui.MarkdownView/ViewWriter.cs b/src/Plugin.Maui.MarkdownView/ViewWriter.cs deleted file mode 100644 index 745de2e..0000000 --- a/src/Plugin.Maui.MarkdownView/ViewWriter.cs +++ /dev/null @@ -1,227 +0,0 @@ -using System.Diagnostics; -using CommonMark.Syntax; - -namespace Plugin.Maui.MarkdownView; - -public class ViewWriter -{ - private IViewSupplier ViewSupplier { get; } - private List WrittenViews { get; set; } = new List(); - - private Stack> Workbench { get; } = new Stack>(); - private ViewWriterCache GetWorkbenchItem() - { - if (Workbench.Count == 0) - { - return null; - } - - return Workbench.Peek(); - } - - public ViewWriter(IViewSupplier viewSupplier) - { - ViewSupplier = viewSupplier; - } - - public List Flush() - { - var collectedViews = WrittenViews; - WrittenViews = new List(); - - return collectedViews; - } - - public void StartBlock(BlockTag blockType, string content = "") - { - Workbench.Push(new ViewWriterCache { ComponentType = blockType }); - GetWorkbenchItem().Add(content); - } - - public void FinalizeParagraphBlock() - { - var wbi = GetWorkbenchItem(); - if (wbi.ComponentType != BlockTag.Paragraph) - { - Debug.WriteLine($"Finalizing Paragraph can not finalize {wbi.ComponentType}"); - return; - } - - var views = new List(); - - var topWorkbenchItem = Workbench.Pop(); - var itemsCache = topWorkbenchItem.GetGroupedCachedValues(); - - foreach (var itemsCacheTuple in itemsCache) - { - var view = !string.IsNullOrEmpty(itemsCacheTuple.Item1) ? - ViewSupplier.GetTextView(itemsCacheTuple.Item1) : itemsCacheTuple.Item2; - - if (view != null) - { - views.Add(view); - } - } - - StoreView(StackViews(views)); - } - - public void FinalizeBlockquoteBlock() - { - var wbi = GetWorkbenchItem(); - if (wbi.ComponentType != BlockTag.BlockQuote) - { - Debug.WriteLine($"Finalizing BlockQuote can not finalize {wbi.ComponentType}"); - return; - } - - var topWorkbenchItem = Workbench.Pop(); - var itemsCache = topWorkbenchItem.GetGroupedCachedValues(); - - var childViews = itemsCache.Select(itemsCacheTuple => itemsCacheTuple.Item2).ToList(); - var childView = StackViews(childViews); - - var blockView = ViewSupplier.GetBlockquotesView(childView); - - StoreView(blockView); - } - - 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; - } - - var views = new List(); - - var topWorkbenchItem = Workbench.Pop(); - var itemsCache = topWorkbenchItem.GetGroupedCachedValues(); - - foreach (var itemsCacheTuple in itemsCache) - { - var view = !string.IsNullOrEmpty(itemsCacheTuple.Item1) ? - ViewSupplier.GetHeaderView(itemsCacheTuple.Item1, headerLevel) : itemsCacheTuple.Item2; - - views.Add(view); - } - - StoreView(StackViews(views)); - } - - public void FinalizeListBlock() - { - var wbi = GetWorkbenchItem(); - if (wbi.ComponentType != BlockTag.List) - { - Debug.WriteLine($"Finalizing List can not finalize {wbi.ComponentType}"); - return; - } - - var topWorkbenchItem = Workbench.Pop(); - var itemsCache = topWorkbenchItem.GetGroupedCachedValues(); - - var listItems = itemsCache.Select(itemsCacheTuple => itemsCacheTuple.Item2).ToList(); - var listView = ViewSupplier.GetListView(listItems); - - StoreView(listView); - } - - public void FinalizeListItemBlock(ListData listData) - { - var wbi = GetWorkbenchItem(); - if (wbi.ComponentType != BlockTag.ListItem) - { - Debug.WriteLine($"Finalizing ListItem can not finalize {wbi.ComponentType}"); - return; - } - - var views = new List(); - - var isOrderedList = listData.ListType == ListType.Ordered; - var sequenceNumber = listData.Start; - var depthLevel = Workbench.Count(wbItem => wbItem.ComponentType == BlockTag.List); - - var topWorkbenchItem = Workbench.Pop(); - var itemsCache = topWorkbenchItem.GetGroupedCachedValues(); - - - foreach (var itemsCacheTuple in itemsCache) - { - var view = !string.IsNullOrEmpty(itemsCacheTuple.Item1) ? - ViewSupplier.GetTextView(itemsCacheTuple.Item1) : itemsCacheTuple.Item2; - - if (view != null) - { - views.Add(view); - } - } - - var flattendView = StackViews(views); - - var listItemView = ViewSupplier.GetListItemView(flattendView, isOrderedList, sequenceNumber, depthLevel); - - StoreView(listItemView); - } - - public void AddText(string content) - { - GetWorkbenchItem().Add(content); - } - - public void StartAndFinalizeImageBlock(string targetUrl, string subscription, string imageId) - { - var imageView = ViewSupplier.GetImageView(targetUrl, subscription, imageId); - StoreView(imageView); - } - - public void StartAndFinalizeThematicBreak() - { - var seperator = ViewSupplier.GetThematicBreak(); - StoreView(seperator); - } - - public void StartAndFinalizePlaceholderBlock(string placeholderName) - { - var placeholderView = ViewSupplier.GetPlaceholder(placeholderName); - StoreView(placeholderView); - } - - private T StackViews(List views) - { - 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); - - return viewToStore; - } - - private void StoreView(T view) - { - if (view == null) - { - return; - } - - // Check if Workbench has an item where its working on - var wbi = GetWorkbenchItem(); - if (wbi != null) // add the new View to the WorkbenchItem - { - wbi.Add(view); - } - else // otherwise add the new View to finalized views collection - { - WrittenViews.Add(view); - } - } - -} \ No newline at end of file diff --git a/src/Plugin.Maui.MarkdownView/ViewWriterCache.cs b/src/Plugin.Maui.MarkdownView/ViewWriterCache.cs deleted file mode 100644 index fa2a247..0000000 --- a/src/Plugin.Maui.MarkdownView/ViewWriterCache.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Text; -using CommonMark.Syntax; - -namespace Plugin.Maui.MarkdownView; - -public class ViewWriterCache -{ - public BlockTag? ComponentType { get; set; } - - private readonly Stack> _valuesStack = new Stack>(); - private readonly T _defaultT = default(T); - - public void Add(T item) - { - if (EqualityComparer.Default.Equals(item, _defaultT)) - { - return; - } - - _valuesStack.Push(new Tuple(null, item)); - } - - public void Add(string item) - { - if (string.IsNullOrEmpty(item)) - { - return; - } - - _valuesStack.Push(new Tuple(item, _defaultT)); - } - - /// - /// Get cached items in order, each Tuple has a text or T value (never both in the same Tuple) - /// - /// collection that contain a string of T (never both in the same Tuple) - public List> GetGroupedCachedValues() - { - var groupedCache = new List>(); - - var workbenchItemTextCacheBuilder = new StringBuilder(); - var values = _valuesStack.Reverse().ToArray(); - - foreach (var value in values) - { - // Check for text - if (!string.IsNullOrEmpty(value.Item1)) - { - workbenchItemTextCacheBuilder.Append(value.Item1); - continue; - } - - // No text anymore: Store workbenchItemTextCache if any to groupedCache - if (workbenchItemTextCacheBuilder.Length != 0) - { - groupedCache.Add(new Tuple(workbenchItemTextCacheBuilder.ToString(), _defaultT)); - workbenchItemTextCacheBuilder.Clear(); - } - - // If item2 is not null - if (!EqualityComparer.Default.Equals(value.Item2, _defaultT)) - { - groupedCache.Add(new Tuple(null, value.Item2)); - } - } - - // Store leftovers workbenchItemTextCache - if (workbenchItemTextCacheBuilder.Length != 0) - { - groupedCache.Add(new Tuple(workbenchItemTextCacheBuilder.ToString(), _defaultT)); - workbenchItemTextCacheBuilder.Clear(); - } - - return groupedCache; - } -} \ No newline at end of file