Browse Source

Changes:

Cleaned up crappy public field naming/properties
Added @alert support
Added @fontawesome support
Added tests
tags/0.9.0
Frans Bouma 10 years ago
parent
commit
d0019cfb59
22 changed files with 880 additions and 493 deletions
  1. +2
    -1
      src/DocNet/Utils.cs
  2. +155
    -81
      src/MarkdownDeep/Block.cs
  3. +360
    -218
      src/MarkdownDeep/BlockProcessor.cs
  4. +8
    -8
      src/MarkdownDeep/HtmlTag.cs
  5. +20
    -20
      src/MarkdownDeep/LinkDefinition.cs
  6. +29
    -22
      src/MarkdownDeep/MardownDeep.cs
  7. +87
    -78
      src/MarkdownDeep/SpanFormatter.cs
  8. +22
    -22
      src/MarkdownDeep/StringScanner.cs
  9. +8
    -8
      src/MarkdownDeep/TableSpec.cs
  10. +3
    -0
      src/MarkdownDeep/Token.cs
  11. +49
    -5
      src/MarkdownDeep/Utils.cs
  12. +21
    -21
      src/MarkdownDeepTests/BlockProcessorTests.cs
  13. +26
    -0
      src/MarkdownDeepTests/DocNetMode.cs
  14. +13
    -0
      src/MarkdownDeepTests/MarkdownDeepTests.csproj
  15. +5
    -5
      src/MarkdownDeepTests/StringScannerTests.cs
  16. +1
    -0
      src/MarkdownDeepTests/Utils.cs
  17. +2
    -2
      src/MarkdownDeepTests/XssAttackTests.cs
  18. +24
    -0
      src/MarkdownDeepTests/testfiles/docnetmode/Alert(DocNetMode).html
  19. +24
    -0
      src/MarkdownDeepTests/testfiles/docnetmode/Alert(DocNetMode).txt
  20. +7
    -0
      src/MarkdownDeepTests/testfiles/docnetmode/FontAwesome(DocNetMode).html
  21. +12
    -0
      src/MarkdownDeepTests/testfiles/docnetmode/FontAwesome(DocNetMode).txt
  22. +2
    -2
      src/MarkdownDeepTests/testfiles/githubmode/FencedCodeBlocksAlt(GitHubMode).html

+ 2
- 1
src/DocNet/Utils.cs View File

@@ -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.


+ 155
- 81
src/MarkdownDeep/Block.cs View File

@@ -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
/// <summary>
/// Gets the absolute line start: if line start is 0, it's returning the content start.
/// </summary>
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<string, string>(id, h2ContentAsString));
}
else
{
m.SpanFormatter.Format(b, buf, contentStart, contentLen);
m.SpanFormatter.Format(b, Buf, ContentStart, ContentLen);
}
b.Append("</" + blockType.ToString() + ">\n");
b.Append("</" + BlockType.ToString() + ">\n");
break;

case BlockType.hr:
b.Append("<hr />\n");
return;
case BlockType.user_break:
return;

case BlockType.ol_li:
case BlockType.ul_li:
b.Append("<li>");
m.SpanFormatter.Format(b, buf, contentStart, contentLen);
m.SpanFormatter.Format(b, Buf, ContentStart, ContentLen);
b.Append("</li>\n");
break;

case BlockType.dd:
b.Append("<dd>");
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("</dd>\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 = "</code></pre>\n\n";

if(m.GitHubCodeBlocks && !string.IsNullOrWhiteSpace(dataArgument))
{
b.AppendFormat("<pre><code class=\"{0}\">", dataArgument);
if(dataArgument == "nohighlight")
{
b.Append("<pre class=\"nocode\">");
tagSuffix = "</pre>";
}
else
{
b.AppendFormat("<pre><code class=\"{0}\">", dataArgument);
}
}
else
{
b.Append("<pre><code>");
}
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("</code></pre>\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("<p>");
if (contentLen > 0)
if (ContentLen > 0)
{
m.SpanFormatter.Format(b, buf, contentStart, contentLen);
m.SpanFormatter.Format(b, Buf, ContentStart, ContentLen);
b.Append("&nbsp;");
}
b.Append((string)data);
b.Append((string)Data);
b.Append("</p>\n");
break;
// DocNet Extensions
case BlockType.font_awesome:
if(m.DocNetMode)
{
b.Append("<i class=\"fa fa-");
b.Append(this.Data as string ?? string.Empty);
b.Append("\"></i>");
}
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("</" + blockType.ToString() + ">\n");
b.Append("<" + BlockType.ToString() + ">");
m.SpanFormatter.Format(b, Buf, ContentStart, ContentLen);
b.Append("</" + BlockType.ToString() + ">\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("<div class=\"alert alert-");
b.Append(alertType);
b.Append("\"><span class=\"alert-title\"><i class=\"fa fa-");
b.Append(faIconName);
b.Append("\"></i>");
b.Append(title);
b.Append("</span>");
RenderChildren(m, b);
b.Append("</div>");
}

#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 ? "<null>" : c);
return BlockType.ToString() + " - " + (c==null ? "<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<Block> 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<Block> Children { get; set; }

#endregion
}
}

