From 6f18a19ad58a97411df4be23dbca9d5134689d83 Mon Sep 17 00:00:00 2001 From: Frans Bouma Date: Tue, 16 Feb 2016 17:44:09 +0100 Subject: [PATCH] Changes Ported @Tabs support over. --- src/MarkdownDeep/Block.cs | 59 ++++++++- src/MarkdownDeep/BlockProcessor.cs | 117 +++++++++++++++--- src/MarkdownDeep/MardownDeep.cs | 13 +- src/MarkdownDeep/StringScanner.cs | 28 +++++ .../MarkdownDeepTests.csproj | 6 + .../docnetmode/Alert(DocNetMode).html | 4 + .../docnetmode/Alert(DocNetMode).txt | 4 + .../docnetmode/Tabs(DocNetMode).html | 14 +++ .../testfiles/docnetmode/Tabs(DocNetMode).txt | 15 +++ 9 files changed, 239 insertions(+), 21 deletions(-) create mode 100644 src/MarkdownDeepTests/testfiles/docnetmode/Tabs(DocNetMode).html create mode 100644 src/MarkdownDeepTests/testfiles/docnetmode/Tabs(DocNetMode).txt diff --git a/src/MarkdownDeep/Block.cs b/src/MarkdownDeep/Block.cs index b09b93d..7b500ff 100644 --- a/src/MarkdownDeep/Block.cs +++ b/src/MarkdownDeep/Block.cs @@ -59,6 +59,8 @@ namespace MarkdownDeep // DocNet extensions font_awesome, // Data is icon name alert, // Data is the alert type specified. + tab, // Data is the tab header text. + tabs, // children are the tabs. } class Block @@ -385,7 +387,12 @@ namespace MarkdownDeep RenderAlert(m, b); } break; - + case BlockType.tabs: + if(m.DocNetMode) + { + RenderTabs(m, b); + } + break; // End DocNet Extensions default: b.Append("<" + BlockType.ToString() + ">"); @@ -395,7 +402,6 @@ namespace MarkdownDeep } } - internal void RenderPlain(Markdown m, StringBuilder b) { switch (BlockType) @@ -513,13 +519,60 @@ namespace MarkdownDeep b.Append(alertType); b.Append("\">"); + b.Append("\"> "); b.Append(title); b.Append(""); RenderChildren(m, b); b.Append(""); } + + private void RenderTabs(Markdown m, StringBuilder b) + { + var tabId = m.GetNewTabId(); + var headerSB = new StringBuilder(); + var contentSB = new StringBuilder(); + var tabCounter = 0; + var checkedAttribute = " checked"; + foreach(var tabBlock in this.Children) + { + if(tabBlock.BlockType != BlockType.tab) + { + return; + } + var tabHeaderText = tabBlock.Data as string ?? "Tab"; + // header + string tabIdSuffix = tabCounter + "_" + tabId; + headerSB.Append(""); + + // content + var tabContentSB = new StringBuilder(); + tabBlock.RenderChildren(m, tabContentSB); + contentSB.Append("
"); + contentSB.Append(tabContentSB.ToString()); + contentSB.Append("
"); + + // done + checkedAttribute = string.Empty; + tabCounter++; + } + b.Append("
"); + b.Append(headerSB.ToString()); + b.Append(contentSB.ToString()); + b.Append("
"); + } + + #region Properties public int ContentEnd { diff --git a/src/MarkdownDeep/BlockProcessor.cs b/src/MarkdownDeep/BlockProcessor.cs index 2396382..6fb3996 100644 --- a/src/MarkdownDeep/BlockProcessor.cs +++ b/src/MarkdownDeep/BlockProcessor.cs @@ -1268,10 +1268,15 @@ namespace MarkdownDeep { return HandleFontAwesomeExtension(b); } + // first match @tabs, and then @tab, as both are handled by this processor. if(DoesMatch("@tabs")) { return HandleTabsExtension(b); } + if(DoesMatch("@tab")) + { + return HandleTabForTabsExtension(b); + } if(DoesMatch("@alert")) { return HandleAlertExtension(b); @@ -1280,6 +1285,7 @@ namespace MarkdownDeep } + /// /// Handles the alert extension: /// @alert type @@ -1327,21 +1333,7 @@ namespace MarkdownDeep } // Remove the trailing line end - if(Input[endContent - 1] == '\r' && Input[endContent - 2] == '\n') - { - endContent -= 2; - } - else - { - if(Input[endContent - 1] == '\n' && Input[endContent - 2] == '\r') - { - endContent -= 2; - } - else - { - endContent--; - } - } + endContent = UnskipCRLFBeforePos(endContent); b.BlockType = BlockType.alert; b.Data = alertType; // scan the content, as it can contain markdown statements. @@ -1350,10 +1342,101 @@ namespace MarkdownDeep return true; } + private bool HandleTabsExtension(Block b) { -#warning IMPLEMENT - return false; + // skip '@tabs' + if(!SkipString("@tabs")) + { + return false; + } + // ignore what's specified behind @tabs + SkipToNextLine(); + int startContent = this.Position; + // find @end. + if(!Find("@endtabs")) + { + return false; + } + // Character before must be a eol char + if(!IsLineEnd(CharAtOffset(-1))) + { + return false; + } + int endContent = Position; + // skip @end + SkipString("@endtabs"); + SkipLinespace(); + if(!Eol) + { + return false; + } + // Remove the trailing line end + endContent = UnskipCRLFBeforePos(endContent); + b.BlockType = BlockType.tabs; + // scan the content, as it can contain markdown statements. + var contentProcessor = new BlockProcessor(m_markdown, m_markdown.MarkdownInHtml); + var scanLines = contentProcessor.ScanLines(this.Input, startContent, endContent - startContent); + // check whether the content is solely tab blocks. If not, we ignore this tabs specification. + if(scanLines.Any(x=>x.BlockType != BlockType.tab)) + { + return false; + } + b.Children = scanLines; + return true; + } + + + /// + /// Handles the tab for tabs extension. This is a docnet extension and it handles: + /// @tab tab head text + /// tab content + /// @end + /// + /// The current block. + /// + private bool HandleTabForTabsExtension(Block b) + { + // skip '@tab' + if(!SkipString("@tab")) + { + return false; + } + SkipLinespace(); + var tabHeaderTextStart = this.Position; + // skip to eol, then grab the content between positions. + SkipToEol(); + var tabHeaderText = this.Input.Substring(tabHeaderTextStart, this.Position - tabHeaderTextStart); + SkipToNextLine(); + int startContent = this.Position; + + // find @end. + if(!Find("@end")) + { + return false; + } + // Character before must be a eol char + if(!IsLineEnd(CharAtOffset(-1))) + { + return false; + } + int endContent = Position; + // skip @end + SkipString("@end"); + SkipLinespace(); + if(!Eol) + { + return false; + } + + // Remove the trailing line end + endContent = UnskipCRLFBeforePos(endContent); + b.BlockType = BlockType.tab; + b.Data = tabHeaderText; + // scan the content, as it can contain markdown statements. + var contentProcessor = new BlockProcessor(m_markdown, m_markdown.MarkdownInHtml); + b.Children = contentProcessor.ScanLines(Input, startContent, endContent - startContent); + return true; } diff --git a/src/MarkdownDeep/MardownDeep.cs b/src/MarkdownDeep/MardownDeep.cs index 6b1c585..abea7c9 100644 --- a/src/MarkdownDeep/MardownDeep.cs +++ b/src/MarkdownDeep/MardownDeep.cs @@ -42,6 +42,7 @@ namespace MarkdownDeep private Dictionary m_UsedHeaderIDs; private Dictionary m_AbbreviationMap; private List m_AbbreviationList; + private int _tabIdCounter; #endregion // Constructor @@ -57,6 +58,7 @@ namespace MarkdownDeep m_UsedFootnotes = new List(); m_UsedHeaderIDs = new Dictionary(); this.CreatedH2IdCollector = new List>(); + _tabIdCounter = 0; } internal List ProcessBlocks(string str) @@ -551,6 +553,16 @@ namespace MarkdownDeep return sb.ToString(); } + /// + /// Gets the new tab identifier, which is a unique number within the markdown parsing pass. + /// + /// + internal int GetNewTabId() + { + _tabIdCounter++; + return _tabIdCounter; + } + // Add a link definition internal void AddLinkDefinition(LinkDefinition link) { @@ -1019,7 +1031,6 @@ namespace MarkdownDeep get; set; } - #endregion } diff --git a/src/MarkdownDeep/StringScanner.cs b/src/MarkdownDeep/StringScanner.cs index ceaebee..0548cc3 100644 --- a/src/MarkdownDeep/StringScanner.cs +++ b/src/MarkdownDeep/StringScanner.cs @@ -537,5 +537,33 @@ namespace MarkdownDeep int pos; int end; int mark; + + + /// + /// Unskips the CRLF before position. This simply means it returns the new position calculated from the specified position if before the specified + /// position a CRLF is present. + /// + /// The position. + /// position minus 0, 1 or 2 depending on whether a CRLF is present right before position: if so, it returns position - the length of the CRLF + /// which can be 1 or 2 depending on the fact whether it's \r or \n or both. + protected int UnskipCRLFBeforePos(int position) + { + if(this.Input[position - 1] == '\r' && this.Input[position - 2] == '\n') + { + position -= 2; + } + else + { + if(this.Input[position - 1] == '\n' && this.Input[position - 2] == '\r') + { + position -= 2; + } + else + { + position--; + } + } + return position; + } } } diff --git a/src/MarkdownDeepTests/MarkdownDeepTests.csproj b/src/MarkdownDeepTests/MarkdownDeepTests.csproj index 1babcd2..bce03b3 100644 --- a/src/MarkdownDeepTests/MarkdownDeepTests.csproj +++ b/src/MarkdownDeepTests/MarkdownDeepTests.csproj @@ -467,6 +467,12 @@ + + + + + +