diff --git a/src/DocNet/Utils.cs b/src/DocNet/Utils.cs index cda6373..c3e9428 100644 --- a/src/DocNet/Utils.cs +++ b/src/DocNet/Utils.cs @@ -45,7 +45,8 @@ namespace Docnet ExtraMode = true, GitHubCodeBlocks = true, AutoHeadingIDs = true, - NewWindowForExternalLinks = true + NewWindowForExternalLinks = true, + DocNetMode = true, }; #warning SET DocumentRoot and DocumentLocation for image features. diff --git a/src/MarkdownDeep/Block.cs b/src/MarkdownDeep/Block.cs index 7eb3742..b09b93d 100644 --- a/src/MarkdownDeep/Block.cs +++ b/src/MarkdownDeep/Block.cs @@ -44,7 +44,7 @@ namespace MarkdownDeep unsafe_html, // unsafe html that should be encoded span, // an undecorated span of text (used for simple list items // where content is not wrapped in paragraph tags - codeblock, // a code block (render only). If in github mode, `Data` contains the language name specified after the leading ```. + codeblock, // a code block (render only). If in githubcodeblocks mode, `Data` contains the language name specified after the leading ```. li, // a list item (render only) ol, // ordered list (render only) ul, // unordered list (render only) @@ -56,6 +56,9 @@ namespace MarkdownDeep dl, // render only footnote, // footnote definition eg: [^id] `data` holds the footnote id p_footnote, // paragraph with footnote return link append. Return link string is in `data`. + // DocNet extensions + font_awesome, // Data is icon name + alert, // Data is the alert type specified. } class Block @@ -67,18 +70,18 @@ namespace MarkdownDeep internal Block(BlockType type) { - blockType = type; + BlockType = type; } public string Content { get { - switch (blockType) + switch (BlockType) { case BlockType.codeblock: StringBuilder s = new StringBuilder(); - foreach (var line in children) + foreach (var line in Children) { s.Append(line.Content); s.Append('\n'); @@ -87,24 +90,27 @@ namespace MarkdownDeep } - if (buf==null) + if (Buf==null) return null; else - return contentStart == -1 ? buf : buf.Substring(contentStart, contentLen); + return ContentStart == -1 ? Buf : Buf.Substring(ContentStart, ContentLen); } } - public int LineStart + /// + /// Gets the absolute line start: if line start is 0, it's returning the content start. + /// + public int LineStartAbsolute { get { - return lineStart == 0 ? contentStart : lineStart; + return LineStart == 0 ? ContentStart : LineStart; } } internal void RenderChildren(Markdown m, StringBuilder b) { - foreach (var block in children) + foreach (var block in Children) { block.Render(m, b); } @@ -112,7 +118,7 @@ namespace MarkdownDeep internal void RenderChildrenPlain(Markdown m, StringBuilder b) { - foreach (var block in children) + foreach (var block in Children) { block.RenderPlain(m, b); } @@ -121,39 +127,39 @@ namespace MarkdownDeep internal string ResolveHeaderID(Markdown m) { // Already resolved? - if (this.data!=null && this.data is string) - return (string)this.data; + if (this.Data!=null && this.Data is string) + return (string)this.Data; // Approach 1 - PHP Markdown Extra style header id - int end=contentEnd; - string id = Utils.StripHtmlID(buf, contentStart, ref end); + int end=ContentEnd; + string id = Utils.StripHtmlID(Buf, ContentStart, ref end); if (id != null) { - contentEnd = end; + ContentEnd = end; } else { // Approach 2 - pandoc style header id - id = m.MakeUniqueHeaderID(buf, contentStart, contentLen); + id = m.MakeUniqueHeaderID(Buf, ContentStart, ContentLen); } - this.data = id; + this.Data = id; return id; } internal void Render(Markdown m, StringBuilder b) { - switch (blockType) + switch (BlockType) { case BlockType.Blank: return; case BlockType.p: - m.SpanFormatter.FormatParagraph(b, buf, contentStart, contentLen); + m.SpanFormatter.FormatParagraph(b, Buf, ContentStart, ContentLen); break; case BlockType.span: - m.SpanFormatter.Format(b, buf, contentStart, contentLen); + m.SpanFormatter.Format(b, Buf, ContentStart, ContentLen); b.Append("\n"); break; @@ -166,7 +172,7 @@ namespace MarkdownDeep string id = string.Empty; if (m.ExtraMode && !m.SafeMode) { - b.Append("<" + blockType.ToString()); + b.Append("<" + BlockType.ToString()); id = ResolveHeaderID(m); if (!String.IsNullOrEmpty(id)) { @@ -181,53 +187,53 @@ namespace MarkdownDeep } else { - b.Append("<" + blockType.ToString() + ">"); + b.Append("<" + BlockType.ToString() + ">"); } - if(blockType == BlockType.h2 && !string.IsNullOrWhiteSpace(id)) + if(m.DocNetMode && BlockType == BlockType.h2 && !string.IsNullOrWhiteSpace(id)) { // collect h2 id + text in collector var h2ContentSb = new StringBuilder(); - m.SpanFormatter.Format(h2ContentSb, buf, contentStart, contentLen); + m.SpanFormatter.Format(h2ContentSb, Buf, ContentStart, ContentLen); var h2ContentAsString = h2ContentSb.ToString(); b.Append(h2ContentAsString); m.CreatedH2IdCollector.Add(new Tuple(id, h2ContentAsString)); } else { - m.SpanFormatter.Format(b, buf, contentStart, contentLen); + m.SpanFormatter.Format(b, Buf, ContentStart, ContentLen); } - b.Append("\n"); + b.Append("\n"); break; case BlockType.hr: b.Append("
\n"); return; - + case BlockType.user_break: return; case BlockType.ol_li: case BlockType.ul_li: b.Append("
  • "); - m.SpanFormatter.Format(b, buf, contentStart, contentLen); + m.SpanFormatter.Format(b, Buf, ContentStart, ContentLen); b.Append("
  • \n"); break; case BlockType.dd: b.Append("
    "); - if (children != null) + if (Children != null) { b.Append("\n"); RenderChildren(m, b); } else - m.SpanFormatter.Format(b, buf, contentStart, contentLen); + m.SpanFormatter.Format(b, Buf, ContentStart, ContentLen); b.Append("
    \n"); break; case BlockType.dt: { - if (children == null) + if (Children == null) { foreach (var l in Content.Split('\n')) { @@ -252,38 +258,48 @@ namespace MarkdownDeep return; case BlockType.html: - b.Append(buf, contentStart, contentLen); + b.Append(Buf, ContentStart, ContentLen); return; case BlockType.unsafe_html: - m.HtmlEncode(b, buf, contentStart, contentLen); + m.HtmlEncode(b, Buf, ContentStart, ContentLen); return; case BlockType.codeblock: if(m.FormatCodeBlockFunc == null) { - var dataArgument = this.data as string ?? string.Empty; + var dataArgument = this.Data as string ?? string.Empty; + string tagSuffix = "\n\n"; + if(m.GitHubCodeBlocks && !string.IsNullOrWhiteSpace(dataArgument)) { - b.AppendFormat("
    ", dataArgument);
    +							if(dataArgument == "nohighlight")
    +							{
    +								b.Append("
    ");
    +								tagSuffix = "
    "; + } + else + { + b.AppendFormat("
    ", dataArgument);
    +							}
     						}
     						else
     						{
     							b.Append("
    ");
     						}
    -						foreach(var line in children)
    +						foreach(var line in Children)
     						{
    -							m.HtmlEncodeAndConvertTabsToSpaces(b, line.buf, line.contentStart, line.contentLen);
    +							m.HtmlEncodeAndConvertTabsToSpaces(b, line.Buf, line.ContentStart, line.ContentLen);
     							b.Append("\n");
     						}
    -						b.Append("
    \n\n"); + b.Append(tagSuffix); } else { var sb = new StringBuilder(); - foreach(var line in children) + foreach(var line in Children) { - m.HtmlEncodeAndConvertTabsToSpaces(sb, line.buf, line.contentStart, line.contentLen); + m.HtmlEncodeAndConvertTabsToSpaces(sb, line.Buf, line.ContentStart, line.ContentLen); sb.Append("\n"); } b.Append(m.FormatCodeBlockFunc(m, sb.ToString())); @@ -315,7 +331,7 @@ namespace MarkdownDeep return; case BlockType.HtmlTag: - var tag = (HtmlTag)data; + var tag = (HtmlTag)Data; // Prepare special tags var name=tag.name.ToLowerInvariant(); @@ -341,38 +357,55 @@ namespace MarkdownDeep return; case BlockType.table_spec: - ((TableSpec)data).Render(m, b); + ((TableSpec)Data).Render(m, b); break; case BlockType.p_footnote: b.Append("

    "); - if (contentLen > 0) + if (ContentLen > 0) { - m.SpanFormatter.Format(b, buf, contentStart, contentLen); + m.SpanFormatter.Format(b, Buf, ContentStart, ContentLen); b.Append(" "); } - b.Append((string)data); + b.Append((string)Data); b.Append("

    \n"); break; +// DocNet Extensions + case BlockType.font_awesome: + if(m.DocNetMode) + { + b.Append(""); + } + break; + case BlockType.alert: + if(m.DocNetMode) + { + RenderAlert(m, b); + } + break; +// End DocNet Extensions default: - b.Append("<" + blockType.ToString() + ">"); - m.SpanFormatter.Format(b, buf, contentStart, contentLen); - b.Append("\n"); + b.Append("<" + BlockType.ToString() + ">"); + m.SpanFormatter.Format(b, Buf, ContentStart, ContentLen); + b.Append("\n"); break; } } + internal void RenderPlain(Markdown m, StringBuilder b) { - switch (blockType) + switch (BlockType) { case BlockType.Blank: return; case BlockType.p: case BlockType.span: - m.SpanFormatter.FormatPlain(b, buf, contentStart, contentLen); + m.SpanFormatter.FormatPlain(b, Buf, ContentStart, ContentLen); b.Append(" "); break; @@ -382,7 +415,7 @@ namespace MarkdownDeep case BlockType.h4: case BlockType.h5: case BlockType.h6: - m.SpanFormatter.FormatPlain(b, buf, contentStart, contentLen); + m.SpanFormatter.FormatPlain(b, Buf, ContentStart, ContentLen); b.Append(" - "); break; @@ -390,23 +423,23 @@ namespace MarkdownDeep case BlockType.ol_li: case BlockType.ul_li: b.Append("* "); - m.SpanFormatter.FormatPlain(b, buf, contentStart, contentLen); + m.SpanFormatter.FormatPlain(b, Buf, ContentStart, ContentLen); b.Append(" "); break; case BlockType.dd: - if (children != null) + if (Children != null) { b.Append("\n"); RenderChildrenPlain(m, b); } else - m.SpanFormatter.FormatPlain(b, buf, contentStart, contentLen); + m.SpanFormatter.FormatPlain(b, Buf, ContentStart, ContentLen); break; case BlockType.dt: { - if (children == null) + if (Children == null) { foreach (var l in Content.Split('\n')) { @@ -426,9 +459,9 @@ namespace MarkdownDeep return; case BlockType.codeblock: - foreach (var line in children) + foreach (var line in Children) { - b.Append(line.buf, line.contentStart, line.contentLen); + b.Append(line.Buf, line.ContentStart, line.ContentLen); b.Append(" "); } return; @@ -445,34 +478,72 @@ namespace MarkdownDeep public void RevertToPlain() { - blockType = BlockType.p; - contentStart = lineStart; - contentLen = lineLen; + BlockType = BlockType.p; + ContentStart = LineStart; + ContentLen = LineLen; } - public int contentEnd + + private void RenderAlert(Markdown m, StringBuilder b) + { + var alertType = this.Data as string; + if(string.IsNullOrWhiteSpace(alertType)) + { + alertType = "info"; + } + string title = string.Empty; + string faIconName = string.Empty; + switch(alertType) + { + case "danger": + title = "Danger!"; + faIconName = "times-circle"; + break; + case "warning": + title = "Warning!"; + faIconName = "warning"; + break; + case "neutral": + case "info": + title = "Info"; + faIconName = "info-circle"; + break; + } + b.Append("
    "); + b.Append(title); + b.Append(""); + RenderChildren(m, b); + b.Append("
    "); + } + + #region Properties + public int ContentEnd { get { - return contentStart + contentLen; + return ContentStart + ContentLen; } set { - contentLen = value - contentStart; + ContentLen = value - ContentStart; } } // Count the leading spaces on a block // Used by list item evaluation to determine indent levels // irrespective of indent line type. - public int leadingSpaces + public int LeadingSpaces { get { int count = 0; - for (int i = lineStart; i < lineStart + lineLen; i++) + for (int i = LineStart; i < LineStart + LineLen; i++) { - if (buf[i] == ' ') + if (Buf[i] == ' ') { count++; } @@ -488,27 +559,30 @@ namespace MarkdownDeep public override string ToString() { string c = Content; - return blockType.ToString() + " - " + (c==null ? "" : c); + return BlockType.ToString() + " - " + (c==null ? "" : c); } public Block CopyFrom(Block other) { - blockType = other.blockType; - buf = other.buf; - contentStart = other.contentStart; - contentLen = other.contentLen; - lineStart = other.lineStart; - lineLen = other.lineLen; + BlockType = other.BlockType; + Buf = other.Buf; + ContentStart = other.ContentStart; + ContentLen = other.ContentLen; + LineStart = other.LineStart; + LineLen = other.LineLen; return this; } - internal BlockType blockType; - internal string buf; - internal int contentStart; - internal int contentLen; - internal int lineStart; - internal int lineLen; - internal object data; // content depends on block type - internal List children; + + internal BlockType BlockType { get; set; } + internal string Buf { get; set; } + internal int ContentStart { get; set; } + internal int ContentLen { get; set; } + internal int LineStart { get; set; } + internal int LineLen { get; set; } + internal object Data { get; set; } // content depends on block type + internal List Children { get; set; } + + #endregion } } diff --git a/src/MarkdownDeep/BlockProcessor.cs b/src/MarkdownDeep/BlockProcessor.cs index 0980d1b..2396382 100644 --- a/src/MarkdownDeep/BlockProcessor.cs +++ b/src/MarkdownDeep/BlockProcessor.cs @@ -21,6 +21,19 @@ namespace MarkdownDeep { public class BlockProcessor : StringScanner { + #region Enums + + internal enum MarkdownInHtmlMode + { + NA, // No markdown attribute on the tag + Block, // markdown=1 or markdown=block + Span, // markdown=1 or markdown=span + Deep, // markdown=deep - recursive block mode + Off, // Markdown="something else" + } + + #endregion + public BlockProcessor(Markdown m, bool MarkdownInHtml) { m_markdown = m; @@ -62,19 +75,19 @@ namespace MarkdownDeep // Rewind, parse the header row then fast forward back to current pos if (lines.Count == 1) { - int savepos = position; - position = lines[0].lineStart; + int savepos = Position; + Position = lines[0].LineStart; spec.Headers = spec.ParseRow(this); if (spec.Headers == null) return false; - position = savepos; + Position = savepos; lines.Clear(); } // Parse all rows while (true) { - int savepos = position; + int savepos = Position; var row=spec.ParseRow(this); if (row!=null) @@ -83,7 +96,7 @@ namespace MarkdownDeep continue; } - position = savepos; + Position = savepos; break; } @@ -101,25 +114,25 @@ namespace MarkdownDeep // Add all blocks BlockType PrevBlockType = BlockType.unsafe_html; - while (!eof) + while (!Eof) { // Remember if the previous line was blank bool bPreviousBlank = PrevBlockType == BlockType.Blank; // Get the next block var b = EvaluateLine(); - PrevBlockType = b.blockType; + PrevBlockType = b.BlockType; // For dd blocks, we need to know if it was preceeded by a blank line // so store that fact as the block's data. - if (b.blockType == BlockType.dd) + if (b.BlockType == BlockType.dd) { - b.data = bPreviousBlank; + b.Data = bPreviousBlank; } // SetExt header? - if (b.blockType == BlockType.post_h1 || b.blockType == BlockType.post_h2) + if (b.BlockType == BlockType.post_h1 || b.BlockType == BlockType.post_h2) { if (lines.Count > 0) { @@ -128,11 +141,11 @@ namespace MarkdownDeep CollapseLines(blocks, lines); // If previous line was blank, - if (prevline.blockType != BlockType.Blank) + if (prevline.BlockType != BlockType.Blank) { // Convert the previous line to a heading and add to block list prevline.RevertToPlain(); - prevline.blockType = b.blockType == BlockType.post_h1 ? BlockType.h1 : BlockType.h2; + prevline.BlockType = b.BlockType == BlockType.post_h1 ? BlockType.h1 : BlockType.h2; blocks.Add(prevline); continue; } @@ -140,7 +153,7 @@ namespace MarkdownDeep // Couldn't apply setext header to a previous line - if (b.blockType == BlockType.post_h1) + if (b.BlockType == BlockType.post_h1) { // `===` gets converted to normal paragraph b.RevertToPlain(); @@ -149,9 +162,9 @@ namespace MarkdownDeep else { // `---` gets converted to hr - if (b.contentLen >= 3) + if (b.ContentLen >= 3) { - b.blockType = BlockType.hr; + b.BlockType = BlockType.hr; blocks.Add(b); } else @@ -166,20 +179,20 @@ namespace MarkdownDeep // Work out the current paragraph type - BlockType currentBlockType = lines.Count > 0 ? lines[0].blockType : BlockType.Blank; + BlockType currentBlockType = lines.Count > 0 ? lines[0].BlockType : BlockType.Blank; // Starting a table? - if (b.blockType == BlockType.table_spec) + if (b.BlockType == BlockType.table_spec) { // Get the table spec, save position - TableSpec spec = (TableSpec)b.data; - int savepos = position; + TableSpec spec = (TableSpec)b.Data; + int savepos = Position; if (!StartTable(spec, lines)) { // Not a table, revert the tablespec row to plain, // fast forward back to where we were up to and continue // on as if nothing happened - position = savepos; + Position = savepos; b.RevertToPlain(); } else @@ -190,7 +203,7 @@ namespace MarkdownDeep } // Process this line - switch (b.blockType) + switch (b.BlockType) { case BlockType.Blank: switch (currentBlockType) @@ -233,7 +246,7 @@ namespace MarkdownDeep case BlockType.dd: case BlockType.footnote: var prevline = lines.Last(); - if (prevline.blockType == BlockType.Blank) + if (prevline.BlockType == BlockType.Blank) { CollapseLines(blocks, lines); lines.Add(b); @@ -266,7 +279,7 @@ namespace MarkdownDeep case BlockType.p: case BlockType.quote: var prevline = lines.Last(); - if (prevline.blockType == BlockType.Blank) + if (prevline.BlockType == BlockType.Blank) { // Start a code block after a paragraph CollapseLines(blocks, lines); @@ -314,7 +327,7 @@ namespace MarkdownDeep case BlockType.p: case BlockType.quote: var prevline = lines.Last(); - if (prevline.blockType == BlockType.Blank || m_parentType==BlockType.ol_li || m_parentType==BlockType.ul_li || m_parentType==BlockType.dd) + if (prevline.BlockType == BlockType.Blank || m_parentType==BlockType.ol_li || m_parentType==BlockType.ul_li || m_parentType==BlockType.dd) { // List starting after blank line after paragraph or quote CollapseLines(blocks, lines); @@ -330,7 +343,7 @@ namespace MarkdownDeep case BlockType.ol_li: case BlockType.ul_li: - if (b.blockType!=BlockType.ol_li && b.blockType!=BlockType.ul_li) + if (b.BlockType!=BlockType.ol_li && b.BlockType!=BlockType.ul_li) { CollapseLines(blocks, lines); } @@ -339,7 +352,7 @@ namespace MarkdownDeep case BlockType.dd: case BlockType.footnote: - if (b.blockType != currentBlockType) + if (b.BlockType != currentBlockType) { CollapseLines(blocks, lines); } @@ -412,7 +425,7 @@ namespace MarkdownDeep StringBuilder b = m_markdown.GetStringBuilder(); foreach (var l in lines) { - b.Append(l.buf, l.contentStart, l.contentLen); + b.Append(l.Buf, l.ContentStart, l.ContentLen); b.Append('\n'); } return b.ToString(); @@ -421,7 +434,7 @@ namespace MarkdownDeep internal void CollapseLines(List blocks, List lines) { // Remove trailing blank lines - while (lines.Count>0 && lines.Last().blockType == BlockType.Blank) + while (lines.Count>0 && lines.Last().BlockType == BlockType.Blank) { FreeBlock(lines.Pop()); } @@ -434,16 +447,16 @@ namespace MarkdownDeep // What sort of block? - switch (lines[0].blockType) + switch (lines[0].BlockType) { case BlockType.p: { // Collapse all lines into a single paragraph var para = CreateBlock(); - para.blockType = BlockType.p; - para.buf = lines[0].buf; - para.contentStart = lines[0].contentStart; - para.contentEnd = lines.Last().contentEnd; + para.BlockType = BlockType.p; + para.Buf = lines[0].Buf; + para.ContentStart = lines[0].ContentStart; + para.ContentEnd = lines.Last().ContentEnd; blocks.Add(para); FreeBlocks(lines); break; @@ -453,7 +466,7 @@ namespace MarkdownDeep { // Create a new quote block var quote = new Block(BlockType.quote); - quote.children = new BlockProcessor(m_markdown, m_bMarkdownInHtml, BlockType.quote).Process(RenderLines(lines)); + quote.Children = new BlockProcessor(m_markdown, m_bMarkdownInHtml, BlockType.quote).Process(RenderLines(lines)); FreeBlocks(lines); blocks.Add(quote); break; @@ -468,10 +481,10 @@ namespace MarkdownDeep if (blocks.Count > 0) { var prev=blocks[blocks.Count-1]; - switch (prev.blockType) + switch (prev.BlockType) { case BlockType.p: - prev.blockType = BlockType.dt; + prev.BlockType = BlockType.dt; break; case BlockType.dd: @@ -479,9 +492,9 @@ namespace MarkdownDeep default: var wrapper = CreateBlock(); - wrapper.blockType = BlockType.dt; - wrapper.children = new List(); - wrapper.children.Add(prev); + wrapper.BlockType = BlockType.dt; + wrapper.Children = new List(); + wrapper.Children.Add(prev); blocks.Pop(); blocks.Add(wrapper); break; @@ -510,8 +523,8 @@ namespace MarkdownDeep } } */ - codeblock.children = new List(); - codeblock.children.AddRange(lines); + codeblock.Children = new List(); + codeblock.Children.AddRange(lines); blocks.Add(codeblock); lines.Clear(); break; @@ -526,24 +539,24 @@ namespace MarkdownDeep Block b=CreateBlock(); // Store line start - b.lineStart=position; - b.buf=input; + b.LineStart=Position; + b.Buf=Input; // Scan the line - b.contentStart = position; - b.contentLen = -1; - b.blockType=EvaluateLine(b); + b.ContentStart = Position; + b.ContentLen = -1; + b.BlockType=EvaluateLine(b); // If end of line not returned, do it automatically - if (b.contentLen < 0) + if (b.ContentLen < 0) { // Move to end of line SkipToEol(); - b.contentLen = position - b.contentStart; + b.ContentLen = Position - b.ContentStart; } // Setup line length - b.lineLen=position-b.lineStart; + b.LineLen=Position-b.LineStart; // Next line SkipEol(); @@ -555,20 +568,20 @@ namespace MarkdownDeep BlockType EvaluateLine(Block b) { // Empty line? - if (eol) + if (Eol) return BlockType.Blank; // Save start of line position - int line_start= position; + int line_start= Position; // ## Heading ## - char ch=current; + char ch=Current; if (ch == '#') { // Work out heading level int level = 1; SkipForward(1); - while (current == '#') + while (Current == '#') { level++; SkipForward(1); @@ -582,7 +595,7 @@ namespace MarkdownDeep SkipLinespace(); // Save start position - b.contentStart = position; + b.ContentStart = Position; // Jump to end SkipToEol(); @@ -590,29 +603,29 @@ namespace MarkdownDeep // In extra mode, check for a trailing HTML ID if (m_markdown.ExtraMode && !m_markdown.SafeMode) { - int end=position; - string strID = Utils.StripHtmlID(input, b.contentStart, ref end); + int end=Position; + string strID = Utils.StripHtmlID(Input, b.ContentStart, ref end); if (strID!=null) { - b.data = strID; - position = end; + b.Data = strID; + Position = end; } } // Rewind over trailing hashes - while (position>b.contentStart && CharAtOffset(-1) == '#') + while (Position>b.ContentStart && CharAtOffset(-1) == '#') { SkipForward(-1); } // Rewind over trailing spaces - while (position>b.contentStart && char.IsWhiteSpace(CharAtOffset(-1))) + while (Position>b.ContentStart && char.IsWhiteSpace(CharAtOffset(-1))) { SkipForward(-1); } // Create the heading block - b.contentEnd = position; + b.ContentEnd = Position; SkipToEol(); return BlockType.h1 + (level - 1); @@ -623,7 +636,7 @@ namespace MarkdownDeep { // Skip all matching characters char chType = ch; - while (current==chType) + while (Current==chType) { SkipForward(1); } @@ -632,12 +645,12 @@ namespace MarkdownDeep SkipLinespace(); // If not at eol, must have found something other than setext header - if (eol) + if (Eol) { return chType == '=' ? BlockType.post_h1 : BlockType.post_h2; } - position = line_start; + Position = line_start; } // MarkdownExtra Table row indicator? @@ -646,37 +659,37 @@ namespace MarkdownDeep TableSpec spec = TableSpec.Parse(this); if (spec!=null) { - b.data = spec; + b.Data = spec; return BlockType.table_spec; } - position = line_start; + Position = line_start; } // Fenced code blocks? if((m_markdown.ExtraMode && (ch == '~' || ch=='`')) || (m_markdown.GitHubCodeBlocks && (ch=='`'))) { if (ProcessFencedCodeBlock(b)) - return b.blockType; + return b.BlockType; // Rewind - position = line_start; + Position = line_start; } // Scan the leading whitespace, remembering how many spaces and where the first tab is int tabPos = -1; int leadingSpaces = 0; - while (!eol) + while (!Eol) { - if (current == ' ') + if (Current == ' ') { if (tabPos < 0) leadingSpaces++; } - else if (current == '\t') + else if (Current == '\t') { if (tabPos < 0) - tabPos = position; + tabPos = Position; } else { @@ -687,41 +700,41 @@ namespace MarkdownDeep } // Blank line? - if (eol) + if (Eol) { - b.contentEnd = b.contentStart; + b.ContentEnd = b.ContentStart; return BlockType.Blank; } // 4 leading spaces? if (leadingSpaces >= 4) { - b.contentStart = line_start + 4; + b.ContentStart = line_start + 4; return BlockType.indent; } // Tab in the first 4 characters? if (tabPos >= 0 && tabPos - line_start<4) { - b.contentStart = tabPos + 1; + b.ContentStart = tabPos + 1; return BlockType.indent; } // Treat start of line as after leading whitespace - b.contentStart = position; + b.ContentStart = Position; // Get the next character - ch = current; + ch = Current; // Html block? if (ch == '<') { // Scan html block if (ScanHtml(b)) - return b.blockType; + return b.BlockType; // Rewind - position = b.contentStart; + Position = b.ContentStart; } // Block quotes start with '>' and have one space or one tab following @@ -732,12 +745,12 @@ namespace MarkdownDeep { // Skip it and create quote block SkipForward(2); - b.contentStart = position; + b.ContentStart = Position; return BlockType.quote; } SkipForward(1); - b.contentStart = position; + b.ContentStart = Position; return BlockType.quote; } @@ -745,17 +758,17 @@ namespace MarkdownDeep if (ch == '-' || ch == '_' || ch == '*') { int count = 0; - while (!eol) + while (!Eol) { - char chType = current; - if (current == ch) + char chType = Current; + if (Current == ch) { count++; SkipForward(1); continue; } - if (IsLineSpace(current)) + if (IsLineSpace(Current)) { SkipForward(1); continue; @@ -764,7 +777,7 @@ namespace MarkdownDeep break; } - if (eol && count >= 3) + if (Eol && count >= 3) { if (m_markdown.UserBreaks) return BlockType.user_break; @@ -773,7 +786,7 @@ namespace MarkdownDeep } // Rewind - position = b.contentStart; + Position = b.ContentStart; } // Abbreviation definition? @@ -783,13 +796,13 @@ namespace MarkdownDeep SkipLinespace(); Mark(); - while (!eol && current != ']') + while (!Eol && Current != ']') { SkipForward(1); } var abbr = Extract().Trim(); - if (current == ']' && CharAtOffset(1) == ':' && !string.IsNullOrEmpty(abbr)) + if (Current == ']' && CharAtOffset(1) == ':' && !string.IsNullOrEmpty(abbr)) { SkipForward(2); SkipLinespace(); @@ -805,7 +818,7 @@ namespace MarkdownDeep return BlockType.Blank; } - position = b.contentStart; + Position = b.ContentStart; } // Unordered list @@ -814,7 +827,7 @@ namespace MarkdownDeep // Skip it SkipForward(1); SkipLinespace(); - b.contentStart = position; + b.ContentStart = Position; return BlockType.ul_li; } @@ -823,7 +836,7 @@ namespace MarkdownDeep { SkipForward(1); SkipLinespace(); - b.contentStart = position; + b.ContentStart = Position; return BlockType.dd; } @@ -834,16 +847,16 @@ namespace MarkdownDeep // Skip all digits SkipForward(1); - while (char.IsDigit(current)) + while (char.IsDigit(Current)) SkipForward(1); if (SkipChar('.') && SkipLinespace()) { - b.contentStart = position; + b.ContentStart = Position; return BlockType.ol_li; } - position=b.contentStart; + Position=b.ContentStart; } // Reference link definition? @@ -852,7 +865,7 @@ namespace MarkdownDeep // Footnote definition? if (m_markdown.ExtraMode && CharAtOffset(1) == '^') { - var savepos = position; + var savepos = Position; SkipForward(2); @@ -860,12 +873,12 @@ namespace MarkdownDeep if (SkipFootnoteID(out id) && SkipChar(']') && SkipChar(':')) { SkipLinespace(); - b.contentStart = position; - b.data = id; + b.ContentStart = Position; + b.Data = id; return BlockType.footnote; } - position = savepos; + Position = savepos; } // Parse a link definition @@ -877,19 +890,22 @@ namespace MarkdownDeep } } + // DocNet '@' extensions + if(ch == '@' && m_markdown.DocNetMode) + { + if(HandleDocNetExtension(b)) + { + return b.BlockType; + } + + // Not valid, Rewind + Position = b.ContentStart; + } + // Nothing special return BlockType.p; } - internal enum MarkdownInHtmlMode - { - NA, // No markdown attribute on the tag - Block, // markdown=1 or markdown=block - Span, // markdown=1 or markdown=span - Deep, // markdown=deep - recursive block mode - Off, // Markdown="something else" - } - internal MarkdownInHtmlMode GetMarkdownMode(HtmlTag tag) { // Get the markdown attribute @@ -926,17 +942,17 @@ namespace MarkdownDeep // Current position is just after the opening tag // Scan until we find matching closing tag - int inner_pos = position; + int inner_pos = Position; int depth = 1; bool bHasUnsafeContent = false; - while (!eof) + while (!Eof) { // Find next angle bracket if (!Find('<')) break; // Is it a html tag? - int tagpos = position; + int tagpos = Position; HtmlTag tag = HtmlTag.Parse(this); if (tag == null) { @@ -968,22 +984,22 @@ namespace MarkdownDeep SkipLinespace(); SkipEol(); - b.blockType = BlockType.HtmlTag; - b.data = openingTag; - b.contentEnd = position; + b.BlockType = BlockType.HtmlTag; + b.Data = openingTag; + b.ContentEnd = Position; switch (mode) { case MarkdownInHtmlMode.Span: { Block span = this.CreateBlock(); - span.buf = input; - span.blockType = BlockType.span; - span.contentStart = inner_pos; - span.contentLen = tagpos - inner_pos; + span.Buf = Input; + span.BlockType = BlockType.span; + span.ContentStart = inner_pos; + span.ContentLen = tagpos - inner_pos; - b.children = new List(); - b.children.Add(span); + b.Children = new List(); + b.Children.Add(span); break; } @@ -992,7 +1008,7 @@ namespace MarkdownDeep { // Scan the internal content var bp = new BlockProcessor(m_markdown, mode == MarkdownInHtmlMode.Deep); - b.children = bp.ScanLines(input, inner_pos, tagpos - inner_pos); + b.Children = bp.ScanLines(Input, inner_pos, tagpos - inner_pos); break; } @@ -1000,19 +1016,19 @@ namespace MarkdownDeep { if (bHasUnsafeContent) { - b.blockType = BlockType.unsafe_html; - b.contentEnd = position; + b.BlockType = BlockType.unsafe_html; + b.ContentEnd = Position; } else { Block span = this.CreateBlock(); - span.buf = input; - span.blockType = BlockType.html; - span.contentStart = inner_pos; - span.contentLen = tagpos - inner_pos; + span.Buf = Input; + span.BlockType = BlockType.html; + span.ContentStart = inner_pos; + span.ContentLen = tagpos - inner_pos; - b.children = new List(); - b.children.Add(span); + b.Children = new List(); + b.Children.Add(span); } break; } @@ -1037,7 +1053,7 @@ namespace MarkdownDeep internal bool ScanHtml(Block b) { // Remember start of html - int posStartPiece = this.position; + int posStartPiece = this.Position; // Parse a HTML tag HtmlTag openingTag = HtmlTag.Parse(this); @@ -1049,9 +1065,7 @@ namespace MarkdownDeep return false; // Safe mode? - bool bHasUnsafeContent = false; - if (m_markdown.SafeMode && !openingTag.IsSafe()) - bHasUnsafeContent = true; + bool bHasUnsafeContent = m_markdown.SafeMode && !openingTag.IsSafe(); HtmlTagFlags flags = openingTag.Flags; @@ -1065,8 +1079,8 @@ namespace MarkdownDeep SkipLinespace(); SkipEol(); - b.contentEnd = position; - b.blockType = bHasUnsafeContent ? BlockType.unsafe_html : BlockType.html; + b.ContentEnd = Position; + b.BlockType = bHasUnsafeContent ? BlockType.unsafe_html : BlockType.html; return true; } @@ -1075,13 +1089,13 @@ namespace MarkdownDeep { // Yes, opening tag must be on a line by itself SkipLinespace(); - if (!eol) + if (!Eol) return false; } // Head block extraction? bool bHeadBlock = m_markdown.ExtractHeadBlocks && string.Compare(openingTag.name, "head", true) == 0; - int headStart = this.position; + int headStart = this.Position; // Work out the markdown mode for this element if (!bHeadBlock && m_markdown.ExtraMode) @@ -1098,14 +1112,14 @@ namespace MarkdownDeep // Now capture everything up to the closing tag and put it all in a single HTML block int depth = 1; - while (!eof) + while (!Eof) { // Find next angle bracket if (!Find('<')) break; // Save position of current tag - int posStartCurrentTag = position; + int posStartCurrentTag = Position; // Is it a html tag? HtmlTag tag = HtmlTag.Parse(this); @@ -1142,10 +1156,10 @@ namespace MarkdownDeep if (posStartCurrentTag > posStartPiece) { Block htmlBlock = this.CreateBlock(); - htmlBlock.buf = input; - htmlBlock.blockType = BlockType.html; - htmlBlock.contentStart = posStartPiece; - htmlBlock.contentLen = posStartCurrentTag - posStartPiece; + htmlBlock.Buf = Input; + htmlBlock.BlockType = BlockType.html; + htmlBlock.ContentStart = posStartPiece; + htmlBlock.ContentLen = posStartCurrentTag - posStartPiece; childBlocks.Add(htmlBlock); } @@ -1154,7 +1168,7 @@ namespace MarkdownDeep childBlocks.Add(markdownBlock); // Remember start of the next piece - posStartPiece = position; + posStartPiece = Position; continue; } @@ -1180,8 +1194,8 @@ namespace MarkdownDeep // If anything unsafe detected, just encode the whole block if (bHasUnsafeContent) { - b.blockType = BlockType.unsafe_html; - b.contentEnd = position; + b.BlockType = BlockType.unsafe_html; + b.ContentEnd = Position; return true; } @@ -1189,21 +1203,21 @@ namespace MarkdownDeep if (childBlocks != null) { // Create a block for the remainder - if (position > posStartPiece) + if (Position > posStartPiece) { Block htmlBlock = this.CreateBlock(); - htmlBlock.buf = input; - htmlBlock.blockType = BlockType.html; - htmlBlock.contentStart = posStartPiece; - htmlBlock.contentLen = position - posStartPiece; + htmlBlock.Buf = Input; + htmlBlock.BlockType = BlockType.html; + htmlBlock.ContentStart = posStartPiece; + htmlBlock.ContentLen = Position - posStartPiece; childBlocks.Add(htmlBlock); } // Return a composite block - b.blockType = BlockType.Composite; - b.contentEnd = position; - b.children = childBlocks; + b.BlockType = BlockType.Composite; + b.ContentEnd = Position; + b.Children = childBlocks; return true; } @@ -1212,16 +1226,16 @@ namespace MarkdownDeep { var content = this.Substring(headStart, posStartCurrentTag - headStart); m_markdown.HeadBlockContent = (m_markdown.HeadBlockContent ?? "") + content.Trim() + "\n"; - b.blockType = BlockType.html; - b.contentStart = position; - b.contentEnd = position; - b.lineStart = position; + b.BlockType = BlockType.html; + b.ContentStart = Position; + b.ContentEnd = Position; + b.LineStart = Position; return true; } // Straight html block - b.blockType = BlockType.html; - b.contentEnd = position; + b.BlockType = BlockType.html; + b.ContentEnd = Position; return true; } } @@ -1236,6 +1250,134 @@ namespace MarkdownDeep return false; } + + /// + /// Handles the docnet extension, starting with '@'. This can be: + /// * @fa- + /// * @alert + /// @end + /// * @tabs + /// @tabsend + /// + /// The b. + /// true if extension was correctly handled, false otherwise (error) + private bool HandleDocNetExtension(Block b) + { + var initialStart = this.Position; + if(DoesMatch("@fa-")) + { + return HandleFontAwesomeExtension(b); + } + if(DoesMatch("@tabs")) + { + return HandleTabsExtension(b); + } + if(DoesMatch("@alert")) + { + return HandleAlertExtension(b); + } + return false; + } + + + /// + /// Handles the alert extension: + /// @alert type + /// text + /// @end + /// + /// where text can be anything and has to be handled further. + /// type is: danger, warning, info or neutral. + /// + /// The b. + /// + private bool HandleAlertExtension(Block b) + { + // skip '@alert' + if(!SkipString("@alert")) + { + return false; + } + SkipLinespace(); + var alertType = string.Empty; + if(!SkipIdentifier(ref alertType)) + { + return false; + } + 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 + 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--; + } + } + b.BlockType = BlockType.alert; + b.Data = alertType; + // 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; + } + + private bool HandleTabsExtension(Block b) + { +#warning IMPLEMENT + return false; + } + + + /// + /// Handles the font awesome extension, which is available in DocNet mode. FontAwesome extension uses @fa-iconname, where iconname is the name of the fontawesome icon. + /// Called when '@fa-' has been seen. Current position is on 'f' of 'fa-'. + /// + /// The b. + /// + private bool HandleFontAwesomeExtension(Block b) + { + string iconName = string.Empty; + int newPosition = this.Position; + if(!Utils.SkipFontAwesome(this.Input, this.Position, out newPosition, out iconName)) + { + return false; + } + this.Position = newPosition; + b.BlockType = BlockType.font_awesome; + b.Data = iconName; + return true; + } + + /* * Spacing * @@ -1249,7 +1391,7 @@ namespace MarkdownDeep private Block BuildList(List lines) { // What sort of list are we dealing with - BlockType listType = lines[0].blockType; + BlockType listType = lines[0].BlockType; System.Diagnostics.Debug.Assert(listType == BlockType.ul_li || listType == BlockType.ol_li); // Preprocess @@ -1257,31 +1399,31 @@ namespace MarkdownDeep // 2. Promote any unindented lines that have more leading space // than the original list item to indented, including leading // special chars - int leadingSpace = lines[0].leadingSpaces; + int leadingSpace = lines[0].LeadingSpaces; for (int i = 1; i < lines.Count; i++) { // Join plain paragraphs - if ((lines[i].blockType == BlockType.p) && - (lines[i - 1].blockType == BlockType.p || lines[i - 1].blockType == BlockType.ul_li || lines[i - 1].blockType==BlockType.ol_li)) + if ((lines[i].BlockType == BlockType.p) && + (lines[i - 1].BlockType == BlockType.p || lines[i - 1].BlockType == BlockType.ul_li || lines[i - 1].BlockType==BlockType.ol_li)) { - lines[i - 1].contentEnd = lines[i].contentEnd; + lines[i - 1].ContentEnd = lines[i].ContentEnd; FreeBlock(lines[i]); lines.RemoveAt(i); i--; continue; } - if (lines[i].blockType != BlockType.indent && lines[i].blockType != BlockType.Blank) + if (lines[i].BlockType != BlockType.indent && lines[i].BlockType != BlockType.Blank) { - int thisLeadingSpace = lines[i].leadingSpaces; + int thisLeadingSpace = lines[i].LeadingSpaces; if (thisLeadingSpace > leadingSpace) { // Change line to indented, including original leading chars // (eg: '* ', '>', '1.' etc...) - lines[i].blockType = BlockType.indent; - int saveend = lines[i].contentEnd; - lines[i].contentStart = lines[i].lineStart + thisLeadingSpace; - lines[i].contentEnd = saveend; + lines[i].BlockType = BlockType.indent; + int saveend = lines[i].ContentEnd; + lines[i].ContentStart = lines[i].LineStart + thisLeadingSpace; + lines[i].ContentEnd = saveend; } } } @@ -1289,21 +1431,21 @@ namespace MarkdownDeep // Create the wrapping list item var List = new Block(listType == BlockType.ul_li ? BlockType.ul : BlockType.ol); - List.children = new List(); + List.Children = new List(); // Process all lines in the range for (int i = 0; i < lines.Count; i++) { - System.Diagnostics.Debug.Assert(lines[i].blockType == BlockType.ul_li || lines[i].blockType==BlockType.ol_li); + System.Diagnostics.Debug.Assert(lines[i].BlockType == BlockType.ul_li || lines[i].BlockType==BlockType.ol_li); // Find start of item, including leading blanks int start_of_li = i; - while (start_of_li > 0 && lines[start_of_li - 1].blockType == BlockType.Blank) + while (start_of_li > 0 && lines[start_of_li - 1].BlockType == BlockType.Blank) start_of_li--; // Find end of the item, including trailing blanks int end_of_li = i; - while (end_of_li < lines.Count - 1 && lines[end_of_li + 1].blockType != BlockType.ul_li && lines[end_of_li + 1].blockType != BlockType.ol_li) + while (end_of_li < lines.Count - 1 && lines[end_of_li + 1].BlockType != BlockType.ul_li && lines[end_of_li + 1].BlockType != BlockType.ol_li) end_of_li++; // Is this a simple or complex list item? @@ -1311,7 +1453,7 @@ namespace MarkdownDeep { // It's a simple, single line item item System.Diagnostics.Debug.Assert(start_of_li == i); - List.children.Add(CreateBlock().CopyFrom(lines[i])); + List.Children.Add(CreateBlock().CopyFrom(lines[i])); } else { @@ -1321,10 +1463,10 @@ namespace MarkdownDeep for (int j = start_of_li; j <= end_of_li; j++) { var l = lines[j]; - sb.Append(l.buf, l.contentStart, l.contentLen); + sb.Append(l.Buf, l.ContentStart, l.ContentLen); sb.Append('\n'); - if (lines[j].blockType == BlockType.Blank) + if (lines[j].BlockType == BlockType.Blank) { bAnyBlanks = true; } @@ -1332,22 +1474,22 @@ namespace MarkdownDeep // Create the item and process child blocks var item = new Block(BlockType.li); - item.children = new BlockProcessor(m_markdown, m_bMarkdownInHtml, listType).Process(sb.ToString()); + item.Children = new BlockProcessor(m_markdown, m_bMarkdownInHtml, listType).Process(sb.ToString()); // If no blank lines, change all contained paragraphs to plain text if (!bAnyBlanks) { - foreach (var child in item.children) + foreach (var child in item.Children) { - if (child.blockType == BlockType.p) + if (child.BlockType == BlockType.p) { - child.blockType = BlockType.span; + child.BlockType = BlockType.span; } } } // Add the complex item - List.children.Add(item); + List.Children.Add(item); } // Continue processing from end of li @@ -1370,10 +1512,10 @@ namespace MarkdownDeep for (int i = 1; i < lines.Count; i++) { // Join plain paragraphs - if ((lines[i].blockType == BlockType.p) && - (lines[i - 1].blockType == BlockType.p || lines[i - 1].blockType == BlockType.dd)) + if ((lines[i].BlockType == BlockType.p) && + (lines[i - 1].BlockType == BlockType.p || lines[i - 1].BlockType == BlockType.dd)) { - lines[i - 1].contentEnd = lines[i].contentEnd; + lines[i - 1].ContentEnd = lines[i].ContentEnd; FreeBlock(lines[i]); lines.RemoveAt(i); i--; @@ -1382,7 +1524,7 @@ namespace MarkdownDeep } // Single line definition - bool bPreceededByBlank=(bool)lines[0].data; + bool bPreceededByBlank=(bool)lines[0].Data; if (lines.Count==1 && !bPreceededByBlank) { var ret=lines[0]; @@ -1395,14 +1537,14 @@ namespace MarkdownDeep for (int i = 0; i < lines.Count; i++) { var l = lines[i]; - sb.Append(l.buf, l.contentStart, l.contentLen); + sb.Append(l.Buf, l.ContentStart, l.ContentLen); sb.Append('\n'); } // Create the item and process child blocks var item = this.CreateBlock(); - item.blockType = BlockType.dd; - item.children = new BlockProcessor(m_markdown, m_bMarkdownInHtml, BlockType.dd).Process(sb.ToString()); + item.BlockType = BlockType.dd; + item.Children = new BlockProcessor(m_markdown, m_bMarkdownInHtml, BlockType.dd).Process(sb.ToString()); FreeBlocks(lines); lines.Clear(); @@ -1416,20 +1558,20 @@ namespace MarkdownDeep Block currentList = null; for (int i = 0; i < blocks.Count; i++) { - switch (blocks[i].blockType) + switch (blocks[i].BlockType) { case BlockType.dt: case BlockType.dd: if (currentList==null) { currentList=CreateBlock(); - currentList.blockType=BlockType.dl; - currentList.children=new List(); + currentList.BlockType=BlockType.dl; + currentList.Children=new List(); blocks.Insert(i, currentList); i++; } - currentList.children.Add(blocks[i]); + currentList.Children.Add(blocks[i]); blocks.RemoveAt(i); i--; break; @@ -1447,10 +1589,10 @@ namespace MarkdownDeep for (int i = 1; i < lines.Count; i++) { // Join plain paragraphs - if ((lines[i].blockType == BlockType.p) && - (lines[i - 1].blockType == BlockType.p || lines[i - 1].blockType == BlockType.footnote)) + if ((lines[i].BlockType == BlockType.p) && + (lines[i - 1].BlockType == BlockType.p || lines[i - 1].BlockType == BlockType.footnote)) { - lines[i - 1].contentEnd = lines[i].contentEnd; + lines[i - 1].ContentEnd = lines[i].ContentEnd; FreeBlock(lines[i]); lines.RemoveAt(i); i--; @@ -1463,15 +1605,15 @@ namespace MarkdownDeep for (int i = 0; i < lines.Count; i++) { var l = lines[i]; - sb.Append(l.buf, l.contentStart, l.contentLen); + sb.Append(l.Buf, l.ContentStart, l.ContentLen); sb.Append('\n'); } // Create the item and process child blocks var item = this.CreateBlock(); - item.blockType = BlockType.footnote; - item.data = lines[0].data; - item.children = new BlockProcessor(m_markdown, m_bMarkdownInHtml, BlockType.footnote).Process(sb.ToString()); + item.BlockType = BlockType.footnote; + item.Data = lines[0].Data; + item.Children = new BlockProcessor(m_markdown, m_bMarkdownInHtml, BlockType.footnote).Process(sb.ToString()); FreeBlocks(lines); lines.Clear(); @@ -1482,11 +1624,11 @@ namespace MarkdownDeep bool ProcessFencedCodeBlock(Block b) { - char delim = current; + char delim = Current; // Extract the fence Mark(); - while (current == delim) + while (Current == delim) SkipForward(1); string strFence = Extract(); @@ -1501,7 +1643,7 @@ namespace MarkdownDeep // allow space between first fence and name SkipLinespace(); SkipIdentifier(ref languageName); - b.data = string.IsNullOrWhiteSpace(languageName) ? "nohighlight" : languageName; + b.Data = string.IsNullOrWhiteSpace(languageName) ? "nohighlight" : languageName; // skip linespace to EOL SkipLinespace(); } @@ -1509,13 +1651,13 @@ namespace MarkdownDeep { // Rest of line must be blank SkipLinespace(); - if(!eol) + if(!Eol) return false; } // Skip the eol and remember start of code SkipEol(); - int startCode = position; + int startCode = Position; // Find the end fence if (!Find(strFence)) @@ -1525,35 +1667,35 @@ namespace MarkdownDeep if (!IsLineEnd(CharAtOffset(-1))) return false; - int endCode = position; + int endCode = Position; // Skip the fence SkipForward(strFence.Length); // Whitespace allowed at end SkipLinespace(); - if (!eol) + if (!Eol) return false; // Create the code block - b.blockType = BlockType.codeblock; - b.children = new List(); + b.BlockType = BlockType.codeblock; + b.Children = new List(); // Remove the trailing line end - if (input[endCode - 1] == '\r' && input[endCode - 2] == '\n') + if (Input[endCode - 1] == '\r' && Input[endCode - 2] == '\n') endCode -= 2; - else if (input[endCode - 1] == '\n' && input[endCode - 2] == '\r') + else if (Input[endCode - 1] == '\n' && Input[endCode - 2] == '\r') endCode -= 2; else endCode--; // Create the child block with the entire content var child = CreateBlock(); - child.blockType = BlockType.indent; - child.buf = input; - child.contentStart = startCode; - child.contentEnd = endCode; - b.children.Add(child); + child.BlockType = BlockType.indent; + child.Buf = Input; + child.ContentStart = startCode; + child.ContentEnd = endCode; + b.Children.Add(child); return true; } diff --git a/src/MarkdownDeep/HtmlTag.cs b/src/MarkdownDeep/HtmlTag.cs index aae5db7..70b950b 100644 --- a/src/MarkdownDeep/HtmlTag.cs +++ b/src/MarkdownDeep/HtmlTag.cs @@ -208,7 +208,7 @@ namespace MarkdownDeep if (ret!=null) { - pos = sp.position; + pos = sp.Position; return ret; } @@ -218,7 +218,7 @@ namespace MarkdownDeep public static HtmlTag Parse(StringScanner p) { // Save position - int savepos = p.position; + int savepos = p.Position; // Parse it var ret = ParseHelper(p); @@ -226,14 +226,14 @@ namespace MarkdownDeep return ret; // Rewind if failed - p.position = savepos; + p.Position = savepos; return null; } private static HtmlTag ParseHelper(StringScanner p) { // Does it look like a tag? - if (p.current != '<') + if (p.Current != '<') return null; // Skip '<' @@ -270,7 +270,7 @@ namespace MarkdownDeep // If it's a closing tag, no attributes if (bClosing) { - if (p.current != '>') + if (p.Current != '>') return null; p.SkipForward(1); @@ -278,7 +278,7 @@ namespace MarkdownDeep } - while (!p.eof) + while (!p.Eof) { // Skip whitespace p.SkipWhitespace(); @@ -328,10 +328,10 @@ namespace MarkdownDeep { // Scan the value p.Mark(); - while (!p.eof && !char.IsWhiteSpace(p.current) && p.current != '>' && p.current != '/') + while (!p.Eof && !char.IsWhiteSpace(p.Current) && p.Current != '>' && p.Current != '/') p.SkipForward(1); - if (!p.eof) + if (!p.Eof) { // Store the value tag.m_attributes.Add(attributeName, p.Extract()); diff --git a/src/MarkdownDeep/LinkDefinition.cs b/src/MarkdownDeep/LinkDefinition.cs index b60d51e..9ac9db4 100644 --- a/src/MarkdownDeep/LinkDefinition.cs +++ b/src/MarkdownDeep/LinkDefinition.cs @@ -146,10 +146,10 @@ namespace MarkdownDeep // Parse a link definition internal static LinkDefinition ParseLinkDefinition(StringScanner p, bool ExtraMode) { - int savepos=p.position; + int savepos=p.Position; var l = ParseLinkDefinitionInternal(p, ExtraMode); if (l==null) - p.position = savepos; + p.Position = savepos; return l; } @@ -180,7 +180,7 @@ namespace MarkdownDeep p.SkipLinespace(); // Trailing crap, not a valid link reference... - if (!p.eol) + if (!p.Eol) return null; return link; @@ -195,7 +195,7 @@ namespace MarkdownDeep p.SkipWhitespace(); // End of string? - if (p.eol) + if (p.Eol) return null; // Create the link definition @@ -208,9 +208,9 @@ namespace MarkdownDeep p.Mark(); // Find end of the url - while (p.current != '>') + while (p.Current != '>') { - if (p.eof) + if (p.Eof) return null; p.SkipEscapableChar(ExtraMode); } @@ -230,9 +230,9 @@ namespace MarkdownDeep // Find end of the url p.Mark(); int paren_depth = 1; - while (!p.eol) + while (!p.Eol) { - char ch=p.current; + char ch=p.Current; if (char.IsWhiteSpace(ch)) break; if (id == null) @@ -259,9 +259,9 @@ namespace MarkdownDeep if (p.DoesMatch(')')) return r; - bool bOnNewLine = p.eol; - int posLineEnd = p.position; - if (p.eol) + bool bOnNewLine = p.Eol; + int posLineEnd = p.Position; + if (p.Eol) { p.SkipEol(); p.SkipLinespace(); @@ -269,11 +269,11 @@ namespace MarkdownDeep // Work out what the title is delimited with char delim; - switch (p.current) + switch (p.Current) { case '\'': case '\"': - delim = p.current; + delim = p.Current; break; case '(': @@ -283,7 +283,7 @@ namespace MarkdownDeep default: if (bOnNewLine) { - p.position = posLineEnd; + p.Position = posLineEnd; return r; } else @@ -297,15 +297,15 @@ namespace MarkdownDeep p.Mark(); while (true) { - if (p.eol) + if (p.Eol) return null; - if (p.current == delim) + if (p.Current == delim) { if (delim != ')') { - int savepos = p.position; + int savepos = p.Position; // Check for embedded quotes in title @@ -315,13 +315,13 @@ namespace MarkdownDeep // Next we expect either the end of the line for a link definition // or the close bracket for an inline link - if ((id == null && p.current != ')') || - (id != null && !p.eol)) + if ((id == null && p.Current != ')') || + (id != null && !p.Eol)) { continue; } - p.position = savepos; + p.Position = savepos; } // End of title diff --git a/src/MarkdownDeep/MardownDeep.cs b/src/MarkdownDeep/MardownDeep.cs index 2e31917..6b1c585 100644 --- a/src/MarkdownDeep/MardownDeep.cs +++ b/src/MarkdownDeep/MardownDeep.cs @@ -178,28 +178,28 @@ namespace MarkdownDeep var fn = m_UsedFootnotes[i]; sb.Append("
  • \n"); // We need to get the return link appended to the last paragraph // in the footnote - string strReturnLink = string.Format("", (string)fn.data); + string strReturnLink = string.Format("", (string)fn.Data); // Get the last child of the footnote - var child = fn.children[fn.children.Count - 1]; - if (child.blockType == BlockType.p) + var child = fn.Children[fn.Children.Count - 1]; + if (child.BlockType == BlockType.p) { - child.blockType = BlockType.p_footnote; - child.data = strReturnLink; + child.BlockType = BlockType.p_footnote; + child.Data = strReturnLink; } else { child = CreateBlock(); - child.contentLen = 0; - child.blockType = BlockType.p_footnote; - child.data = strReturnLink; - fn.children.Add(child); + child.ContentLen = 0; + child.BlockType = BlockType.p_footnote; + child.Data = strReturnLink; + fn.Children.Add(child); } @@ -420,7 +420,7 @@ namespace MarkdownDeep private bool IsSectionHeader(Block b) { - return b.blockType >= BlockType.h1 && b.blockType <= BlockType.h3; + return b.BlockType >= BlockType.h1 && b.BlockType <= BlockType.h3; } @@ -441,10 +441,10 @@ namespace MarkdownDeep for (int i = 0; i < blocks.Count; i++) { var b = blocks[i]; - if (b.blockType==BlockType.user_break) + if (b.BlockType==BlockType.user_break) { // Get the offset of the section - int iSectionOffset = b.lineStart; + int iSectionOffset = b.LineStart; // Add section Sections.Add(markdown.Substring(iPrevSectionOffset, iSectionOffset - iPrevSectionOffset).Trim()); @@ -452,9 +452,9 @@ namespace MarkdownDeep // Next section starts on next line if (i + 1 < blocks.Count) { - iPrevSectionOffset = blocks[i + 1].lineStart; + iPrevSectionOffset = blocks[i + 1].LineStart; if (iPrevSectionOffset==0) - iPrevSectionOffset = blocks[i + 1].contentStart; + iPrevSectionOffset = blocks[i + 1].ContentStart; } else iPrevSectionOffset = markdown.Length; @@ -512,7 +512,7 @@ namespace MarkdownDeep if (md.IsSectionHeader(b)) { // Get the offset of the section - int iSectionOffset = b.lineStart; + int iSectionOffset = b.LineStart; // Add section Sections.Add(markdown.Substring(iPrevSectionOffset, iSectionOffset - iPrevSectionOffset)); @@ -560,7 +560,7 @@ namespace MarkdownDeep internal void AddFootnote(Block footnote) { - m_Footnotes[(string)footnote.data] = footnote; + m_Footnotes[(string)footnote.Data] = footnote; } // Look up a footnote, claim it and return it's index (or -1 if not found) @@ -618,9 +618,9 @@ namespace MarkdownDeep { m_StringScanner.Reset(str, start, len); var p = m_StringScanner; - while (!p.eof) + while (!p.Eof) { - char ch = p.current; + char ch = p.Current; switch (ch) { case '&': @@ -654,9 +654,9 @@ namespace MarkdownDeep m_StringScanner.Reset(str, start, len); var p = m_StringScanner; int pos = 0; - while (!p.eof) + while (!p.Eof) { - char ch = p.current; + char ch = p.Current; switch (ch) { case '\t': @@ -826,6 +826,13 @@ namespace MarkdownDeep set; } + + /// + /// Gets or sets a value indicating whether the parser is in DocNet mode. If set to true, it will handle '@' extensions added for DocNet as well as + /// collect H2 id elements. + /// + public bool DocNetMode { get; set; } + // When set, all html block level elements automatically support // markdown syntax within them. // (Similar to Pandoc's handling of markdown in html) @@ -924,7 +931,7 @@ namespace MarkdownDeep /// /// Collector for the created id's for H2 headers. First element in Tuple is id name, second is name for ToC (the text for H2). Id's are generated - /// by the parser and use pandoc algorithm, as AutoHeadingId's is switched on. + /// by the parser and use pandoc algorithm, as AutoHeadingId's is switched on. Only in use if DocNetMode is set to true /// public List> CreatedH2IdCollector { get; private set; } diff --git a/src/MarkdownDeep/SpanFormatter.cs b/src/MarkdownDeep/SpanFormatter.cs index 292c7cb..db534e4 100644 --- a/src/MarkdownDeep/SpanFormatter.cs +++ b/src/MarkdownDeep/SpanFormatter.cs @@ -137,18 +137,18 @@ namespace MarkdownDeep base.Reset(sb.ToString()); // Skip everything up to the first letter - while (!eof) + while (!Eof) { - if (Char.IsLetter(current)) + if (Char.IsLetter(Current)) break; SkipForward(1); } // Process all characters sb.Length = 0; - while (!eof) + while (!Eof) { - char ch = current; + char ch = Current; if (char.IsLetterOrDigit(ch) || ch=='_' || ch=='-' || ch=='.') sb.Append(Char.ToLower(ch)); else if (ch == ' ') @@ -262,6 +262,12 @@ namespace MarkdownDeep sb.Append(""); break; } + + case TokenType.font_awesome: + sb.Append(""); + break; } FreeToken(t); @@ -337,18 +343,18 @@ namespace MarkdownDeep bool ExtraMode = m_Markdown.ExtraMode; // Scan string - int start_text_token = position; - while (!eof) + int start_text_token = Position; + while (!Eof) { - int end_text_token=position; + int end_text_token=Position; // Work out token Token token = null; - switch (current) + int positionSave = Position; + switch (Current) { case '*': case '_': - // Create emphasis mark token = CreateEmphasisMark(); @@ -376,64 +382,55 @@ namespace MarkdownDeep case '[': case '!': - { // Process link reference - int linkpos = position; + int linkpos = Position; token = ProcessLinkOrImageOrFootnote(); // Rewind if invalid syntax // (the '[' or '!' will be treated as a regular character and processed below) if (token == null) - position = linkpos; + Position = linkpos; break; - } case '<': - { // Is it a valid html tag? - int save = position; HtmlTag tag = HtmlTag.Parse(this); if (tag != null) { if (!m_Markdown.SafeMode || tag.IsSafe()) { // Yes, create a token for it - token = CreateToken(TokenType.HtmlTag, save, position - save); + token = CreateToken(TokenType.HtmlTag, positionSave, Position - positionSave); } else { // No, rewrite and encode it - position = save; + Position = positionSave; } } else { // No, rewind and check if it's a valid autolink eg: - position = save; + Position = positionSave; token = ProcessAutoLink(); if (token == null) - position = save; + Position = positionSave; } break; - } case '&': - { // Is it a valid html entity - int save=position; string unused=null; if (SkipHtmlEntity(ref unused)) { // Yes, create a token for it - token = CreateToken(TokenType.Html, save, position - save); + token = CreateToken(TokenType.Html, positionSave, Position - positionSave); } break; - } case ' ': - { // Check for double space at end of a line if (CharAtOffset(1)==' ' && IsLineEnd(CharAtOffset(2))) { @@ -441,17 +438,15 @@ namespace MarkdownDeep SkipForward(2); // Don't put br's at the end of a paragraph - if (!eof) + if (!Eof) { SkipEol(); token = CreateToken(TokenType.br, end_text_token, 0); } } break; - } case '\\': - { // Special handling for escaping /* if (CharAtOffset(1) == '<') @@ -474,27 +469,44 @@ namespace MarkdownDeep // Check followed by an escapable character if (Utils.IsEscapableChar(CharAtOffset(1), ExtraMode)) { - token = CreateToken(TokenType.Text, position + 1, 1); + token = CreateToken(TokenType.Text, Position + 1, 1); SkipForward(2); } } break; - } + + case '@': + if(m_Markdown.DocNetMode) + { + if(this.DoesMatch("@fa-")) + { + // expect FontAwesome. + string iconName = string.Empty; + int newPosition = 0; + if(Utils.SkipFontAwesome(this.Input, this.Position, out newPosition, out iconName)) + { + // token should be just the iconname, so adjust position specification to that. + token = CreateToken(TokenType.font_awesome, newPosition - iconName.Length, iconName.Length); + this.Position = newPosition; + } + } + } + break; } // Look for abbreviations. if (token == null && Abbreviations!=null && !Char.IsLetterOrDigit(CharAtOffset(-1))) { - var savepos = position; + var savepos = Position; foreach (var abbr in Abbreviations) { - if (SkipString(abbr.Abbr) && !Char.IsLetterOrDigit(current)) + if (SkipString(abbr.Abbr) && !Char.IsLetterOrDigit(Current)) { token = CreateToken(TokenType.abbreviation, abbr); break; } - position = savepos; + Position = savepos; } } @@ -512,7 +524,7 @@ namespace MarkdownDeep m_Tokens.Add(token); // Remember where the next text token starts - start_text_token=position; + start_text_token=Position; } else { @@ -522,9 +534,9 @@ namespace MarkdownDeep } // Append a token for any trailing text after the last token. - if (position > start_text_token) + if (Position > start_text_token) { - m_Tokens.Add(CreateToken(TokenType.Text, start_text_token, position-start_text_token)); + m_Tokens.Add(CreateToken(TokenType.Text, start_text_token, Position-start_text_token)); } // Do we need to resolve and emphasis marks? @@ -532,9 +544,6 @@ namespace MarkdownDeep { ResolveEmphasisMarks(m_Tokens, emphasis_marks); } - - // Done! - return; } static bool IsEmphasisChar(char ch) @@ -559,61 +568,61 @@ namespace MarkdownDeep public Token CreateEmphasisMark() { // Capture current state - char ch = current; + char ch = Current; char altch = ch == '*' ? '_' : '*'; - int savepos = position; + int savepos = Position; // Check for a consecutive sequence of just '_' and '*' - if (bof || char.IsWhiteSpace(CharAtOffset(-1))) + if (Bof || char.IsWhiteSpace(CharAtOffset(-1))) { - while (IsEmphasisChar(current)) + while (IsEmphasisChar(Current)) SkipForward(1); - if (eof || char.IsWhiteSpace(current)) + if (Eof || char.IsWhiteSpace(Current)) { - return new Token(TokenType.Html, savepos, position - savepos); + return new Token(TokenType.Html, savepos, Position - savepos); } // Rewind - position = savepos; + Position = savepos; } // Scan backwards and see if we have space before while (IsEmphasisChar(CharAtOffset(-1))) SkipForward(-1); - bool bSpaceBefore = bof || char.IsWhiteSpace(CharAtOffset(-1)); - position = savepos; + bool bSpaceBefore = Bof || char.IsWhiteSpace(CharAtOffset(-1)); + Position = savepos; // Count how many matching emphasis characters - while (current == ch) + while (Current == ch) { SkipForward(1); } - int count=position-savepos; + int count=Position-savepos; // Scan forwards and see if we have space after while (IsEmphasisChar(CharAtOffset(1))) SkipForward(1); - bool bSpaceAfter = eof || char.IsWhiteSpace(current); - position = savepos + count; + bool bSpaceAfter = Eof || char.IsWhiteSpace(Current); + Position = savepos + count; // This should have been stopped by check above System.Diagnostics.Debug.Assert(!bSpaceBefore || !bSpaceAfter); if (bSpaceBefore) { - return CreateToken(TokenType.opening_mark, savepos, position - savepos); + return CreateToken(TokenType.opening_mark, savepos, Position - savepos); } if (bSpaceAfter) { - return CreateToken(TokenType.closing_mark, savepos, position - savepos); + return CreateToken(TokenType.closing_mark, savepos, Position - savepos); } - if (m_Markdown.ExtraMode && ch == '_' && (Char.IsLetterOrDigit(current))) + if (m_Markdown.ExtraMode && ch == '_' && (Char.IsLetterOrDigit(Current))) return null; - return CreateToken(TokenType.internal_mark, savepos, position - savepos); + return CreateToken(TokenType.internal_mark, savepos, Position - savepos); } // Split mark token @@ -656,7 +665,7 @@ namespace MarkdownDeep break; // Ignore if different type (ie: `*` vs `_`) - if (input[opening_mark.startOffset] != input[closing_mark.startOffset]) + if (Input[opening_mark.startOffset] != Input[closing_mark.startOffset]) continue; // strong or em? @@ -718,7 +727,7 @@ namespace MarkdownDeep continue; // Ignore if different type (ie: `*` vs `_`) - if (input[opening_mark.startOffset] != input[closing_mark.startOffset]) + if (Input[opening_mark.startOffset] != Input[closing_mark.startOffset]) continue; // Must be at least two @@ -764,7 +773,7 @@ namespace MarkdownDeep continue; // Ignore if different type (ie: `*` vs `_`) - if (input[opening_mark.startOffset] != input[closing_mark.startOffset]) + if (Input[opening_mark.startOffset] != Input[closing_mark.startOffset]) continue; // Split the opening mark, keeping the LHS @@ -884,9 +893,9 @@ namespace MarkdownDeep bool ExtraMode = m_Markdown.ExtraMode; // Allow anything up to the closing angle, watch for escapable characters - while (!eof) + while (!Eof) { - char ch = current; + char ch = Current; // No whitespace allowed if (char.IsWhiteSpace(ch)) @@ -945,7 +954,7 @@ namespace MarkdownDeep return null; // Is it a foonote? - var savepos=position; + var savepos=Position; if (m_Markdown.ExtraMode && token_type==TokenType.link && SkipChar('^')) { SkipLinespace(); @@ -964,7 +973,7 @@ namespace MarkdownDeep } // Rewind - position = savepos; + Position = savepos; } if (DisableLinks && token_type==TokenType.link) @@ -976,9 +985,9 @@ namespace MarkdownDeep // escapable characters Mark(); int depth = 1; - while (!eof) + while (!Eof) { - char ch = current; + char ch = Current; if (ch == '[') { depth++; @@ -994,7 +1003,7 @@ namespace MarkdownDeep } // Quit if end - if (eof) + if (Eof) return null; // Get the link text and unescape it @@ -1004,7 +1013,7 @@ namespace MarkdownDeep SkipForward(1); // Save position in case we need to rewind - savepos = position; + savepos = Position; // Inline links must follow immediately if (SkipChar('(')) @@ -1029,7 +1038,7 @@ namespace MarkdownDeep // If there's line end, we're allow it and as must line space as we want // before the link id. - if (eol) + if (Eol) { SkipEol(); SkipLinespace(); @@ -1037,7 +1046,7 @@ namespace MarkdownDeep // Reference link? string link_id = null; - if (current == '[') + if (Current == '[') { // Skip the opening '[' SkipForward(1); @@ -1056,7 +1065,7 @@ namespace MarkdownDeep else { // Rewind to just after the closing ']' - position = savepos; + Position = savepos; } // Link id not specified? @@ -1087,7 +1096,7 @@ namespace MarkdownDeep // Process a ``` code span ``` Token ProcessCodeSpan() { - int start = position; + int start = Position; // Count leading ticks int tickcount = 0; @@ -1100,23 +1109,23 @@ namespace MarkdownDeep SkipWhitespace(); // End? - if (eof) - return CreateToken(TokenType.Text, start, position - start); + if (Eof) + return CreateToken(TokenType.Text, start, Position - start); - int startofcode = position; + int startofcode = Position; // Find closing ticks if (!Find(Substring(start, tickcount))) - return CreateToken(TokenType.Text, start, position - start); + return CreateToken(TokenType.Text, start, Position - start); // Save end position before backing up over trailing whitespace - int endpos = position + tickcount; + int endpos = Position + tickcount; while (char.IsWhiteSpace(CharAtOffset(-1))) SkipForward(-1); // Create the token, move back to the end and we're done - var ret = CreateToken(TokenType.code_span, startofcode, position - startofcode); - position = endpos; + var ret = CreateToken(TokenType.code_span, startofcode, Position - startofcode); + Position = endpos; return ret; } diff --git a/src/MarkdownDeep/StringScanner.cs b/src/MarkdownDeep/StringScanner.cs index 7d3be3b..ceaebee 100644 --- a/src/MarkdownDeep/StringScanner.cs +++ b/src/MarkdownDeep/StringScanner.cs @@ -84,7 +84,7 @@ namespace MarkdownDeep } // Get the entire input string - public string input + public string Input { get { @@ -93,7 +93,7 @@ namespace MarkdownDeep } // Get the character at the current position - public char current + public char Current { get { @@ -105,7 +105,7 @@ namespace MarkdownDeep } // Get/set the current position - public int position + public int Position { get { @@ -119,11 +119,11 @@ namespace MarkdownDeep // Get the remainder of the input // (use this in a watch window while debugging :) - public string remainder + public string Remainder { get { - return Substring(position); + return Substring(Position); } } @@ -201,7 +201,7 @@ namespace MarkdownDeep // Skip a character if present public bool SkipChar(char ch) { - if (current == ch) + if (Current == ch) { SkipForward(1); return true; @@ -237,11 +237,11 @@ namespace MarkdownDeep // Skip any whitespace public bool SkipWhitespace() { - if (!char.IsWhiteSpace(current)) + if (!char.IsWhiteSpace(Current)) return false; SkipForward(1); - while (char.IsWhiteSpace(current)) + while (char.IsWhiteSpace(Current)) SkipForward(1); return true; @@ -256,11 +256,11 @@ namespace MarkdownDeep // Skip spaces and tabs public bool SkipLinespace() { - if (!IsLineSpace(current)) + if (!IsLineSpace(Current)) return false; SkipForward(1); - while (IsLineSpace(current)) + while (IsLineSpace(Current)) SkipForward(1); return true; @@ -269,7 +269,7 @@ namespace MarkdownDeep // Does current character match something public bool DoesMatch(char ch) { - return current == ch; + return Current == ch; } // Does character at offset match a character @@ -314,7 +314,7 @@ namespace MarkdownDeep // Does current string position match a string public bool DoesMatchI(string str) { - return string.Compare(str, Substring(position, str.Length), true) == 0; + return string.Compare(str, Substring(Position, str.Length), true) == 0; } // Extract a substring @@ -393,7 +393,7 @@ namespace MarkdownDeep } // Are we at eof? - public bool eof + public bool Eof { get { @@ -402,16 +402,16 @@ namespace MarkdownDeep } // Are we at eol? - public bool eol + public bool Eol { get { - return IsLineEnd(current); + return IsLineEnd(Current); } } // Are we at bof? - public bool bof + public bool Bof { get { @@ -437,7 +437,7 @@ namespace MarkdownDeep // Skip an identifier public bool SkipIdentifier(ref string identifier) { - int savepos = position; + int savepos = Position; if (!Utils.ParseIdentifier(this.str, ref pos, ref identifier)) return false; if (pos >= end) @@ -450,7 +450,7 @@ namespace MarkdownDeep public bool SkipFootnoteID(out string id) { - int savepos = position; + int savepos = Position; SkipLinespace(); @@ -458,14 +458,14 @@ namespace MarkdownDeep while (true) { - char ch = current; + char ch = Current; if (char.IsLetterOrDigit(ch) || ch == '-' || ch == '_' || ch == ':' || ch == '.' || ch == ' ') SkipForward(1); else break; } - if (position > mark) + if (Position > mark) { id = Extract().Trim(); if (!String.IsNullOrEmpty(id)) @@ -475,7 +475,7 @@ namespace MarkdownDeep } } - position = savepos; + Position = savepos; id = null; return false; } @@ -483,7 +483,7 @@ namespace MarkdownDeep // Skip a Html entity (eg: &) public bool SkipHtmlEntity(ref string entity) { - int savepos = position; + int savepos = Position; if (!Utils.SkipHtmlEntity(this.str, ref pos, ref entity)) return false; if (pos > end) diff --git a/src/MarkdownDeep/TableSpec.cs b/src/MarkdownDeep/TableSpec.cs index 774cea7..8fa2430 100644 --- a/src/MarkdownDeep/TableSpec.cs +++ b/src/MarkdownDeep/TableSpec.cs @@ -44,7 +44,7 @@ namespace MarkdownDeep { p.SkipLinespace(); - if (p.eol) + if (p.Eol) return null; // Blank line ends the table bool bAnyBars=LeadingBar; @@ -58,11 +58,11 @@ namespace MarkdownDeep // Parse all columns except the last - while (!p.eol) + while (!p.Eol) { // Find the next vertical bar p.Mark(); - while (!p.eol && p.current != '|') + while (!p.Eol && p.Current != '|') p.SkipEscapableChar(true); row.Add(p.Extract().Trim()); @@ -143,7 +143,7 @@ namespace MarkdownDeep p.SkipLinespace(); // Quick check for typical case - if (p.current != '|' && p.current != ':' && p.current != '-') + if (p.Current != '|' && p.Current != ':' && p.Current != '-') return null; // Don't create the spec until it at least looks like one @@ -164,11 +164,11 @@ namespace MarkdownDeep p.SkipLinespace(); // Must have something in the spec - if (p.current == '|') + if (p.Current == '|') return null; bool AlignLeft = p.SkipChar(':'); - while (p.current == '-') + while (p.Current == '-') p.SkipForward(1); bool AlignRight = p.SkipChar(':'); p.SkipLinespace(); @@ -182,7 +182,7 @@ namespace MarkdownDeep else if (AlignRight) col = ColumnAlignment.Right; - if (p.eol) + if (p.Eol) { // Not a spec? if (spec == null) @@ -206,7 +206,7 @@ namespace MarkdownDeep // Check for trailing vertical bar p.SkipLinespace(); - if (p.eol) + if (p.Eol) { spec.TrailingBar = true; return spec; diff --git a/src/MarkdownDeep/Token.cs b/src/MarkdownDeep/Token.cs index afdf2be..8478d97 100644 --- a/src/MarkdownDeep/Token.cs +++ b/src/MarkdownDeep/Token.cs @@ -52,6 +52,9 @@ namespace MarkdownDeep opening_mark, // opening '*' or '_' closing_mark, // closing '*' or '_' internal_mark, // internal '*' or '_' + + // DocNet Extensions + font_awesome, // } // Token diff --git a/src/MarkdownDeep/Utils.cs b/src/MarkdownDeep/Utils.cs index 2e8b820..a485a85 100644 --- a/src/MarkdownDeep/Utils.cs +++ b/src/MarkdownDeep/Utils.cs @@ -87,7 +87,7 @@ namespace MarkdownDeep identifer = str.Substring(startpos, pos - startpos); return true; } - + // Skip over anything that looks like a valid html entity (eg: &, {, &#nnn) etc... // Updates `pos` to character after the entity if matched public static bool SkipHtmlEntity(string str, ref int pos, ref string entity) @@ -322,7 +322,7 @@ namespace MarkdownDeep // Extension method. Skip an escapable character, or one normal character public static void SkipEscapableChar(this StringScanner p, bool ExtraMode) { - if (p.current == '\\' && IsEscapableChar(p.CharAtOffset(1), ExtraMode)) + if (p.Current == '\\' && IsEscapableChar(p.CharAtOffset(1), ExtraMode)) { p.SkipForward(2); } @@ -367,16 +367,16 @@ namespace MarkdownDeep StringBuilder sb = new StringBuilder(); StringScanner sp = new StringScanner(str); - while (!sp.eof) + while (!sp.Eof) { - if (sp.eol) + if (sp.Eol) { sb.Append('\n'); sp.SkipEol(); } else { - sb.Append(sp.current); + sb.Append(sp.Current); sp.SkipForward(1); } } @@ -491,5 +491,49 @@ namespace MarkdownDeep return url.Contains("://") || url.StartsWith("mailto:"); } + + /// + /// Skips the font awesome specification at the current specified pos. Pos is inside str at the '@'. font awesome + /// directives are @fa-iconname. newPos is positioned to character after iconname if successful match. iconname scanned is specified in iconname. + /// + /// The string. + /// The current position. + /// The new position. + /// Name of the icon. + /// + /// bool if match was found and properly skipped, otherwise false. + /// + public static bool SkipFontAwesome(string str, int currentPos, out int newPos, out string iconName) + { + newPos = currentPos; + iconName = string.Empty; + if(str[currentPos] != '@') + { + return false; + } + + var scanner = new StringScanner(str, currentPos); + // skip '@' + scanner.SkipForward(1); + if(!scanner.DoesMatch("fa-")) + { + // not a font awesome specification + return false; + } + scanner.SkipForward(3); + iconName = string.Empty; + if(!scanner.SkipIdentifier(ref iconName)) + { + // no icon name specified + return false; + } + if(string.IsNullOrWhiteSpace(iconName)) + { + return false; + } + // matched a fontawesome specification + newPos = scanner.Position; + return true; + } } } diff --git a/src/MarkdownDeepTests/BlockProcessorTests.cs b/src/MarkdownDeepTests/BlockProcessorTests.cs index 917733f..c8c1ac2 100644 --- a/src/MarkdownDeepTests/BlockProcessorTests.cs +++ b/src/MarkdownDeepTests/BlockProcessorTests.cs @@ -21,7 +21,7 @@ namespace MarkdownDeepTests { var b = p.Process("paragraph"); Assert.AreEqual(1, b.Count); - Assert.AreEqual(BlockType.p, b[0].blockType); + Assert.AreEqual(BlockType.p, b[0].BlockType); Assert.AreEqual("paragraph", b[0].Content); } @@ -30,7 +30,7 @@ namespace MarkdownDeepTests { var b = p.Process("l1\nl2\n\n"); Assert.AreEqual(1, b.Count); - Assert.AreEqual(BlockType.p, b[0].blockType); + Assert.AreEqual(BlockType.p, b[0].BlockType); Assert.AreEqual("l1\nl2", b[0].Content); } @@ -39,7 +39,7 @@ namespace MarkdownDeepTests { var b = p.Process("heading\n===\n\n"); Assert.AreEqual(1, b.Count); - Assert.AreEqual(BlockType.h1, b[0].blockType); + Assert.AreEqual(BlockType.h1, b[0].BlockType); Assert.AreEqual("heading", b[0].Content); } @@ -48,7 +48,7 @@ namespace MarkdownDeepTests { var b = p.Process("heading\n---\n\n"); Assert.AreEqual(1, b.Count); - Assert.AreEqual(BlockType.h2, b[0].blockType); + Assert.AreEqual(BlockType.h2, b[0].BlockType); Assert.AreEqual("heading", b[0].Content); } @@ -58,13 +58,13 @@ namespace MarkdownDeepTests var b = p.Process("p1\nheading\n---\np2\n"); Assert.AreEqual(3, b.Count); - Assert.AreEqual(BlockType.p, b[0].blockType); + Assert.AreEqual(BlockType.p, b[0].BlockType); Assert.AreEqual("p1", b[0].Content); - Assert.AreEqual(BlockType.h2, b[1].blockType); + Assert.AreEqual(BlockType.h2, b[1].BlockType); Assert.AreEqual("heading", b[1].Content); - Assert.AreEqual(BlockType.p, b[2].blockType); + Assert.AreEqual(BlockType.p, b[2].BlockType); Assert.AreEqual("p2", b[2].Content); } @@ -74,10 +74,10 @@ namespace MarkdownDeepTests var b = p.Process("#heading#\nparagraph\n"); Assert.AreEqual(2, b.Count); - Assert.AreEqual(BlockType.h1, b[0].blockType); + Assert.AreEqual(BlockType.h1, b[0].BlockType); Assert.AreEqual("heading", b[0].Content); - Assert.AreEqual(BlockType.p, b[1].blockType); + Assert.AreEqual(BlockType.p, b[1].BlockType); Assert.AreEqual("paragraph", b[1].Content); } @@ -88,13 +88,13 @@ namespace MarkdownDeepTests Assert.AreEqual(3, b.Count); - Assert.AreEqual(BlockType.p, b[0].blockType); + Assert.AreEqual(BlockType.p, b[0].BlockType); Assert.AreEqual("p1", b[0].Content); - Assert.AreEqual(BlockType.h2, b[1].blockType); + Assert.AreEqual(BlockType.h2, b[1].BlockType); Assert.AreEqual("heading", b[1].Content); - Assert.AreEqual(BlockType.p, b[2].blockType); + Assert.AreEqual(BlockType.p, b[2].BlockType); Assert.AreEqual("p2", b[2].Content); } @@ -107,7 +107,7 @@ namespace MarkdownDeepTests Block cb = b[0] as Block; Assert.AreEqual("code1\n\tcode2\ncode3\n", cb.Content); - Assert.AreEqual(BlockType.p, b[1].blockType); + Assert.AreEqual(BlockType.p, b[1].BlockType); Assert.AreEqual("paragraph", b[1].Content); } @@ -116,7 +116,7 @@ namespace MarkdownDeepTests { var b = p.Process("
    \n
    \n"); Assert.AreEqual(1, b.Count); - Assert.AreEqual(BlockType.html, b[0].blockType); + Assert.AreEqual(BlockType.html, b[0].BlockType); Assert.AreEqual("
    \n
    \n", b[0].Content); } @@ -125,7 +125,7 @@ namespace MarkdownDeepTests { var b = p.Process("\n"); Assert.AreEqual(1, b.Count); - Assert.AreEqual(BlockType.html, b[0].blockType); + Assert.AreEqual(BlockType.html, b[0].BlockType); Assert.AreEqual("\n", b[0].Content); } @@ -134,27 +134,27 @@ namespace MarkdownDeepTests { var b = p.Process("---\n"); Assert.AreEqual(1, b.Count); - Assert.AreEqual(BlockType.hr, b[0].blockType); + Assert.AreEqual(BlockType.hr, b[0].BlockType); b = p.Process("___\n"); Assert.AreEqual(1, b.Count); - Assert.AreEqual(BlockType.hr, b[0].blockType); + Assert.AreEqual(BlockType.hr, b[0].BlockType); b = p.Process("***\n"); Assert.AreEqual(1, b.Count); - Assert.AreEqual(BlockType.hr, b[0].blockType); + Assert.AreEqual(BlockType.hr, b[0].BlockType); b = p.Process(" - - - \n"); Assert.AreEqual(1, b.Count); - Assert.AreEqual(BlockType.hr, b[0].blockType); + Assert.AreEqual(BlockType.hr, b[0].BlockType); b = p.Process(" _ _ _ \n"); Assert.AreEqual(1, b.Count); - Assert.AreEqual(BlockType.hr, b[0].blockType); + Assert.AreEqual(BlockType.hr, b[0].BlockType); b = p.Process(" * * * \n"); Assert.AreEqual(1, b.Count); - Assert.AreEqual(BlockType.hr, b[0].blockType); + Assert.AreEqual(BlockType.hr, b[0].BlockType); } diff --git a/src/MarkdownDeepTests/DocNetMode.cs b/src/MarkdownDeepTests/DocNetMode.cs new file mode 100644 index 0000000..f6cdd3c --- /dev/null +++ b/src/MarkdownDeepTests/DocNetMode.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using MarkdownDeep; +using System.Reflection; + +namespace MarkdownDeepTests +{ + [TestFixture] + class DocNetModeTests + { + public static IEnumerable GetTests() + { + return Utils.GetTests("docnetmode"); + } + + + [Test, TestCaseSource("GetTests")] + public void Test(string resourceName) + { + Utils.RunResourceTest(resourceName); + } + } +} diff --git a/src/MarkdownDeepTests/MarkdownDeepTests.csproj b/src/MarkdownDeepTests/MarkdownDeepTests.csproj index d1602cb..1babcd2 100644 --- a/src/MarkdownDeepTests/MarkdownDeepTests.csproj +++ b/src/MarkdownDeepTests/MarkdownDeepTests.csproj @@ -70,6 +70,7 @@ + @@ -454,6 +455,18 @@ MarkdownDeep + + + + + + + + + + + +