+ 360
- 218
src/MarkdownDeep/BlockProcessor.cs
File diff suppressed because it is too large
View File


+ 8
- 8
src/MarkdownDeep/HtmlTag.cs View File

@@ -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());


+ 20
- 20
src/MarkdownDeep/LinkDefinition.cs View File

@@ -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


+ 29
- 22
src/MarkdownDeep/MardownDeep.cs View File

@@ -178,28 +178,28 @@ namespace MarkdownDeep
var fn = m_UsedFootnotes[i];

sb.Append("<li id=\"fn:");
sb.Append((string)fn.data); // footnote id
sb.Append((string)fn.Data); // footnote id
sb.Append("\">\n");


// We need to get the return link appended to the last paragraph
// in the footnote
string strReturnLink = string.Format("<a href=\"#fnref:{0}\" rev=\"footnote\">&#8617;</a>", (string)fn.data);
string strReturnLink = string.Format("<a href=\"#fnref:{0}\" rev=\"footnote\">&#8617;</a>", (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;
}


/// <summary>
/// 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.
/// </summary>
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

/// <summary>
/// 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
/// </summary>
public List<Tuple<string, string>> CreatedH2IdCollector { get; private set; }



+ 87
- 78
src/MarkdownDeep/SpanFormatter.cs View File

@@ -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("</abbr>");
break;
}

case TokenType.font_awesome:
sb.Append("<i class=\"fa fa-");
sb.Append(str, t.startOffset, t.length);
sb.Append("\"></i>");
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: <google.com>
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 <autolinks>
/*
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;
}



+ 22
- 22
src/MarkdownDeep/StringScanner.cs View File

@@ -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: &amp;)
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)


+ 8
- 8
src/MarkdownDeep/TableSpec.cs View File

@@ -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;


+ 3
- 0
src/MarkdownDeep/Token.cs View File

@@ -52,6 +52,9 @@ namespace MarkdownDeep
opening_mark, // opening '*' or '_'
closing_mark, // closing '*' or '_'
internal_mark, // internal '*' or '_'

// DocNet Extensions
font_awesome, // <i class="fa fa-name"></i>
}

// Token


+ 49
- 5
src/MarkdownDeep/Utils.cs View File

@@ -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: &amp, &#123, &#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:");
}


/// <summary>
/// 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.
/// </summary>
/// <param name="str">The string.</param>
/// <param name="currentPos">The current position.</param>
/// <param name="newPos">The new position.</param>
/// <param name="iconName">Name of the icon.</param>
/// <returns>
/// bool if match was found and properly skipped, otherwise false.
/// </returns>
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;
}
}
}

+ 21
- 21
src/MarkdownDeepTests/BlockProcessorTests.cs View File

@@ -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("<div>\n</div>\n");
Assert.AreEqual(1, b.Count);
Assert.AreEqual(BlockType.html, b[0].blockType);
Assert.AreEqual(BlockType.html, b[0].BlockType);
Assert.AreEqual("<div>\n</div>\n", b[0].Content);
}

@@ -125,7 +125,7 @@ namespace MarkdownDeepTests
{
var b = p.Process("<!-- this is a\ncomments -->\n");
Assert.AreEqual(1, b.Count);
Assert.AreEqual(BlockType.html, b[0].blockType);
Assert.AreEqual(BlockType.html, b[0].BlockType);
Assert.AreEqual("<!-- this is a\ncomments -->\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);
}




+ 26
- 0
src/MarkdownDeepTests/DocNetMode.cs View File

@@ -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<TestCaseData> GetTests()
{
return Utils.GetTests("docnetmode");
}


[Test, TestCaseSource("GetTests")]
public void Test(string resourceName)
{
Utils.RunResourceTest(resourceName);
}
}
}

+ 13
- 0
src/MarkdownDeepTests/MarkdownDeepTests.csproj View File

@@ -70,6 +70,7 @@
<Compile Include="CodeSpanTests.cs" />
<Compile Include="AutoLinkTests.cs" />
<Compile Include="AutoHeaderIDTests.cs" />
<Compile Include="DocNetMode.cs" />
<Compile Include="GithubMode.cs" />
<Compile Include="TableSpecTests.cs" />
<Compile Include="ExtraMode.cs" />
@@ -454,6 +455,18 @@
<Name>MarkdownDeep</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="testfiles\docnetmode\FontAwesome%28DocNetMode%29.html" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="testfiles\docnetmode\FontAwesome%28DocNetMode%29.txt" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="testfiles\docnetmode\Alert%28DocNetMode%29.txt" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="testfiles\docnetmode\Alert%28DocNetMode%29.html" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.


+ 5
- 5
src/MarkdownDeepTests/StringScannerTests.cs View File

@@ -16,11 +16,11 @@ namespace MarkdownDeepTests
var p = new StringScanner();

p.Reset("This is a string with something [bracketed]");
Assert.IsTrue(p.bof);
Assert.IsFalse(p.eof);
Assert.IsTrue(p.Bof);
Assert.IsFalse(p.Eof);
Assert.IsTrue(p.SkipString("This"));
Assert.IsFalse(p.bof);
Assert.IsFalse(p.eof);
Assert.IsFalse(p.Bof);
Assert.IsFalse(p.Eof);
Assert.IsFalse(p.SkipString("huh?"));
Assert.IsTrue(p.SkipLinespace());
Assert.IsTrue(p.SkipChar('i'));
@@ -37,7 +37,7 @@ namespace MarkdownDeepTests
Assert.IsTrue(p.Find(']'));
Assert.AreEqual("bracketed", p.Extract());
Assert.IsTrue(p.SkipChar(']'));
Assert.IsTrue(p.eof);
Assert.IsTrue(p.Eof);
}
}
}

+ 1
- 0
src/MarkdownDeepTests/Utils.cs View File

@@ -125,6 +125,7 @@ namespace MarkdownDeepTests
md.SafeMode = resourceName.IndexOf("(SafeMode)") >= 0;
md.ExtraMode = resourceName.IndexOf("(ExtraMode)") >= 0;
md.GitHubCodeBlocks = resourceName.IndexOf("(GitHubMode)") >= 0;
md.DocNetMode = resourceName.IndexOf("(DocNetMode") >= 0;
md.MarkdownInHtml = resourceName.IndexOf("(MarkdownInHtml)") >= 0;
md.AutoHeadingIDs = resourceName.IndexOf("(AutoHeadingIDs)") >= 0;



+ 2
- 2
src/MarkdownDeepTests/XssAttackTests.cs View File

@@ -87,7 +87,7 @@ namespace MarkdownDeepTests
{
StringScanner p = new StringScanner(input);

while (!p.eof)
while (!p.Eof)
{
HtmlTag tag=HtmlTag.Parse(p);
if (tag!=null)
@@ -117,7 +117,7 @@ namespace MarkdownDeepTests
{
StringScanner p = new StringScanner(input);

while (!p.eof)
while (!p.Eof)
{
HtmlTag tag = HtmlTag.Parse(p);
if (tag != null)


+ 24
- 0
src/MarkdownDeepTests/testfiles/docnetmode/Alert(DocNetMode).html View File

@@ -0,0 +1,24 @@
<p>pre</p>

<p>This is some text</p>

<div class="alert alert-danger">
<span class="alert-title"><i class="fa fa-times-circle"></i>Danger!</span><p>This is a dangerous text, it will be displayed in a danger alert box!</p>
</div>

<div class="alert alert-warning">
<span class="alert-title"><i class="fa fa-warning"></i>Warning!</span><p>This is a warning text, it will be displayed in a warning alert box!</p>
</div>

<div class="alert alert-info">
<span class="alert-title"><i class="fa fa-info-circle"></i>Info</span><p>This is an info text, it will be displayed in an info alert box!</p>
</div>

<div class="alert alert-neutral">
<span class="alert-title"><i class="fa fa-info-circle"></i>Info</span><p>This is an info text, it will be displayed in an info alert box!</p>
</div>

<p>@alert
This is an info text, it will be displayed in an info alert box!
@end</p>
<p>post</p>

+ 24
- 0
src/MarkdownDeepTests/testfiles/docnetmode/Alert(DocNetMode).txt View File

@@ -0,0 +1,24 @@
pre

This is some text
@alert danger
This is a dangerous text, it will be displayed in a danger alert box!
@end

@alert warning
This is a warning text, it will be displayed in a warning alert box!
@end

@alert info
This is an info text, it will be displayed in an info alert box!
@end

@alert neutral
This is an info text, it will be displayed in an info alert box!
@end

@alert
This is an info text, it will be displayed in an info alert box!
@end

post

+ 7
- 0
src/MarkdownDeepTests/testfiles/docnetmode/FontAwesome(DocNetMode).html View File

@@ -0,0 +1,7 @@
<p>pre</p>

<p>This is some text with a <i class="fa fa-github"></i> font awesome icon in it! <code>@fa-github</code>.</p>

<i class="fa fa-github"></i><i class="fa fa-github"></i><i class="fa fa-github"></i><p>a <i class="fa fa-github"></i></p>

<p>post</p>

+ 12
- 0
src/MarkdownDeepTests/testfiles/docnetmode/FontAwesome(DocNetMode).txt View File

@@ -0,0 +1,12 @@
pre

This is some text with a @fa-github font awesome icon in it! `@fa-github`.

@fa-github

@fa-github
@fa-github

a @fa-github
post

+ 2
- 2
src/MarkdownDeepTests/testfiles/githubmode/FencedCodeBlocksAlt(GitHubMode).html View File

@@ -24,10 +24,10 @@ line 3

</code></pre>

<pre><code class="nohighlight">This is a fenced nohighlight code block
<pre class="nocode">This is a fenced nohighlight code block
line 2
line 3
</code></pre>
</pre>

<pre><code class="cs">
This is a fenced c# code block with blank lines around it


Loading…
Cancel
Save