From 01ac99c46aff602808a7c295d3ecfeb92d611928 Mon Sep 17 00:00:00 2001 From: Griboedoff Date: Sat, 19 Nov 2016 11:53:08 +0500 Subject: [PATCH 1/8] start 2 task --- Markdown/Md.cs | 4 +++- Markdown/Test/MD_Should.cs | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Markdown/Md.cs b/Markdown/Md.cs index 229d5dda3..cc902b234 100644 --- a/Markdown/Md.cs +++ b/Markdown/Md.cs @@ -11,10 +11,12 @@ public class Md private readonly Dictionary> mdTagParserFuncMatch; private readonly Dictionary> validateFunctions; + private readonly string baseUrl; - public Md(string plainMd) + public Md(string plainMd, string baseUrl = "") { this.plainMd = plainMd; + this.baseUrl = baseUrl; mdTagParserFuncMatch = new Dictionary> { [Tag.Em] = ParseEmToken, diff --git a/Markdown/Test/MD_Should.cs b/Markdown/Test/MD_Should.cs index 71458e48f..671b94456 100644 --- a/Markdown/Test/MD_Should.cs +++ b/Markdown/Test/MD_Should.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Linq.Expressions; using System.Text; using FluentAssertions; using NUnit.Framework; From 4749f92ee47ad637103908e1b23ef2b18a189586 Mon Sep 17 00:00:00 2001 From: Griboedoff Date: Tue, 22 Nov 2016 00:17:58 +0500 Subject: [PATCH 2/8] add simple url parsing --- Chess/ChessProblem_Test.cs | 110 ++++++++++++------------- ControlDigit/ControlDigitExtensions.cs | 82 +++++++++--------- Markdown/HtmlToken.cs | 41 --------- Markdown/Markdown.csproj | 10 ++- Markdown/Md.cs | 89 +++++++++++++++----- Markdown/MdParserException.cs | 11 +++ Markdown/Tag.cs | 1 + Markdown/Test/HtmlToken_Should.cs | 12 +-- Markdown/Test/MD_Should.cs | 8 +- Markdown/Tokens/AHtmlToken.cs | 35 ++++++++ Markdown/Tokens/EmHtmlToken.cs | 18 ++++ Markdown/Tokens/EmptyHtmlToken.cs | 17 ++++ Markdown/Tokens/HtmlToken.cs | 44 ++++++++++ Markdown/Tokens/StrongHtmlToken.cs | 18 ++++ 14 files changed, 330 insertions(+), 166 deletions(-) delete mode 100644 Markdown/HtmlToken.cs create mode 100644 Markdown/MdParserException.cs create mode 100644 Markdown/Tokens/AHtmlToken.cs create mode 100644 Markdown/Tokens/EmHtmlToken.cs create mode 100644 Markdown/Tokens/EmptyHtmlToken.cs create mode 100644 Markdown/Tokens/HtmlToken.cs create mode 100644 Markdown/Tokens/StrongHtmlToken.cs diff --git a/Chess/ChessProblem_Test.cs b/Chess/ChessProblem_Test.cs index dd559b3d7..fc3c9beee 100644 --- a/Chess/ChessProblem_Test.cs +++ b/Chess/ChessProblem_Test.cs @@ -1,55 +1,55 @@ -using System; -using System.IO; -using NUnit.Framework; - -namespace Chess -{ - [TestFixture] - public class ChessProblem_Test - { - private static void TestOnFile(string filename) - { - var board = File.ReadAllLines(filename); - ChessProblem.LoadFrom(board); - var expectedAnswer = File.ReadAllText(Path.ChangeExtension(filename, ".ans")).Trim(); - ChessProblem.CalculateChessStatus(); - Assert.AreEqual(expectedAnswer, ChessProblem.ChessStatus.ToString().ToLower(), "Failed test " + filename); - } - - [Test] - public void RepeatedMethodCallDoNotChangeBehaviour() - { - var board = new[] - { - " ", - " ", - " ", - " q ", - " K ", - " Q ", - " ", - " ", - }; - ChessProblem.LoadFrom(board); - ChessProblem.CalculateChessStatus(); - Assert.AreEqual(ChessStatus.Check, ChessProblem.ChessStatus); - - // Now check that internal board modifictions during the first call do not change answer - ChessProblem.CalculateChessStatus(); - Assert.AreEqual(ChessStatus.Check, ChessProblem.ChessStatus); - } - - [Test] - public void FullTests() - { - var dir = TestContext.CurrentContext.TestDirectory; - var testsCount = 0; - foreach (var filename in Directory.GetFiles(Path.Combine(dir, "ChessTests"), "*.in")) - { - TestOnFile(filename); - testsCount++; - } - Console.WriteLine("Tests passed: " + testsCount); - } - } -} \ No newline at end of file +//using System; +//using System.IO; +//using NUnit.Framework; +// +//namespace Chess +//{ +// [TestFixture] +// public class ChessProblem_Test +// { +// private static void TestOnFile(string filename) +// { +// var board = File.ReadAllLines(filename); +// ChessProblem.LoadFrom(board); +// var expectedAnswer = File.ReadAllText(Path.ChangeExtension(filename, ".ans")).Trim(); +// ChessProblem.CalculateChessStatus(); +// Assert.AreEqual(expectedAnswer, ChessProblem.ChessStatus.ToString().ToLower(), "Failed test " + filename); +// } +// +// [Test] +// public void RepeatedMethodCallDoNotChangeBehaviour() +// { +// var board = new[] +// { +// " ", +// " ", +// " ", +// " q ", +// " K ", +// " Q ", +// " ", +// " ", +// }; +// ChessProblem.LoadFrom(board); +// ChessProblem.CalculateChessStatus(); +// Assert.AreEqual(ChessStatus.Check, ChessProblem.ChessStatus); +// +// // Now check that internal board modifictions during the first call do not change answer +// ChessProblem.CalculateChessStatus(); +// Assert.AreEqual(ChessStatus.Check, ChessProblem.ChessStatus); +// } +// +// [Test] +// public void FullTests() +// { +// var dir = TestContext.CurrentContext.TestDirectory; +// var testsCount = 0; +// foreach (var filename in Directory.GetFiles(Path.Combine(dir, "ChessTests"), "*.in")) +// { +// TestOnFile(filename); +// testsCount++; +// } +// Console.WriteLine("Tests passed: " + testsCount); +// } +// } +//} \ No newline at end of file diff --git a/ControlDigit/ControlDigitExtensions.cs b/ControlDigit/ControlDigitExtensions.cs index 807fc087c..d3291bdac 100644 --- a/ControlDigit/ControlDigitExtensions.cs +++ b/ControlDigit/ControlDigitExtensions.cs @@ -32,45 +32,45 @@ public static int ControlDigit2(this long number) } } - [TestFixture] - public class ControlDigitExtensions_Tests - { - [TestCase(0, ExpectedResult = 0)] - [TestCase(1, ExpectedResult = 1)] - [TestCase(2, ExpectedResult = 2)] - [TestCase(9, ExpectedResult = 9)] - [TestCase(10, ExpectedResult = 3)] - [TestCase(15, ExpectedResult = 8)] - [TestCase(17, ExpectedResult = 1)] - [TestCase(18, ExpectedResult = 0)] - public int TestControlDigit(long x) - { - return x.ControlDigit(); - } - - [Test] - public void CompareImplementations() - { - for (long i = 0; i < 100000; i++) - Assert.AreEqual(i.ControlDigit(), i.ControlDigit2()); - } - } - - [TestFixture] - public class ControlDigit_PerformanceTests - { - [Test] - public void TestControlDigitSpeed() - { - var count = 10000000; - var sw = Stopwatch.StartNew(); - for (int i = 0; i < count; i++) - 12345678L.ControlDigit(); - Console.WriteLine("Old " + sw.Elapsed); - sw.Restart(); - for (int i = 0; i < count; i++) - 12345678L.ControlDigit2(); - Console.WriteLine("New " + sw.Elapsed); - } - } +// [TestFixture] +// public class ControlDigitExtensions_Tests +// { +// [TestCase(0, ExpectedResult = 0)] +// [TestCase(1, ExpectedResult = 1)] +// [TestCase(2, ExpectedResult = 2)] +// [TestCase(9, ExpectedResult = 9)] +// [TestCase(10, ExpectedResult = 3)] +// [TestCase(15, ExpectedResult = 8)] +// [TestCase(17, ExpectedResult = 1)] +// [TestCase(18, ExpectedResult = 0)] +// public int TestControlDigit(long x) +// { +// return x.ControlDigit(); +// } +// +// [Test] +// public void CompareImplementations() +// { +// for (long i = 0; i < 100000; i++) +// Assert.AreEqual(i.ControlDigit(), i.ControlDigit2()); +// } +// } +// +// [TestFixture] +// public class ControlDigit_PerformanceTests +// { +// [Test] +// public void TestControlDigitSpeed() +// { +// var count = 10000000; +// var sw = Stopwatch.StartNew(); +// for (int i = 0; i < count; i++) +// 12345678L.ControlDigit(); +// Console.WriteLine("Old " + sw.Elapsed); +// sw.Restart(); +// for (int i = 0; i < count; i++) +// 12345678L.ControlDigit2(); +// Console.WriteLine("New " + sw.Elapsed); +// } +// } } diff --git a/Markdown/HtmlToken.cs b/Markdown/HtmlToken.cs deleted file mode 100644 index 9cfe3fc97..000000000 --- a/Markdown/HtmlToken.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Markdown -{ - public class HtmlToken - { - private readonly Tag tag; - private readonly List parsedTokens; - private readonly string data; - private bool IsTagged => !tag.Equals(Tag.Empty); - public int Length => parsedTokens.Sum(x => x.Length) + (data ?? "").Length + escapedCharacters + tag.Md.Length * 2; - private readonly int escapedCharacters; - - public HtmlToken(Tag tag, string data, int escapedCharacters) - { - this.tag = tag; - this.data = data; - this.escapedCharacters = escapedCharacters; - parsedTokens = new List(); - } - - public HtmlToken(Tag tag, List parsedTokens, int escapedCharacters) - { - this.tag = tag; - this.parsedTokens = parsedTokens; - this.escapedCharacters = escapedCharacters; - } - - private string InsertInToTags(string dataToInsert) => IsTagged - ? $"<{tag.Html}>{dataToInsert}" - : dataToInsert; - - public override string ToString() - { - return parsedTokens.Count > 0 - ? InsertInToTags(string.Join("", parsedTokens.Select(token => token.ToString()))) - : InsertInToTags(data); - } - } -} \ No newline at end of file diff --git a/Markdown/Markdown.csproj b/Markdown/Markdown.csproj index bfccc7366..41f5f64c3 100644 --- a/Markdown/Markdown.csproj +++ b/Markdown/Markdown.csproj @@ -39,6 +39,9 @@ ..\packages\FluentAssertions.4.16.0\lib\net45\FluentAssertions.Core.dll + + ..\..\..\..\..\Library\Frameworks\Mono.framework\Versions\4.8.0\lib\mono\4.5-api\Microsoft.CSharp.dll + ..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll @@ -51,9 +54,14 @@ - + + + + + + diff --git a/Markdown/Md.cs b/Markdown/Md.cs index cc902b234..cf95f9efe 100644 --- a/Markdown/Md.cs +++ b/Markdown/Md.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Markdown.Tokens; namespace Markdown { @@ -11,17 +12,17 @@ public class Md private readonly Dictionary> mdTagParserFuncMatch; private readonly Dictionary> validateFunctions; - private readonly string baseUrl; - public Md(string plainMd, string baseUrl = "") + + public Md(string plainMd) { this.plainMd = plainMd; - this.baseUrl = baseUrl; mdTagParserFuncMatch = new Dictionary> { [Tag.Em] = ParseEmToken, [Tag.Empty] = ParseNoMarkup, - [Tag.Strong] = ParseStrongToken + [Tag.Strong] = ParseStrongToken, + [Tag.A] = ParseUrl }; validateFunctions = new Dictionary> { @@ -61,8 +62,8 @@ private HtmlToken ParseEmToken(int index, string alreadyParsed = "", int already } return index != plainMd.Length - ? new HtmlToken(Tag.Em, tokenData.ToString(), alreadyEscaped) - : new HtmlToken(Tag.Empty, tokenData.Insert(0, '_').ToString(), alreadyEscaped); + ? (HtmlToken) new EmHtmlToken(tokenData.ToString(), alreadyEscaped) + : new EmptyHtmlToken(tokenData.Insert(0, '_').ToString(), alreadyEscaped); } private HtmlToken ParseStrongToken(int index, string alreadyParsed = "", int alreadyEscaped = 0) @@ -95,16 +96,16 @@ private HtmlToken ParseStrongToken(int index, string alreadyParsed = "", int alr index++; } - parsedTokens.Add(new HtmlToken(Tag.Empty, tokenData.ToString(), alreadyEscaped)); + parsedTokens.Add(new EmptyHtmlToken(tokenData.ToString(), alreadyEscaped)); return index != plainMd.Length - ? new HtmlToken(Tag.Strong, parsedTokens, 0) - : new HtmlToken(Tag.Empty, tokenData.Insert(0, "__").ToString(), alreadyEscaped); + ? (HtmlToken) new StrongHtmlToken(parsedTokens, 0) + : new EmptyHtmlToken(tokenData.Insert(0, "__").ToString(), alreadyEscaped); } private HtmlToken ParseEmInStrong(ref int index, ref int alreadyEscaped, ICollection parsedTokens, StringBuilder tokenData) { - parsedTokens.Add(new HtmlToken(Tag.Empty, tokenData.ToString(), alreadyEscaped)); + parsedTokens.Add(new EmptyHtmlToken(tokenData.ToString(), alreadyEscaped)); alreadyEscaped = 0; tokenData.Clear(); var htmlToken = ParseEmToken(index); @@ -130,7 +131,62 @@ private HtmlToken ParseNoMarkup(int index, string alreadyParsed = "", int alread tokenData.Append(plainMd[index]); index++; } - return new HtmlToken(Tag.Empty, tokenData.ToString(), escaped); + return new EmptyHtmlToken(tokenData.ToString(), escaped); + } + + private HtmlToken ParseUrl(int index, string alreadyParsed = "", int alreadyEscaped = 0) + { + var url = new StringBuilder(); + var returnedValue = ParseInsideBracers(']', index, alreadyEscaped, alreadyParsed); + var escaped = returnedValue.Escaped; + index = returnedValue.Index; + var urlText = returnedValue.Data; + + if (plainMd[index] == '(') + { + returnedValue = ParseInsideBracers(')', index, alreadyEscaped, alreadyParsed); + index = returnedValue.Index; + return new AHtmlToken((string) urlText, returnedValue.Data, escaped + returnedValue.Escaped); + } + + throw new MdParserException($"Can't parse link at index {index}"); + } + + private dynamic ParseInsideBracers(char closeBracer, int index, int escaped, string alreadyParsed) + { + var data = new StringBuilder(alreadyParsed); + index++; + while (index < plainMd.Length && plainMd[index] != closeBracer) + { + if (plainMd[index] == '\\') + { + index++; + escaped++; + } + data.Append(plainMd[index]); + index++; + } + index++; + var dataStr = data.ToString(); + return new + { + Index = index, + Escaped = escaped, + Data = dataStr + }; + } + + private Tag ParseTag(int tagIndex) + { + if (plainMd[tagIndex] == '[') + return Tag.A; + if (plainMd[tagIndex] == '_') + { + if (tagIndex != plainMd.Length - 1) + return plainMd[tagIndex + 1] == '_' ? Tag.Strong : Tag.Em; + return Tag.Em; + } + return Tag.Empty; } private bool NotInsideDigits(int tagIndex) @@ -171,17 +227,6 @@ private bool IsValidStrongTag(int tagIndex, bool isOpenTag) && NoSpaceNearMdTag(tagIndex, 2, isOpenTag); } - private Tag ParseTag(int tagIndex) - { - if (plainMd[tagIndex] == '_') - { - if (tagIndex != plainMd.Length - 1) - return plainMd[tagIndex + 1] == '_' ? Tag.Strong : Tag.Em; - return Tag.Em; - } - return Tag.Empty; - } - private IEnumerable TryParseToHtml() { var i = 0; diff --git a/Markdown/MdParserException.cs b/Markdown/MdParserException.cs new file mode 100644 index 000000000..792c18634 --- /dev/null +++ b/Markdown/MdParserException.cs @@ -0,0 +1,11 @@ +using System; + +namespace Markdown +{ + public class MdParserException : Exception + { + public MdParserException(string message) : base(message) + { + } + } +} \ No newline at end of file diff --git a/Markdown/Tag.cs b/Markdown/Tag.cs index 13e6a44a0..b54f63941 100644 --- a/Markdown/Tag.cs +++ b/Markdown/Tag.cs @@ -10,6 +10,7 @@ public class Tag public static readonly Tag Em = new Tag("_", "em"); public static readonly Tag Strong = new Tag("__", "strong"); public static readonly Tag Empty = new Tag("", ""); + public static readonly Tag A = new Tag("", "a"); private static readonly List Tags = new List {Em, Strong, Empty}; private Tag(string md, string html) diff --git a/Markdown/Test/HtmlToken_Should.cs b/Markdown/Test/HtmlToken_Should.cs index 4eb05377b..f18dac208 100644 --- a/Markdown/Test/HtmlToken_Should.cs +++ b/Markdown/Test/HtmlToken_Should.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using NUnit.Framework; using FluentAssertions; +using Markdown.Tokens; + namespace Markdown.Test { [TestFixture] @@ -9,7 +11,7 @@ public class HtmlToken_Should [Test] public void ShouldInsertDataInToTags_WhenToStringCalls() { - var token = new HtmlToken(Tag.Em, "data", 0); + var token = new EmHtmlToken("data", 0); token.ToString().Should().Be("data"); } @@ -19,12 +21,12 @@ public void ShouldConcatManyTags_WhenHasInsertedTags() { var tokenList = new List { - new HtmlToken(Tag.Em, "italic", 0), - new HtmlToken(Tag.Empty, "empty", 0), - new HtmlToken(Tag.Strong, "bold", 0) + new EmHtmlToken("italic", 0), + new EmptyHtmlToken("empty", 0), + new StrongHtmlToken("bold", 0) }; - var token = new HtmlToken(Tag.Strong, tokenList, 0); + var token = new StrongHtmlToken(tokenList, 0); token.ToString().Should().Be("italicemptybold"); } } diff --git a/Markdown/Test/MD_Should.cs b/Markdown/Test/MD_Should.cs index 671b94456..83bb36cb9 100644 --- a/Markdown/Test/MD_Should.cs +++ b/Markdown/Test/MD_Should.cs @@ -76,6 +76,12 @@ public string ParseMixedTagsCorrectrly(string plainMd) return new Md(plainMd).Render(); } + [TestCase("[url](www.url.com)", ExpectedResult = "url")] + public string ParseUrlTagCorrectrly(string plainMd) + { + return new Md(plainMd).Render(); + } + private static string GenerateMd(Tag tag, int length) { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; @@ -107,7 +113,7 @@ public void PerformanceTest() var parser = new Md(plainMd); parseWatch.Start(); - var html = parser.Render(); + parser.Render(); parseWatch.Stop(); Console.WriteLine( diff --git a/Markdown/Tokens/AHtmlToken.cs b/Markdown/Tokens/AHtmlToken.cs new file mode 100644 index 000000000..b6065ed8e --- /dev/null +++ b/Markdown/Tokens/AHtmlToken.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +namespace Markdown.Tokens +{ + public class AHtmlToken : HtmlToken + { + private readonly string url; + private readonly bool isReferece; + public override int Length => Data.Length + url.Length + 4 + escapedCharacters; + public static readonly Dictionary ReferenceUrlsBase = new Dictionary(); + + public AHtmlToken(string data, string url, int escapedCharacters) : base("a", data, escapedCharacters) + { + this.url = url; + this.isReferece = isReferece; + } + + public AHtmlToken(List parsedTokens, int escapedCharacters) + : base("a", parsedTokens, escapedCharacters) + { + } + + public static void AddReferenceUrlBase(string url, string name) + { + ReferenceUrlsBase[name] = url; + } + + public override string ToString() + { + if (!isReferece) + return $"{Data}"; + return ""; + } + } +} \ No newline at end of file diff --git a/Markdown/Tokens/EmHtmlToken.cs b/Markdown/Tokens/EmHtmlToken.cs new file mode 100644 index 000000000..8a5a40a1b --- /dev/null +++ b/Markdown/Tokens/EmHtmlToken.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Markdown.Tokens +{ + public class EmHtmlToken : HtmlToken + { + public override int Length => base.Length + 2; + + public EmHtmlToken(string data, int escapedCharacters) : base("em", data, escapedCharacters) + { + } + + public EmHtmlToken(List parsedTokens, int escapedCharacters) + : base("em", parsedTokens, escapedCharacters) + { + } + } +} \ No newline at end of file diff --git a/Markdown/Tokens/EmptyHtmlToken.cs b/Markdown/Tokens/EmptyHtmlToken.cs new file mode 100644 index 000000000..8f3655817 --- /dev/null +++ b/Markdown/Tokens/EmptyHtmlToken.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Markdown.Tokens +{ + public class EmptyHtmlToken : HtmlToken + { + public EmptyHtmlToken(string data, int escapedCharacters) : base("", data, escapedCharacters) + { + } + + public EmptyHtmlToken(List parsedTokens, int escapedCharacters) + : base("", parsedTokens, escapedCharacters) + { + } + } +} \ No newline at end of file diff --git a/Markdown/Tokens/HtmlToken.cs b/Markdown/Tokens/HtmlToken.cs new file mode 100644 index 000000000..4e98ae105 --- /dev/null +++ b/Markdown/Tokens/HtmlToken.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Markdown.Tokens +{ + public abstract class HtmlToken + { + protected readonly string Tag; + protected readonly List ParsedTokens; + protected readonly string Data; + + protected virtual bool IsTagged => !string.IsNullOrEmpty(Tag); + + public virtual int Length => ParsedTokens.Sum(x => x.Length) + (Data ?? "").Length + escapedCharacters; + + protected readonly int escapedCharacters; + + protected HtmlToken(string tag, string data, int escapedCharacters) + { + Tag = tag; + Data = data; + this.escapedCharacters = escapedCharacters; + ParsedTokens = new List(); + } + + protected HtmlToken(string tag, List parsedTokens, int escapedCharacters) + { + Tag = tag; + ParsedTokens = parsedTokens; + this.escapedCharacters = escapedCharacters; + } + + protected virtual string InsertInToTags(string dataToInsert) => IsTagged + ? $"<{Tag}>{dataToInsert}" + : dataToInsert; + + public override string ToString() + { + return ParsedTokens.Count > 0 + ? InsertInToTags(string.Join("", ParsedTokens.Select(token => token.ToString()))) + : InsertInToTags(Data); + } + } +} \ No newline at end of file diff --git a/Markdown/Tokens/StrongHtmlToken.cs b/Markdown/Tokens/StrongHtmlToken.cs new file mode 100644 index 000000000..8ed4ebbcc --- /dev/null +++ b/Markdown/Tokens/StrongHtmlToken.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Markdown.Tokens +{ + public class StrongHtmlToken : HtmlToken + { + public override int Length => base.Length + 4; + + public StrongHtmlToken(string data, int escapedCharacters) : base("strong", data, escapedCharacters) + { + } + + public StrongHtmlToken(List parsedTokens, int escapedCharacters) + : base("strong", parsedTokens, escapedCharacters) + { + } + } +} \ No newline at end of file From 6d74b925383344f5cacf3f84cc0bbc2ab79c8000 Mon Sep 17 00:00:00 2001 From: Griboedoff Date: Tue, 22 Nov 2016 11:54:55 +0500 Subject: [PATCH 3/8] add css and building urls --- Markdown/CssClassInfo.cs | 14 ++++ Markdown/Markdown.csproj | 4 +- Markdown/Md.cs | 105 ++++++++++++++++-------------- Markdown/Tag.cs | 5 +- Markdown/Test/HtmlToken_Should.cs | 4 +- Markdown/Test/MD_Should.cs | 52 ++++++++++++--- Markdown/Tokens/AHtmlToken.cs | 20 ++---- Markdown/Tokens/HtmlToken.cs | 15 +++-- 8 files changed, 139 insertions(+), 80 deletions(-) create mode 100644 Markdown/CssClassInfo.cs diff --git a/Markdown/CssClassInfo.cs b/Markdown/CssClassInfo.cs new file mode 100644 index 000000000..368704822 --- /dev/null +++ b/Markdown/CssClassInfo.cs @@ -0,0 +1,14 @@ +namespace Markdown +{ + public class CssClassInfo + { + public readonly string ClassName; + public readonly string Description; + + public CssClassInfo(string className, string description) + { + Description = description; + ClassName = className; + } + } +} \ No newline at end of file diff --git a/Markdown/Markdown.csproj b/Markdown/Markdown.csproj index 41f5f64c3..59dafea81 100644 --- a/Markdown/Markdown.csproj +++ b/Markdown/Markdown.csproj @@ -10,8 +10,9 @@ Properties Markdown Markdown - v4.5 + v4.6 512 + 6 AnyCPU @@ -53,6 +54,7 @@ + diff --git a/Markdown/Md.cs b/Markdown/Md.cs index cf95f9efe..2087ebe77 100644 --- a/Markdown/Md.cs +++ b/Markdown/Md.cs @@ -9,14 +9,18 @@ namespace Markdown public class Md { private readonly string plainMd; + private readonly string baseUrl; + private readonly CssClassInfo cssClassInfo; private readonly Dictionary> mdTagParserFuncMatch; private readonly Dictionary> validateFunctions; - - public Md(string plainMd) + public Md(string plainMd, string baseUrl = "", CssClassInfo cssClassInfo = null) { this.plainMd = plainMd; + this.baseUrl = baseUrl; + this.cssClassInfo = cssClassInfo; + mdTagParserFuncMatch = new Dictionary> { [Tag.Em] = ParseEmToken, @@ -28,7 +32,8 @@ public Md(string plainMd) { [Tag.Em] = IsValidEmTag, [Tag.Strong] = IsValidStrongTag, - [Tag.Empty] = (i, b) => false + [Tag.Empty] = (i, b) => false, + [Tag.A] = IsValidATag }; } @@ -102,17 +107,6 @@ private HtmlToken ParseStrongToken(int index, string alreadyParsed = "", int alr : new EmptyHtmlToken(tokenData.Insert(0, "__").ToString(), alreadyEscaped); } - private HtmlToken ParseEmInStrong(ref int index, ref int alreadyEscaped, - ICollection parsedTokens, StringBuilder tokenData) - { - parsedTokens.Add(new EmptyHtmlToken(tokenData.ToString(), alreadyEscaped)); - alreadyEscaped = 0; - tokenData.Clear(); - var htmlToken = ParseEmToken(index); - index += htmlToken.Length; - return htmlToken; - } - private HtmlToken ParseNoMarkup(int index, string alreadyParsed = "", int alreadyEscaped = 0) { var tokenData = new StringBuilder(alreadyParsed); @@ -136,23 +130,19 @@ private HtmlToken ParseNoMarkup(int index, string alreadyParsed = "", int alread private HtmlToken ParseUrl(int index, string alreadyParsed = "", int alreadyEscaped = 0) { - var url = new StringBuilder(); var returnedValue = ParseInsideBracers(']', index, alreadyEscaped, alreadyParsed); - var escaped = returnedValue.Escaped; - index = returnedValue.Index; - var urlText = returnedValue.Data; - - if (plainMd[index] == '(') - { - returnedValue = ParseInsideBracers(')', index, alreadyEscaped, alreadyParsed); - index = returnedValue.Index; - return new AHtmlToken((string) urlText, returnedValue.Data, escaped + returnedValue.Escaped); - } - - throw new MdParserException($"Can't parse link at index {index}"); + var escaped = returnedValue.Item2; + index = returnedValue.Item1; + var urlText = returnedValue.Item3; + + if (plainMd[index] != '(') + throw new MdParserException($"Can't parse link at index {index}"); + returnedValue = ParseInsideBracers(')', index, alreadyEscaped, alreadyParsed); + index = returnedValue.Item1; + return new AHtmlToken(urlText, returnedValue.Item3, escaped + returnedValue.Item2, baseUrl); } - private dynamic ParseInsideBracers(char closeBracer, int index, int escaped, string alreadyParsed) + private Tuple ParseInsideBracers(char closeBracer, int index, int escaped, string alreadyParsed) { var data = new StringBuilder(alreadyParsed); index++; @@ -168,25 +158,18 @@ private dynamic ParseInsideBracers(char closeBracer, int index, int escaped, str } index++; var dataStr = data.ToString(); - return new - { - Index = index, - Escaped = escaped, - Data = dataStr - }; + return Tuple.Create(index, escaped, dataStr); } - private Tag ParseTag(int tagIndex) + private HtmlToken ParseEmInStrong(ref int index, ref int alreadyEscaped, + ICollection parsedTokens, StringBuilder tokenData) { - if (plainMd[tagIndex] == '[') - return Tag.A; - if (plainMd[tagIndex] == '_') - { - if (tagIndex != plainMd.Length - 1) - return plainMd[tagIndex + 1] == '_' ? Tag.Strong : Tag.Em; - return Tag.Em; - } - return Tag.Empty; + parsedTokens.Add(new EmptyHtmlToken(tokenData.ToString(), alreadyEscaped)); + alreadyEscaped = 0; + tokenData.Clear(); + var htmlToken = ParseEmToken(index); + index += htmlToken.Length; + return htmlToken; } private bool NotInsideDigits(int tagIndex) @@ -197,8 +180,10 @@ private bool NotInsideDigits(int tagIndex) } private bool IsNotStrongTag(int tagIndex) - => !(tagIndex - 1 != -1 && plainMd[tagIndex - 1] == '_' || - tagIndex + 1 != plainMd.Length && plainMd[tagIndex + 1] == '_'); + { + return !(tagIndex - 1 != -1 && plainMd[tagIndex - 1] == '_' || + tagIndex + 1 != plainMd.Length && plainMd[tagIndex + 1] == '_'); + } private bool NoSpaceNearMdTag(int tagIndex, int tagLength, bool isOpenTag) { @@ -207,7 +192,9 @@ private bool NoSpaceNearMdTag(int tagIndex, int tagLength, bool isOpenTag) } private bool IsNotOpenTagInEndOfString(int tagIndex, int tagLength, bool isOpenTag) - => !(tagIndex == plainMd.Length - tagLength && isOpenTag); + { + return !(tagIndex == plainMd.Length - tagLength && isOpenTag); + } private bool IsValidEmTag(int tagIndex, bool isOpenTag) { @@ -227,6 +214,26 @@ private bool IsValidStrongTag(int tagIndex, bool isOpenTag) && NoSpaceNearMdTag(tagIndex, 2, isOpenTag); } + private bool IsValidATag(int tagIndex, bool isOpenTag) + { + return plainMd[tagIndex] == '['; + } + + private Tag ParseTag(int tagIndex) + { + if (plainMd[tagIndex] == '[') + { + return Tag.A; + } + if (plainMd[tagIndex] == '_') + { + if (tagIndex != plainMd.Length - 1) + return plainMd[tagIndex + 1] == '_' ? Tag.Strong : Tag.Em; + return Tag.Em; + } + return Tag.Empty; + } + private IEnumerable TryParseToHtml() { var i = 0; @@ -244,7 +251,9 @@ private IEnumerable TryParseToHtml() public string Render() { var htmlTokens = TryParseToHtml(); - return string.Join("", htmlTokens.Select(x => x.ToString())); + return string.Join("", + cssClassInfo == null ? "" : cssClassInfo.Description, + string.Join("", htmlTokens.Select(x => x.Render(cssClassInfo)))); } } } \ No newline at end of file diff --git a/Markdown/Tag.cs b/Markdown/Tag.cs index b54f63941..6d1fba496 100644 --- a/Markdown/Tag.cs +++ b/Markdown/Tag.cs @@ -19,7 +19,10 @@ private Tag(string md, string html) Html = html; } - public static Tag GetRandomTag(Random rnd) => Tags[rnd.Next(Tags.Count)]; + public static Tag GetRandomTag(Random rnd) + { + return Tags[rnd.Next(Tags.Count)]; + } public override bool Equals(object other) { diff --git a/Markdown/Test/HtmlToken_Should.cs b/Markdown/Test/HtmlToken_Should.cs index f18dac208..de979fbd4 100644 --- a/Markdown/Test/HtmlToken_Should.cs +++ b/Markdown/Test/HtmlToken_Should.cs @@ -13,7 +13,7 @@ public void ShouldInsertDataInToTags_WhenToStringCalls() { var token = new EmHtmlToken("data", 0); - token.ToString().Should().Be("data"); + token.Render(null).Should().Be("data"); } [Test] @@ -27,7 +27,7 @@ public void ShouldConcatManyTags_WhenHasInsertedTags() }; var token = new StrongHtmlToken(tokenList, 0); - token.ToString().Should().Be("italicemptybold"); + token.Render(null).Should().Be("italicemptybold"); } } } \ No newline at end of file diff --git a/Markdown/Test/MD_Should.cs b/Markdown/Test/MD_Should.cs index 83bb36cb9..bfd1b502e 100644 --- a/Markdown/Test/MD_Should.cs +++ b/Markdown/Test/MD_Should.cs @@ -68,6 +68,33 @@ public string ParseStrongTagCorrectrly(string plainMd) return new Md(plainMd).Render(); } + [TestCase("[url](www.url.com)", "", ExpectedResult = "url")] + [TestCase("[url](/url)", "www.base.com", ExpectedResult = "url")] + [TestCase("[url](www.url.com)\n[url](/url)", "www.base.com", + ExpectedResult = "url\nurl")] + public string ParseUrlTagCorrectrly(string plainMd, string baseUrl) + { + return new Md(plainMd, baseUrl).Render(); + } + + [TestCase("_asd_", "css", "", ExpectedResult = "asd", TestName = "No def")] + [TestCase("_asd_ __qwe__", "css", "", ExpectedResult = "asd qwe", + TestName = "No def, may tags")] + [TestCase("_asd_", "css", "definition", ExpectedResult = "definitionasd", TestName = "Defined") + ] + public string ParseWithDefinedCss(string plainMd, string cssClassName, string cssClassDef) + { + var css = new CssClassInfo(cssClassName, cssClassDef); + + return new Md(plainMd, "", css).Render(); + } + + [TestCase("asd", ExpectedResult = "

asd

")] + public string ParseParagraphsCorrectly(string plainMd) + { + return new Md(plainMd).Render(); + } + [TestCase("r _i_ r _i_", ExpectedResult = "r i r i")] [TestCase("r __b__ r __b__", ExpectedResult = "r b r b")] [TestCase("_i_ __b__ r", ExpectedResult = "i b r")] @@ -76,21 +103,26 @@ public string ParseMixedTagsCorrectrly(string plainMd) return new Md(plainMd).Render(); } - [TestCase("[url](www.url.com)", ExpectedResult = "url")] - public string ParseUrlTagCorrectrly(string plainMd) - { - return new Md(plainMd).Render(); - } private static string GenerateMd(Tag tag, int length) { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; var rnd = new Random(100); - return $@"{tag.Md}{new string( - Enumerable - .Repeat(chars, length) - .Select(s => s[rnd.Next(s.Length)]) - .ToArray())}{tag.Md}"; + return tag.Equals(Tag.A) + ? $@"[{new string( + Enumerable + .Repeat(chars, length) + .Select(s => s[rnd.Next(s.Length)]) + .ToArray())}]({new string( + Enumerable + .Repeat(chars, length) + .Select(s => s[rnd.Next(s.Length)]) + .ToArray())})" + : $@"{tag.Md}{new string( + Enumerable + .Repeat(chars, length) + .Select(s => s[rnd.Next(s.Length)]) + .ToArray())}{tag.Md}"; } [Test] diff --git a/Markdown/Tokens/AHtmlToken.cs b/Markdown/Tokens/AHtmlToken.cs index b6065ed8e..4bbac40cd 100644 --- a/Markdown/Tokens/AHtmlToken.cs +++ b/Markdown/Tokens/AHtmlToken.cs @@ -5,14 +5,14 @@ namespace Markdown.Tokens public class AHtmlToken : HtmlToken { private readonly string url; - private readonly bool isReferece; + private readonly string baseUrl; + private bool IsReferece => url.StartsWith("/"); public override int Length => Data.Length + url.Length + 4 + escapedCharacters; - public static readonly Dictionary ReferenceUrlsBase = new Dictionary(); - public AHtmlToken(string data, string url, int escapedCharacters) : base("a", data, escapedCharacters) + public AHtmlToken(string data, string url, int escapedCharacters, string baseUrl) : base("a", data, escapedCharacters) { this.url = url; - this.isReferece = isReferece; + this.baseUrl = baseUrl; } public AHtmlToken(List parsedTokens, int escapedCharacters) @@ -20,16 +20,10 @@ public AHtmlToken(List parsedTokens, int escapedCharacters) { } - public static void AddReferenceUrlBase(string url, string name) + public override string Render(CssClassInfo cssClassInfo) { - ReferenceUrlsBase[name] = url; - } - - public override string ToString() - { - if (!isReferece) - return $"{Data}"; - return ""; + var buildedUrl = !IsReferece ? url : string.Join("", baseUrl, url); + return $"{Data}"; } } } \ No newline at end of file diff --git a/Markdown/Tokens/HtmlToken.cs b/Markdown/Tokens/HtmlToken.cs index 4e98ae105..e99094977 100644 --- a/Markdown/Tokens/HtmlToken.cs +++ b/Markdown/Tokens/HtmlToken.cs @@ -30,15 +30,20 @@ protected HtmlToken(string tag, List parsedTokens, int escapedCharact this.escapedCharacters = escapedCharacters; } - protected virtual string InsertInToTags(string dataToInsert) => IsTagged - ? $"<{Tag}>{dataToInsert}" + protected virtual string InsertInToTags(string dataToInsert, CssClassInfo cssClassInfo) => IsTagged + ? $"<{Tag}{GetCssClassDef(cssClassInfo)}>{dataToInsert}" : dataToInsert; - public override string ToString() + protected static string GetCssClassDef(CssClassInfo cssClassInfo) + { + return cssClassInfo == null ? "" : $" class=\"{cssClassInfo.ClassName}\""; + } + + public virtual string Render(CssClassInfo cssClassInfo) { return ParsedTokens.Count > 0 - ? InsertInToTags(string.Join("", ParsedTokens.Select(token => token.ToString()))) - : InsertInToTags(Data); + ? InsertInToTags(string.Join("", ParsedTokens.Select(token => token.Render(cssClassInfo))), cssClassInfo) + : InsertInToTags(Data, cssClassInfo); } } } \ No newline at end of file From afe8c9bbb544ee90dfa768dcf5efbbdf3f832e79 Mon Sep 17 00:00:00 2001 From: Griboedoff Date: Fri, 25 Nov 2016 11:46:19 +0500 Subject: [PATCH 4/8] add paragraphs parsing --- Markdown/Markdown.csproj | 1 + Markdown/Md.cs | 199 +++++++++++++++++++--------------- Markdown/Test/MD_Should.cs | 82 +++++++------- Markdown/Tokens/AHtmlToken.cs | 2 +- Markdown/Tokens/HtmlToken.cs | 8 +- Markdown/Tokens/PHtmlToken.cs | 15 +++ 6 files changed, 179 insertions(+), 128 deletions(-) create mode 100644 Markdown/Tokens/PHtmlToken.cs diff --git a/Markdown/Markdown.csproj b/Markdown/Markdown.csproj index 59dafea81..9666ea32f 100644 --- a/Markdown/Markdown.csproj +++ b/Markdown/Markdown.csproj @@ -63,6 +63,7 @@ + diff --git a/Markdown/Md.cs b/Markdown/Md.cs index 2087ebe77..ecb59b0f7 100644 --- a/Markdown/Md.cs +++ b/Markdown/Md.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; using System.Text; using Markdown.Tokens; @@ -8,46 +10,53 @@ namespace Markdown { public class Md { - private readonly string plainMd; + private readonly string[] plainMd; private readonly string baseUrl; private readonly CssClassInfo cssClassInfo; - private readonly Dictionary> mdTagParserFuncMatch; - private readonly Dictionary> validateFunctions; + private readonly Dictionary> mdTagParserFuncMatch; + + private static readonly Dictionary> ValidateFunctions = new Dictionary + > + { + [Tag.Em] = IsValidEmTag, + [Tag.Strong] = IsValidStrongTag, + [Tag.Empty] = (q, w, e) => false, + [Tag.A] = IsValidATag + }; + + private int currLineIndex; + private string CurrLine => plainMd[currLineIndex]; public Md(string plainMd, string baseUrl = "", CssClassInfo cssClassInfo = null) { - this.plainMd = plainMd; + this.plainMd = plainMd.Replace("\n", "\0\n\0").Split('\0'); this.baseUrl = baseUrl; this.cssClassInfo = cssClassInfo; - mdTagParserFuncMatch = new Dictionary> + mdTagParserFuncMatch = new Dictionary> { [Tag.Em] = ParseEmToken, [Tag.Empty] = ParseNoMarkup, [Tag.Strong] = ParseStrongToken, [Tag.A] = ParseUrl }; - validateFunctions = new Dictionary> - { - [Tag.Em] = IsValidEmTag, - [Tag.Strong] = IsValidStrongTag, - [Tag.Empty] = (i, b) => false, - [Tag.A] = IsValidATag - }; + + + currLineIndex = 0; } - private HtmlToken ParseEmToken(int index, string alreadyParsed = "", int alreadyEscaped = 0) + private static HtmlToken ParseEmToken(string currLine, int index, string alreadyParsed = "", int alreadyEscaped = 0) { - if (!IsValidEmTag(index, true)) - return ParseNoMarkup(index); + if (!IsValidEmTag(currLine, index, true)) + return ParseNoMarkup(currLine, index); index++; var tokenData = new StringBuilder(alreadyParsed); - while (index < plainMd.Length && !IsValidEmTag(index, false)) + while (index < currLine.Length && !IsValidEmTag(currLine, index, false)) { - var tag = ParseTag(index); + var tag = ParseTag(currLine, index); if (tag.Equals(Tag.Strong)) { @@ -56,104 +65,125 @@ private HtmlToken ParseEmToken(int index, string alreadyParsed = "", int already continue; } - if (plainMd[index] == '\\') + if (currLine[index] == '\\') { index++; alreadyEscaped++; } - tokenData.Append(plainMd[index]); + tokenData.Append(currLine[index]); index++; } - return index != plainMd.Length + return index != currLine.Length ? (HtmlToken) new EmHtmlToken(tokenData.ToString(), alreadyEscaped) : new EmptyHtmlToken(tokenData.Insert(0, '_').ToString(), alreadyEscaped); } - private HtmlToken ParseStrongToken(int index, string alreadyParsed = "", int alreadyEscaped = 0) + private static HtmlToken ParseStrongToken(string currLine, int index, string alreadyParsed = "", + int alreadyEscaped = 0) { - if (!IsValidStrongTag(index, true)) - return ParseNoMarkup(index); + if (!IsValidStrongTag(currLine, index, true)) + return ParseNoMarkup(currLine, index); var parsedTokens = new List(); index += 2; var tokenData = new StringBuilder(alreadyParsed); - while (index < plainMd.Length && !IsValidStrongTag(index, false)) + while (index < currLine.Length && !IsValidStrongTag(currLine, index, false)) { - var tag = ParseTag(index); + var tag = ParseTag(currLine, index); if (Equals(tag, Tag.Em)) { - parsedTokens.Add(ParseEmInStrong(ref index, ref alreadyEscaped, parsedTokens, tokenData)); - if (index == plainMd.Length) + parsedTokens.Add(ParseEmInStrong(currLine, ref index, ref alreadyEscaped, parsedTokens, tokenData)); + if (index == currLine.Length) break; } - if (plainMd[index] == '\\') + if (currLine[index] == '\\') { index++; alreadyEscaped++; } - tokenData.Append(plainMd[index]); + tokenData.Append(currLine[index]); index++; } parsedTokens.Add(new EmptyHtmlToken(tokenData.ToString(), alreadyEscaped)); - return index != plainMd.Length + return index != currLine.Length ? (HtmlToken) new StrongHtmlToken(parsedTokens, 0) : new EmptyHtmlToken(tokenData.Insert(0, "__").ToString(), alreadyEscaped); } - private HtmlToken ParseNoMarkup(int index, string alreadyParsed = "", int alreadyEscaped = 0) + private static HtmlToken ParseNoMarkup(string currLine, int index, string alreadyParsed = "", int alreadyEscaped = 0) { var tokenData = new StringBuilder(alreadyParsed); var escaped = alreadyEscaped; - while (index < plainMd.Length) + while (index < currLine.Length) { - var tag = ParseTag(index); + var tag = ParseTag(currLine, index); - if (validateFunctions[tag].Invoke(index, true)) + if (ValidateFunctions[tag].Invoke(currLine, index, true)) break; - if (plainMd[index] == '\\') + if (currLine[index] == '\\') { index++; escaped++; } - tokenData.Append(plainMd[index]); + tokenData.Append(currLine[index]); index++; } return new EmptyHtmlToken(tokenData.ToString(), escaped); } - private HtmlToken ParseUrl(int index, string alreadyParsed = "", int alreadyEscaped = 0) + private HtmlToken ParseUrl(string currLine, int index, string alreadyParsed = "", int alreadyEscaped = 0) { - var returnedValue = ParseInsideBracers(']', index, alreadyEscaped, alreadyParsed); + var returnedValue = ParseInsideBracers(']', index, alreadyEscaped, alreadyParsed, currLine); var escaped = returnedValue.Item2; index = returnedValue.Item1; var urlText = returnedValue.Item3; - if (plainMd[index] != '(') + if (currLine[index] != '(') throw new MdParserException($"Can't parse link at index {index}"); - returnedValue = ParseInsideBracers(')', index, alreadyEscaped, alreadyParsed); - index = returnedValue.Item1; + returnedValue = ParseInsideBracers(')', index, alreadyEscaped, alreadyParsed, currLine); return new AHtmlToken(urlText, returnedValue.Item3, escaped + returnedValue.Item2, baseUrl); } - private Tuple ParseInsideBracers(char closeBracer, int index, int escaped, string alreadyParsed) + private HtmlToken ParseParagraph() + { + var innerTags = new List(); + + while (currLineIndex < plainMd.Length && !string.IsNullOrWhiteSpace(CurrLine)) + { + var i = 0; + while (i < CurrLine.Length) + { + var tag = ParseTag(CurrLine, i); + var parsedToken = mdTagParserFuncMatch[tag].Invoke(CurrLine, i, "", 0); + i += parsedToken.Length; + innerTags.Add(parsedToken); + } + currLineIndex++; + } + + return new PHtmlToken(innerTags); + } + + private static Tuple ParseInsideBracers(char closeBracer, int index, int escaped, + string alreadyParsed, string currLine) { var data = new StringBuilder(alreadyParsed); index++; - while (index < plainMd.Length && plainMd[index] != closeBracer) + while (index < currLine.Length && currLine[index] != closeBracer) { - if (plainMd[index] == '\\') + if (currLine[index] == '\\') { index++; escaped++; } - data.Append(plainMd[index]); + data.Append(currLine[index]); index++; } index++; @@ -161,90 +191,87 @@ private Tuple ParseInsideBracers(char closeBracer, int index, return Tuple.Create(index, escaped, dataStr); } - private HtmlToken ParseEmInStrong(ref int index, ref int alreadyEscaped, + private static HtmlToken ParseEmInStrong(string currLine, ref int index, ref int alreadyEscaped, ICollection parsedTokens, StringBuilder tokenData) { parsedTokens.Add(new EmptyHtmlToken(tokenData.ToString(), alreadyEscaped)); alreadyEscaped = 0; tokenData.Clear(); - var htmlToken = ParseEmToken(index); + var htmlToken = ParseEmToken(currLine, index); index += htmlToken.Length; return htmlToken; } - private bool NotInsideDigits(int tagIndex) + private static bool NotInsideDigits(int tagIndex, string currLine) { - if (tagIndex + 1 == plainMd.Length || tagIndex - 1 == -1) + if (tagIndex + 1 == currLine.Length || tagIndex - 1 == -1) return true; - return !char.IsDigit(plainMd[tagIndex - 1]) && !char.IsDigit(plainMd[tagIndex + 1]); + return !char.IsDigit(currLine[tagIndex - 1]) && !char.IsDigit(currLine[tagIndex + 1]); } - private bool IsNotStrongTag(int tagIndex) + private static bool IsNotStrongTag(int tagIndex, string currLine) { - return !(tagIndex - 1 != -1 && plainMd[tagIndex - 1] == '_' || - tagIndex + 1 != plainMd.Length && plainMd[tagIndex + 1] == '_'); + return !(tagIndex - 1 != -1 && currLine[tagIndex - 1] == '_' || + tagIndex + 1 != currLine.Length && currLine[tagIndex + 1] == '_'); } - private bool NoSpaceNearMdTag(int tagIndex, int tagLength, bool isOpenTag) + private static bool NoSpaceNearMdTag(int tagIndex, int tagLength, bool isOpenTag, string currLine) { var nextIndex = tagIndex + (isOpenTag ? tagLength : -1); - return nextIndex >= 0 && nextIndex < plainMd.Length && plainMd[nextIndex] != ' '; + return nextIndex >= 0 && nextIndex < currLine.Length && currLine[nextIndex] != ' '; } - private bool IsNotOpenTagInEndOfString(int tagIndex, int tagLength, bool isOpenTag) + private static bool IsNotOpenTagInEndOfString(int tagIndex, int tagLength, bool isOpenTag, string currLine) { - return !(tagIndex == plainMd.Length - tagLength && isOpenTag); + return !(tagIndex == currLine.Length - tagLength && isOpenTag); } - private bool IsValidEmTag(int tagIndex, bool isOpenTag) + private static bool IsValidEmTag(string currLine, int tagIndex, bool isOpenTag) { - if (plainMd[tagIndex] != '_') + if (currLine[tagIndex] != '_') return false; - return IsNotOpenTagInEndOfString(tagIndex, 1, isOpenTag) - && NoSpaceNearMdTag(tagIndex, 1, isOpenTag) - && IsNotStrongTag(tagIndex) - && NotInsideDigits(tagIndex); + return IsNotOpenTagInEndOfString(tagIndex, 1, isOpenTag, currLine) + && NoSpaceNearMdTag(tagIndex, 1, isOpenTag, currLine) + && IsNotStrongTag(tagIndex, currLine) + && NotInsideDigits(tagIndex, currLine); } - private bool IsValidStrongTag(int tagIndex, bool isOpenTag) + private static bool IsValidStrongTag(string currLine, int tagIndex, bool isOpenTag) { - if (plainMd[tagIndex] != '_' || !ParseTag(tagIndex).Equals(Tag.Strong)) + if (currLine[tagIndex] != '_' || !ParseTag(currLine, tagIndex).Equals(Tag.Strong)) return false; - return IsNotOpenTagInEndOfString(tagIndex, 2, isOpenTag) - && NoSpaceNearMdTag(tagIndex, 2, isOpenTag); + return IsNotOpenTagInEndOfString(tagIndex, 2, isOpenTag, currLine) + && NoSpaceNearMdTag(tagIndex, 2, isOpenTag, currLine); } - private bool IsValidATag(int tagIndex, bool isOpenTag) + private static bool IsValidATag(string currLine, int tagIndex, bool isOpenTag) { - return plainMd[tagIndex] == '['; + return currLine[tagIndex] == '['; } - private Tag ParseTag(int tagIndex) + private static Tag ParseTag(string currLine, int tagIndex) { - if (plainMd[tagIndex] == '[') - { + if (currLine[tagIndex] == '[') return Tag.A; - } - if (plainMd[tagIndex] == '_') - { - if (tagIndex != plainMd.Length - 1) - return plainMd[tagIndex + 1] == '_' ? Tag.Strong : Tag.Em; - return Tag.Em; - } - return Tag.Empty; + if (currLine[tagIndex] != '_') + return Tag.Empty; + if (tagIndex != currLine.Length - 1) + return currLine[tagIndex + 1] == '_' + ? Tag.Strong + : Tag.Em; + return Tag.Em; } private IEnumerable TryParseToHtml() { - var i = 0; var root = new List(); - while (i < plainMd.Length) + + while (currLineIndex < plainMd.Length) { - var tag = ParseTag(i); - var parsedToken = mdTagParserFuncMatch[tag].Invoke(i, "", 0); - i += parsedToken.Length; - root.Add(parsedToken); + root.Add(ParseParagraph()); + currLineIndex++; } + return root; } diff --git a/Markdown/Test/MD_Should.cs b/Markdown/Test/MD_Should.cs index bfd1b502e..2ebad1c21 100644 --- a/Markdown/Test/MD_Should.cs +++ b/Markdown/Test/MD_Should.cs @@ -10,77 +10,79 @@ namespace Markdown.Test [TestFixture] internal class Md_Should { - [TestCase("qwe asd zxc", ExpectedResult = "qwe asd zxc")] + [TestCase("qwe asd zxc", ExpectedResult = "

qwe asd zxc

")] public string ParseNoMarkup(string plainMd) { return new Md(plainMd).Render(); } - [TestCase("_asd_", ExpectedResult = "asd")] - [TestCase("_a s d_", ExpectedResult = "a s d")] - [TestCase("_1_2_3_", ExpectedResult = "1_2_3")] - [TestCase("_a_ _s d_", ExpectedResult = "a s d")] - [TestCase("_a_ _s d", ExpectedResult = "a _s d")] - [TestCase("_aas__abc__abc_", ExpectedResult = "aas__abc__abc")] + [TestCase("_asd_", ExpectedResult = "

asd

")] + [TestCase("_a s d_", ExpectedResult = "

a s d

")] + [TestCase("_1_2_3_", ExpectedResult = "

1_2_3

")] + [TestCase("_a_ _s d_", ExpectedResult = "

a s d

")] + [TestCase("_a_ _s d", ExpectedResult = "

a _s d

")] + [TestCase("_aas__abc__abc_", ExpectedResult = "

aas__abc__abc

")] public string ParseEmTagCorrectly(string plainMd) { return new Md(plainMd).Render(); } - [TestCase("_ d_", ExpectedResult = "_ d_")] - [TestCase("_a _", ExpectedResult = "_a _")] - [TestCase("__ d__", ExpectedResult = "__ d__")] - [TestCase("__a __", ExpectedResult = "__a __")] - [TestCase("_ abc_abc_", ExpectedResult = "_ abcabc")] + [TestCase("_ d_", ExpectedResult = "

_ d_

")] + [TestCase("_a _", ExpectedResult = "

_a _

")] + [TestCase("__ d__", ExpectedResult = "

__ d__

")] + [TestCase("__a __", ExpectedResult = "

__a __

")] + [TestCase("_ abc_abc_", ExpectedResult = "

_ abcabc

")] public string ParseTrailingWhitespaceCorrectly(string plainMd) { return new Md(plainMd).Render(); } - [TestCase("_ab cd", ExpectedResult = "_ab cd")] - [TestCase("__ab cd", ExpectedResult = "__ab cd")] + [TestCase("_ab cd", ExpectedResult = "

_ab cd

")] + [TestCase("__ab cd", ExpectedResult = "

__ab cd

")] public string ParseNoMarkup_IfMissingCloseTag(string plainMd) { return new Md(plainMd).Render(); } - [TestCase(@"_a\_b_", ExpectedResult = "a_b")] - [TestCase(@"__a\_b__", ExpectedResult = "a_b")] - [TestCase(@"a\_b", ExpectedResult = "a_b")] + [TestCase(@"_a\_b_", ExpectedResult = "

a_b

")] + [TestCase(@"__a\_b__", ExpectedResult = "

a_b

")] + [TestCase(@"a\_b", ExpectedResult = "

a_b

")] public string ParseEscapedCorrectly(string plainMd) { return new Md(plainMd).Render(); } - [TestCase("__abc_abc", ExpectedResult = "__abc_abc")] - [TestCase("__abc_abc_", ExpectedResult = "__abcabc")] - [TestCase("_abc__abc", ExpectedResult = "_abc__abc")] - [TestCase("_abc__abc__", ExpectedResult = "_abc__abc__")] + [TestCase("__abc_abc", ExpectedResult = "

__abc_abc

")] + [TestCase("__abc_abc_", ExpectedResult = "

__abcabc

")] + [TestCase("_abc__abc", ExpectedResult = "

_abc__abc

")] + [TestCase("_abc__abc__", ExpectedResult = "

_abc__abc__

")] public string ParseUnpairTags(string plainMd) { return new Md(plainMd).Render(); } - [TestCase("__abc__", ExpectedResult = "abc")] - [TestCase("__abc_abc_abc__", ExpectedResult = "abcabcabc")] + [TestCase("__abc__", ExpectedResult = "

abc

")] + [TestCase("__abc_abc_abc__", ExpectedResult = "

abcabcabc

")] public string ParseStrongTagCorrectrly(string plainMd) { return new Md(plainMd).Render(); } - [TestCase("[url](www.url.com)", "", ExpectedResult = "url")] - [TestCase("[url](/url)", "www.base.com", ExpectedResult = "url")] + [TestCase("[url](www.url.com)", "", ExpectedResult = "

url

")] + [TestCase("[url](/url)", "www.base.com", ExpectedResult = "

url

")] [TestCase("[url](www.url.com)\n[url](/url)", "www.base.com", - ExpectedResult = "url\nurl")] + ExpectedResult = "

url

url

")] public string ParseUrlTagCorrectrly(string plainMd, string baseUrl) { return new Md(plainMd, baseUrl).Render(); } - [TestCase("_asd_", "css", "", ExpectedResult = "asd", TestName = "No def")] - [TestCase("_asd_ __qwe__", "css", "", ExpectedResult = "asd qwe", + [TestCase("_asd_", "css", "", ExpectedResult = "

asd

", TestName = "No def")] + [TestCase("_asd_ __qwe__", "css", "", + ExpectedResult = "

asd qwe

", TestName = "No def, may tags")] - [TestCase("_asd_", "css", "definition", ExpectedResult = "definitionasd", TestName = "Defined") + [TestCase("_asd_", "css", "definition\n", ExpectedResult = "definition\n

asd

", + TestName = "Defined") ] public string ParseWithDefinedCss(string plainMd, string cssClassName, string cssClassDef) { @@ -95,16 +97,16 @@ public string ParseParagraphsCorrectly(string plainMd) return new Md(plainMd).Render(); } - [TestCase("r _i_ r _i_", ExpectedResult = "r i r i")] - [TestCase("r __b__ r __b__", ExpectedResult = "r b r b")] - [TestCase("_i_ __b__ r", ExpectedResult = "i b r")] + [TestCase("r _i_ r _i_", ExpectedResult = "

r i r i

")] + [TestCase("r __b__ r __b__", ExpectedResult = "

r b r b

")] + [TestCase("_i_ __b__ r", ExpectedResult = "

i b r

")] public string ParseMixedTagsCorrectrly(string plainMd) { return new Md(plainMd).Render(); } - private static string GenerateMd(Tag tag, int length) + private static string GenerateMdTag(Tag tag, int length) { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; var rnd = new Random(100); @@ -125,16 +127,22 @@ private static string GenerateMd(Tag tag, int length) .ToArray())}{tag.Md}"; } + private static string GenerateMd(Random rnd) + { + var md = new StringBuilder(); + for (var i = 0; i < 1000; i++) + md.Append(GenerateMdTag(Tag.GetRandomTag(rnd), 100000)); + return md.ToString(); + } + [Test] + [Explicit] public void PerformanceTest() { var iterationWatch = new Stopwatch(); var parseWatch = new Stopwatch(); - var md = new StringBuilder(); var rnd = new Random(10); - for (var i = 0; i < 1000; i++) - md.Append(GenerateMd(Tag.GetRandomTag(rnd), 100000)); - var plainMd = md.ToString(); + var plainMd = GenerateMd(rnd); Console.WriteLine($"Length = {plainMd.Length}"); var c = 0; diff --git a/Markdown/Tokens/AHtmlToken.cs b/Markdown/Tokens/AHtmlToken.cs index 4bbac40cd..ecb40b0dd 100644 --- a/Markdown/Tokens/AHtmlToken.cs +++ b/Markdown/Tokens/AHtmlToken.cs @@ -7,7 +7,7 @@ public class AHtmlToken : HtmlToken private readonly string url; private readonly string baseUrl; private bool IsReferece => url.StartsWith("/"); - public override int Length => Data.Length + url.Length + 4 + escapedCharacters; + public override int Length => Data.Length + url.Length + 4 + EscapedCharacters; public AHtmlToken(string data, string url, int escapedCharacters, string baseUrl) : base("a", data, escapedCharacters) { diff --git a/Markdown/Tokens/HtmlToken.cs b/Markdown/Tokens/HtmlToken.cs index e99094977..0646cb172 100644 --- a/Markdown/Tokens/HtmlToken.cs +++ b/Markdown/Tokens/HtmlToken.cs @@ -11,15 +11,15 @@ public abstract class HtmlToken protected virtual bool IsTagged => !string.IsNullOrEmpty(Tag); - public virtual int Length => ParsedTokens.Sum(x => x.Length) + (Data ?? "").Length + escapedCharacters; + public virtual int Length => ParsedTokens.Sum(x => x.Length) + (Data ?? "").Length + EscapedCharacters; - protected readonly int escapedCharacters; + protected readonly int EscapedCharacters; protected HtmlToken(string tag, string data, int escapedCharacters) { Tag = tag; Data = data; - this.escapedCharacters = escapedCharacters; + this.EscapedCharacters = escapedCharacters; ParsedTokens = new List(); } @@ -27,7 +27,7 @@ protected HtmlToken(string tag, List parsedTokens, int escapedCharact { Tag = tag; ParsedTokens = parsedTokens; - this.escapedCharacters = escapedCharacters; + this.EscapedCharacters = escapedCharacters; } protected virtual string InsertInToTags(string dataToInsert, CssClassInfo cssClassInfo) => IsTagged diff --git a/Markdown/Tokens/PHtmlToken.cs b/Markdown/Tokens/PHtmlToken.cs new file mode 100644 index 000000000..4f09f0c1c --- /dev/null +++ b/Markdown/Tokens/PHtmlToken.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Markdown.Tokens +{ + public class PHtmlToken : HtmlToken + { + public PHtmlToken(string data) : base("p", data, 0) + { + } + + public PHtmlToken(List parsedTokens) : base("p", parsedTokens, 0) + { + } + } +} \ No newline at end of file From aabcc1517c55238c466a05e165135eb0f714f5ab Mon Sep 17 00:00:00 2001 From: Griboedoff Date: Fri, 25 Nov 2016 13:22:09 +0500 Subject: [PATCH 5/8] add header parsing --- Markdown/LineType.cs | 8 +++++ Markdown/Markdown.csproj | 2 ++ Markdown/Md.cs | 65 +++++++++++++++++++++++++---------- Markdown/Tag.cs | 1 + Markdown/Test/MD_Should.cs | 18 +++++++--- Markdown/Tokens/HHtmlToken.cs | 20 +++++++++++ Markdown/Tokens/HtmlToken.cs | 2 +- 7 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 Markdown/LineType.cs create mode 100644 Markdown/Tokens/HHtmlToken.cs diff --git a/Markdown/LineType.cs b/Markdown/LineType.cs new file mode 100644 index 000000000..075c92964 --- /dev/null +++ b/Markdown/LineType.cs @@ -0,0 +1,8 @@ +namespace Markdown +{ + public enum LineType + { + Header, + Simple + } +} \ No newline at end of file diff --git a/Markdown/Markdown.csproj b/Markdown/Markdown.csproj index 9666ea32f..4d6ea6a30 100644 --- a/Markdown/Markdown.csproj +++ b/Markdown/Markdown.csproj @@ -55,6 +55,7 @@
+ @@ -62,6 +63,7 @@ + diff --git a/Markdown/Md.cs b/Markdown/Md.cs index ecb59b0f7..eb9f99943 100644 --- a/Markdown/Md.cs +++ b/Markdown/Md.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; using System.Text; using Markdown.Tokens; @@ -14,7 +12,7 @@ public class Md private readonly string baseUrl; private readonly CssClassInfo cssClassInfo; - private readonly Dictionary> mdTagParserFuncMatch; + private readonly Dictionary> stringTagParserFuncMatch; private static readonly Dictionary> ValidateFunctions = new Dictionary > @@ -25,6 +23,8 @@ public class Md [Tag.A] = IsValidATag }; + private readonly Dictionary> lineTagParserFuncMatch; + private int currLineIndex; private string CurrLine => plainMd[currLineIndex]; @@ -34,19 +34,34 @@ public Md(string plainMd, string baseUrl = "", CssClassInfo cssClassInfo = null) this.baseUrl = baseUrl; this.cssClassInfo = cssClassInfo; - mdTagParserFuncMatch = new Dictionary> + stringTagParserFuncMatch = new Dictionary> { - [Tag.Em] = ParseEmToken, + [Tag.Em] = ParseItalic, [Tag.Empty] = ParseNoMarkup, - [Tag.Strong] = ParseStrongToken, - [Tag.A] = ParseUrl + [Tag.Strong] = ParseBold, + [Tag.A] = ParseUrl, }; + lineTagParserFuncMatch = new Dictionary> + { + [LineType.Header] = ParseHeader, + [LineType.Simple] = ParseParagraph + }; currLineIndex = 0; } - private static HtmlToken ParseEmToken(string currLine, int index, string alreadyParsed = "", int alreadyEscaped = 0) + private HtmlToken ParseHeader() + { + var headerText = CurrLine.Replace("#", ""); + headerText = headerText.Replace("\\", ""); + + var headerImportance = CurrLine.Length - headerText.Length; + + return new HHtmlToken(headerText, headerImportance); + } + + private static HtmlToken ParseItalic(string currLine, int index, string alreadyParsed = "", int alreadyEscaped = 0) { if (!IsValidEmTag(currLine, index, true)) return ParseNoMarkup(currLine, index); @@ -80,7 +95,7 @@ private static HtmlToken ParseEmToken(string currLine, int index, string already : new EmptyHtmlToken(tokenData.Insert(0, '_').ToString(), alreadyEscaped); } - private static HtmlToken ParseStrongToken(string currLine, int index, string alreadyParsed = "", + private static HtmlToken ParseBold(string currLine, int index, string alreadyParsed = "", int alreadyEscaped = 0) { if (!IsValidStrongTag(currLine, index, true)) @@ -155,13 +170,15 @@ private HtmlToken ParseParagraph() { var innerTags = new List(); - while (currLineIndex < plainMd.Length && !string.IsNullOrWhiteSpace(CurrLine)) + while (currLineIndex < plainMd.Length + && !string.IsNullOrWhiteSpace(CurrLine) + && GetLineTypeTag(CurrLine) == LineType.Simple) { var i = 0; while (i < CurrLine.Length) { var tag = ParseTag(CurrLine, i); - var parsedToken = mdTagParserFuncMatch[tag].Invoke(CurrLine, i, "", 0); + var parsedToken = stringTagParserFuncMatch[tag].Invoke(CurrLine, i, "", 0); i += parsedToken.Length; innerTags.Add(parsedToken); } @@ -197,7 +214,7 @@ private static HtmlToken ParseEmInStrong(string currLine, ref int index, ref int parsedTokens.Add(new EmptyHtmlToken(tokenData.ToString(), alreadyEscaped)); alreadyEscaped = 0; tokenData.Clear(); - var htmlToken = ParseEmToken(currLine, index); + var htmlToken = ParseItalic(currLine, index); index += htmlToken.Length; return htmlToken; } @@ -255,11 +272,18 @@ private static Tag ParseTag(string currLine, int tagIndex) return Tag.A; if (currLine[tagIndex] != '_') return Tag.Empty; - if (tagIndex != currLine.Length - 1) - return currLine[tagIndex + 1] == '_' - ? Tag.Strong - : Tag.Em; - return Tag.Em; + if (tagIndex == currLine.Length - 1) + return Tag.Em; + return currLine[tagIndex + 1] == '_' + ? Tag.Strong + : Tag.Em; + } + + private static LineType GetLineTypeTag(string currLine) + { + if (currLine.StartsWith("#")) + return LineType.Header; + return LineType.Simple; } private IEnumerable TryParseToHtml() @@ -268,8 +292,11 @@ private IEnumerable TryParseToHtml() while (currLineIndex < plainMd.Length) { - root.Add(ParseParagraph()); - currLineIndex++; + var htmlToken = lineTagParserFuncMatch[GetLineTypeTag(CurrLine)].Invoke(); + root.Add(htmlToken); + do + currLineIndex++; + while (currLineIndex < plainMd.Length && string.IsNullOrWhiteSpace(CurrLine)); } return root; diff --git a/Markdown/Tag.cs b/Markdown/Tag.cs index 6d1fba496..84d95effa 100644 --- a/Markdown/Tag.cs +++ b/Markdown/Tag.cs @@ -11,6 +11,7 @@ public class Tag public static readonly Tag Strong = new Tag("__", "strong"); public static readonly Tag Empty = new Tag("", ""); public static readonly Tag A = new Tag("", "a"); + public static readonly Tag H = new Tag("#", "h"); private static readonly List Tags = new List {Em, Strong, Empty}; private Tag(string md, string html) diff --git a/Markdown/Test/MD_Should.cs b/Markdown/Test/MD_Should.cs index 2ebad1c21..e1848e913 100644 --- a/Markdown/Test/MD_Should.cs +++ b/Markdown/Test/MD_Should.cs @@ -77,11 +77,13 @@ public string ParseUrlTagCorrectrly(string plainMd, string baseUrl) return new Md(plainMd, baseUrl).Render(); } - [TestCase("_asd_", "css", "", ExpectedResult = "

asd

", TestName = "No def")] + [TestCase("_asd_", "css", "", ExpectedResult = "

asd

", TestName = "No def") + ] [TestCase("_asd_ __qwe__", "css", "", ExpectedResult = "

asd qwe

", TestName = "No def, may tags")] - [TestCase("_asd_", "css", "definition\n", ExpectedResult = "definition\n

asd

", + [TestCase("_asd_", "css", "definition\n", + ExpectedResult = "definition\n

asd

", TestName = "Defined") ] public string ParseWithDefinedCss(string plainMd, string cssClassName, string cssClassDef) @@ -92,11 +94,13 @@ public string ParseWithDefinedCss(string plainMd, string cssClassName, string cs } [TestCase("asd", ExpectedResult = "

asd

")] + [TestCase("q\nb\nc", ExpectedResult = "

q

b

c

")] public string ParseParagraphsCorrectly(string plainMd) { return new Md(plainMd).Render(); } + [TestCase("r _i_ r _i_", ExpectedResult = "

r i r i

")] [TestCase("r __b__ r __b__", ExpectedResult = "

r b r b

")] [TestCase("_i_ __b__ r", ExpectedResult = "

i b r

")] @@ -105,6 +109,12 @@ public string ParseMixedTagsCorrectrly(string plainMd) return new Md(plainMd).Render(); } + [TestCase("##qwe", ExpectedResult = "

qwe

")] + [TestCase("qwe\n##qwe\nqwe", ExpectedResult = "

qwe

qwe

qwe

")] + public string ParseHeaderCorrectly(string plainMd) + { + return new Md(plainMd).Render(); + } private static string GenerateMdTag(Tag tag, int length) { @@ -145,10 +155,10 @@ public void PerformanceTest() var plainMd = GenerateMd(rnd); Console.WriteLine($"Length = {plainMd.Length}"); - var c = 0; iterationWatch.Start(); for (var i = 0; i < plainMd.Length; i++) - c = rnd.Next(); + // ReSharper disable once ReturnValueOfPureMethodIsNotUsed + rnd.Next(); iterationWatch.Stop(); var parser = new Md(plainMd); diff --git a/Markdown/Tokens/HHtmlToken.cs b/Markdown/Tokens/HHtmlToken.cs new file mode 100644 index 000000000..9ee6bcf9e --- /dev/null +++ b/Markdown/Tokens/HHtmlToken.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace Markdown.Tokens +{ + public class HHtmlToken : HtmlToken + + { + private readonly int headerImportance; + + public HHtmlToken(string data, int headerImportance) : base($"h{headerImportance}", data, 0) + { + this.headerImportance = headerImportance; + } + + public HHtmlToken(List parsedTokens, int headerImportance) : base($"h{headerImportance}", parsedTokens, 0) + { + this.headerImportance = headerImportance; + } + } +} \ No newline at end of file diff --git a/Markdown/Tokens/HtmlToken.cs b/Markdown/Tokens/HtmlToken.cs index 0646cb172..5704a3a36 100644 --- a/Markdown/Tokens/HtmlToken.cs +++ b/Markdown/Tokens/HtmlToken.cs @@ -19,7 +19,7 @@ protected HtmlToken(string tag, string data, int escapedCharacters) { Tag = tag; Data = data; - this.EscapedCharacters = escapedCharacters; + EscapedCharacters = escapedCharacters; ParsedTokens = new List(); } From b4292c33c47398910341f9c12074558c78203892 Mon Sep 17 00:00:00 2001 From: Griboedoff Date: Fri, 25 Nov 2016 14:23:52 +0500 Subject: [PATCH 6/8] parce code blocks fix bug in paragraphs --- Markdown/LineType.cs | 1 + Markdown/Markdown.csproj | 1 + Markdown/Md.cs | 73 ++++++++++++++++++++------------ Markdown/Test/MD_Should.cs | 14 ++++-- Markdown/Tokens/CodeHtmlToken.cs | 20 +++++++++ Markdown/Tokens/HtmlToken.cs | 16 +++++-- Markdown/Tokens/PHtmlToken.cs | 2 + 7 files changed, 94 insertions(+), 33 deletions(-) create mode 100644 Markdown/Tokens/CodeHtmlToken.cs diff --git a/Markdown/LineType.cs b/Markdown/LineType.cs index 075c92964..614e7bd43 100644 --- a/Markdown/LineType.cs +++ b/Markdown/LineType.cs @@ -3,6 +3,7 @@ public enum LineType { Header, + CodeBlock, Simple } } \ No newline at end of file diff --git a/Markdown/Markdown.csproj b/Markdown/Markdown.csproj index 4d6ea6a30..eb5a95a6d 100644 --- a/Markdown/Markdown.csproj +++ b/Markdown/Markdown.csproj @@ -61,6 +61,7 @@ + diff --git a/Markdown/Md.cs b/Markdown/Md.cs index eb9f99943..c2aa3d15f 100644 --- a/Markdown/Md.cs +++ b/Markdown/Md.cs @@ -27,10 +27,11 @@ public class Md private int currLineIndex; private string CurrLine => plainMd[currLineIndex]; + private bool IsInPlainMd => currLineIndex < plainMd.Length; public Md(string plainMd, string baseUrl = "", CssClassInfo cssClassInfo = null) { - this.plainMd = plainMd.Replace("\n", "\0\n\0").Split('\0'); + this.plainMd = plainMd.Split('\n'); this.baseUrl = baseUrl; this.cssClassInfo = cssClassInfo; @@ -45,7 +46,8 @@ public Md(string plainMd, string baseUrl = "", CssClassInfo cssClassInfo = null) lineTagParserFuncMatch = new Dictionary> { [LineType.Header] = ParseHeader, - [LineType.Simple] = ParseParagraph + [LineType.Simple] = ParseParagraph, + [LineType.CodeBlock] = ParseCodeBlock }; currLineIndex = 0; @@ -58,9 +60,49 @@ private HtmlToken ParseHeader() var headerImportance = CurrLine.Length - headerText.Length; + currLineIndex++; return new HHtmlToken(headerText, headerImportance); } + private HtmlToken ParseCodeBlock() + { + var builder = new StringBuilder(); + + while (IsInPlainMd && GetLineTypeTag(CurrLine) == LineType.CodeBlock) + { + builder.Append(CurrLine.Substring(CurrLine.StartsWith("\t") ? 1 : 4)); + builder.Append("\n"); + currLineIndex++; + + } + + builder.Remove(builder.Length - 1, 1); + + return new CodeHtmlToken(builder.ToString()); + } + + private HtmlToken ParseParagraph() + { + var innerTags = new List(); + + while (IsInPlainMd && !string.IsNullOrWhiteSpace(CurrLine) && GetLineTypeTag(CurrLine) == LineType.Simple) + { + if (innerTags.Count != 0) + innerTags.Add(new EmptyHtmlToken("\n", 0)); + var i = 0; + while (i < CurrLine.Length) + { + var tag = ParseTag(CurrLine, i); + var parsedToken = stringTagParserFuncMatch[tag].Invoke(CurrLine, i, "", 0); + i += parsedToken.Length; + innerTags.Add(parsedToken); + } + currLineIndex++; + } + + return new PHtmlToken(innerTags); + } + private static HtmlToken ParseItalic(string currLine, int index, string alreadyParsed = "", int alreadyEscaped = 0) { if (!IsValidEmTag(currLine, index, true)) @@ -166,28 +208,6 @@ private HtmlToken ParseUrl(string currLine, int index, string alreadyParsed = "" return new AHtmlToken(urlText, returnedValue.Item3, escaped + returnedValue.Item2, baseUrl); } - private HtmlToken ParseParagraph() - { - var innerTags = new List(); - - while (currLineIndex < plainMd.Length - && !string.IsNullOrWhiteSpace(CurrLine) - && GetLineTypeTag(CurrLine) == LineType.Simple) - { - var i = 0; - while (i < CurrLine.Length) - { - var tag = ParseTag(CurrLine, i); - var parsedToken = stringTagParserFuncMatch[tag].Invoke(CurrLine, i, "", 0); - i += parsedToken.Length; - innerTags.Add(parsedToken); - } - currLineIndex++; - } - - return new PHtmlToken(innerTags); - } - private static Tuple ParseInsideBracers(char closeBracer, int index, int escaped, string alreadyParsed, string currLine) { @@ -283,6 +303,8 @@ private static LineType GetLineTypeTag(string currLine) { if (currLine.StartsWith("#")) return LineType.Header; + if (currLine.StartsWith(" ") || currLine.StartsWith("\t")) + return LineType.CodeBlock; return LineType.Simple; } @@ -294,9 +316,8 @@ private IEnumerable TryParseToHtml() { var htmlToken = lineTagParserFuncMatch[GetLineTypeTag(CurrLine)].Invoke(); root.Add(htmlToken); - do + while (currLineIndex < plainMd.Length && string.IsNullOrWhiteSpace(CurrLine) && string.IsNullOrEmpty(CurrLine)) currLineIndex++; - while (currLineIndex < plainMd.Length && string.IsNullOrWhiteSpace(CurrLine)); } return root; diff --git a/Markdown/Test/MD_Should.cs b/Markdown/Test/MD_Should.cs index e1848e913..0e6538ead 100644 --- a/Markdown/Test/MD_Should.cs +++ b/Markdown/Test/MD_Should.cs @@ -71,7 +71,7 @@ public string ParseStrongTagCorrectrly(string plainMd) [TestCase("[url](www.url.com)", "", ExpectedResult = "

url

")] [TestCase("[url](/url)", "www.base.com", ExpectedResult = "

url

")] [TestCase("[url](www.url.com)\n[url](/url)", "www.base.com", - ExpectedResult = "

url

url

")] + ExpectedResult = "

url\nurl

")] public string ParseUrlTagCorrectrly(string plainMd, string baseUrl) { return new Md(plainMd, baseUrl).Render(); @@ -94,13 +94,13 @@ public string ParseWithDefinedCss(string plainMd, string cssClassName, string cs } [TestCase("asd", ExpectedResult = "

asd

")] - [TestCase("q\nb\nc", ExpectedResult = "

q

b

c

")] + [TestCase("q\nb\nc", ExpectedResult = "

q\nb\nc

")] + [TestCase("q\n\nb", ExpectedResult = "

q

b

")] public string ParseParagraphsCorrectly(string plainMd) { return new Md(plainMd).Render(); } - [TestCase("r _i_ r _i_", ExpectedResult = "

r i r i

")] [TestCase("r __b__ r __b__", ExpectedResult = "

r b r b

")] [TestCase("_i_ __b__ r", ExpectedResult = "

i b r

")] @@ -116,6 +116,14 @@ public string ParseHeaderCorrectly(string plainMd) return new Md(plainMd).Render(); } + [TestCase(" This is a code block.", ExpectedResult = "
This is a code block.
")] + [TestCase("Here is an example of AppleScript:\n\ttell application \"Foo\"\n\t\tbeep\n\tend tell", + ExpectedResult = "

Here is an example of AppleScript:

tell application \"Foo\"\n\tbeep\nend tell
")] + public string ParseCodeBlocksCorrectly(string plainMd) + { + return new Md(plainMd).Render(); + } + private static string GenerateMdTag(Tag tag, int length) { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; diff --git a/Markdown/Tokens/CodeHtmlToken.cs b/Markdown/Tokens/CodeHtmlToken.cs new file mode 100644 index 000000000..924046cdd --- /dev/null +++ b/Markdown/Tokens/CodeHtmlToken.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace Markdown.Tokens +{ + public class CodeHtmlToken : HtmlToken + { + public CodeHtmlToken(string data) : base("code", data, 0) + { + } + + public CodeHtmlToken(List parsedTokens) : base("code", parsedTokens, 0) + { + } + + public override string Render(CssClassInfo cssClassInfo) + { + return InsertInToTags("pre", InsertInToTags(Data, cssClassInfo), cssClassInfo); + } + } +} \ No newline at end of file diff --git a/Markdown/Tokens/HtmlToken.cs b/Markdown/Tokens/HtmlToken.cs index 5704a3a36..cd15b314c 100644 --- a/Markdown/Tokens/HtmlToken.cs +++ b/Markdown/Tokens/HtmlToken.cs @@ -27,12 +27,20 @@ protected HtmlToken(string tag, List parsedTokens, int escapedCharact { Tag = tag; ParsedTokens = parsedTokens; - this.EscapedCharacters = escapedCharacters; + EscapedCharacters = escapedCharacters; + } + + protected virtual string InsertInToTags(string dataToInsert, CssClassInfo cssClassInfo) + { + return IsTagged + ? InsertInToTags(Tag, dataToInsert, cssClassInfo) + : dataToInsert; } - protected virtual string InsertInToTags(string dataToInsert, CssClassInfo cssClassInfo) => IsTagged - ? $"<{Tag}{GetCssClassDef(cssClassInfo)}>{dataToInsert}" - : dataToInsert; + protected virtual string InsertInToTags(string tag, string dataToInsert, CssClassInfo cssClassInfo) + { + return $"<{tag}{GetCssClassDef(cssClassInfo)}>{dataToInsert}"; + } protected static string GetCssClassDef(CssClassInfo cssClassInfo) { diff --git a/Markdown/Tokens/PHtmlToken.cs b/Markdown/Tokens/PHtmlToken.cs index 4f09f0c1c..6d9bf5254 100644 --- a/Markdown/Tokens/PHtmlToken.cs +++ b/Markdown/Tokens/PHtmlToken.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace Markdown.Tokens { @@ -11,5 +12,6 @@ public PHtmlToken(string data) : base("p", data, 0) public PHtmlToken(List parsedTokens) : base("p", parsedTokens, 0) { } + } } \ No newline at end of file From 05b8c3539242a26120993dc32201662bf1ec1ca2 Mon Sep 17 00:00:00 2001 From: Griboedoff Date: Sat, 26 Nov 2016 14:23:00 +0500 Subject: [PATCH 7/8] add list parsing --- Markdown/LineType.cs | 1 + Markdown/Markdown.csproj | 2 ++ Markdown/Md.cs | 47 ++++++++++++++++++++++--- Markdown/Test/MD_Should.cs | 28 ++++++++++++--- Markdown/Tokens/ListItemHtmlToken.cs | 16 +++++++++ Markdown/Tokens/OrderedListHtmlToken.cs | 15 ++++++++ Markdown/Tokens/PHtmlToken.cs | 5 ++- 7 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 Markdown/Tokens/ListItemHtmlToken.cs create mode 100644 Markdown/Tokens/OrderedListHtmlToken.cs diff --git a/Markdown/LineType.cs b/Markdown/LineType.cs index 614e7bd43..57e5235ce 100644 --- a/Markdown/LineType.cs +++ b/Markdown/LineType.cs @@ -4,6 +4,7 @@ public enum LineType { Header, CodeBlock, + OrderedList, Simple } } \ No newline at end of file diff --git a/Markdown/Markdown.csproj b/Markdown/Markdown.csproj index eb5a95a6d..17a2d2fab 100644 --- a/Markdown/Markdown.csproj +++ b/Markdown/Markdown.csproj @@ -66,6 +66,8 @@ + + diff --git a/Markdown/Md.cs b/Markdown/Md.cs index c2aa3d15f..e0e4a8727 100644 --- a/Markdown/Md.cs +++ b/Markdown/Md.cs @@ -26,7 +26,13 @@ public class Md private readonly Dictionary> lineTagParserFuncMatch; private int currLineIndex; - private string CurrLine => plainMd[currLineIndex]; + + private string CurrLine + { + get { return plainMd[currLineIndex]; } + set { plainMd[currLineIndex] = value; } + } + private bool IsInPlainMd => currLineIndex < plainMd.Length; public Md(string plainMd, string baseUrl = "", CssClassInfo cssClassInfo = null) @@ -47,7 +53,8 @@ public Md(string plainMd, string baseUrl = "", CssClassInfo cssClassInfo = null) { [LineType.Header] = ParseHeader, [LineType.Simple] = ParseParagraph, - [LineType.CodeBlock] = ParseCodeBlock + [LineType.CodeBlock] = ParseCodeBlock, + [LineType.OrderedList] = ParseOrderedList }; currLineIndex = 0; @@ -55,8 +62,7 @@ public Md(string plainMd, string baseUrl = "", CssClassInfo cssClassInfo = null) private HtmlToken ParseHeader() { - var headerText = CurrLine.Replace("#", ""); - headerText = headerText.Replace("\\", ""); + var headerText = CurrLine.Replace("#", "").Replace("\\", ""); var headerImportance = CurrLine.Length - headerText.Length; @@ -73,7 +79,6 @@ private HtmlToken ParseCodeBlock() builder.Append(CurrLine.Substring(CurrLine.StartsWith("\t") ? 1 : 4)); builder.Append("\n"); currLineIndex++; - } builder.Remove(builder.Length - 1, 1); @@ -81,6 +86,34 @@ private HtmlToken ParseCodeBlock() return new CodeHtmlToken(builder.ToString()); } + private HtmlToken ParseOrderedList() + { + var listItemsTokens = new List(); + + while (IsInPlainMd && GetLineTypeTag(CurrLine) == LineType.OrderedList) + listItemsTokens.Add(ParseListItem()); + + return new OrderedListHtmlToken(listItemsTokens); + } + + private HtmlToken ParseListItem() + { + CurrLine = CurrLine.Substring(CurrLine.IndexOf(' ') + 1); + + var parsedToken = ParseParagraph(); + + var isParagraph = false; + if (currLineIndex != plainMd.Length && string.IsNullOrWhiteSpace(CurrLine)) + { + isParagraph = true; + currLineIndex++; + } + + return isParagraph + ? new ListItemHtmlToken(new List {parsedToken}) + : ((PHtmlToken) parsedToken).ToListItem(); + } + private HtmlToken ParseParagraph() { var innerTags = new List(); @@ -89,6 +122,7 @@ private HtmlToken ParseParagraph() { if (innerTags.Count != 0) innerTags.Add(new EmptyHtmlToken("\n", 0)); + var i = 0; while (i < CurrLine.Length) { @@ -97,6 +131,7 @@ private HtmlToken ParseParagraph() i += parsedToken.Length; innerTags.Add(parsedToken); } + currLineIndex++; } @@ -305,6 +340,8 @@ private static LineType GetLineTypeTag(string currLine) return LineType.Header; if (currLine.StartsWith(" ") || currLine.StartsWith("\t")) return LineType.CodeBlock; + if (char.IsDigit(currLine[0])) + return LineType.OrderedList; return LineType.Simple; } diff --git a/Markdown/Test/MD_Should.cs b/Markdown/Test/MD_Should.cs index 0e6538ead..31e33d655 100644 --- a/Markdown/Test/MD_Should.cs +++ b/Markdown/Test/MD_Should.cs @@ -109,17 +109,35 @@ public string ParseMixedTagsCorrectrly(string plainMd) return new Md(plainMd).Render(); } - [TestCase("##qwe", ExpectedResult = "

qwe

")] - [TestCase("qwe\n##qwe\nqwe", ExpectedResult = "

qwe

qwe

qwe

")] + [TestCase("##qwe", ExpectedResult = "

qwe

", TestName = "Simple header")] + [TestCase("qwe\n##qwe\nqwe", ExpectedResult = "

qwe

qwe

qwe

", + TestName = "Heaser with paragraphs") + ] public string ParseHeaderCorrectly(string plainMd) { return new Md(plainMd).Render(); } - [TestCase(" This is a code block.", ExpectedResult = "
This is a code block.
")] + [TestCase(" This is a code block.", ExpectedResult = "
This is a code block.
", + TestName = "One line code block")] [TestCase("Here is an example of AppleScript:\n\ttell application \"Foo\"\n\t\tbeep\n\tend tell", - ExpectedResult = "

Here is an example of AppleScript:

tell application \"Foo\"\n\tbeep\nend tell
")] - public string ParseCodeBlocksCorrectly(string plainMd) + ExpectedResult = + "

Here is an example of AppleScript:

tell application \"Foo\"\n\tbeep\nend tell
", + TestName = "Multiline code block")] + [TestCase("\t__thisIsNotTag__", ExpectedResult = "
__thisIsNotTag__
", + TestName = "tags not render in code blocks")] + public string ParseCodeBlocksCorrectly(string plainMd) + { + return new Md(plainMd).Render(); + } + + [TestCase("1. Bird\n1. McHale\n1. Parish", + ExpectedResult = "
  1. Bird
  2. McHale
  3. Parish
", + TestName = "List w/o paragraphs")] + [TestCase("1. Bird\n\n1. McHale\n1. Parish", + ExpectedResult = "
  1. Bird

  2. McHale
  3. Parish
", + TestName = "List with paragraphs")] + public string ParseOrderedListCorrectly(string plainMd) { return new Md(plainMd).Render(); } diff --git a/Markdown/Tokens/ListItemHtmlToken.cs b/Markdown/Tokens/ListItemHtmlToken.cs new file mode 100644 index 000000000..34a898448 --- /dev/null +++ b/Markdown/Tokens/ListItemHtmlToken.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Markdown.Tokens +{ + public class ListItemHtmlToken : HtmlToken + { + public ListItemHtmlToken(string data) : base("li", data, 0) + { + } + + public ListItemHtmlToken(List parsedTokens) : base("li", parsedTokens, 0) + { + } + } +} \ No newline at end of file diff --git a/Markdown/Tokens/OrderedListHtmlToken.cs b/Markdown/Tokens/OrderedListHtmlToken.cs new file mode 100644 index 000000000..b9bbc7977 --- /dev/null +++ b/Markdown/Tokens/OrderedListHtmlToken.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Markdown.Tokens +{ + public class OrderedListHtmlToken : HtmlToken + { + public OrderedListHtmlToken(string data) : base("ol", data, 0) + { + } + + public OrderedListHtmlToken(List parsedTokens) : base("ol", parsedTokens, 0) + { + } + } +} \ No newline at end of file diff --git a/Markdown/Tokens/PHtmlToken.cs b/Markdown/Tokens/PHtmlToken.cs index 6d9bf5254..c2c61ac93 100644 --- a/Markdown/Tokens/PHtmlToken.cs +++ b/Markdown/Tokens/PHtmlToken.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; namespace Markdown.Tokens { @@ -13,5 +12,9 @@ public PHtmlToken(List parsedTokens) : base("p", parsedTokens, 0) { } + public ListItemHtmlToken ToListItem() + { + return new ListItemHtmlToken(ParsedTokens); + } } } \ No newline at end of file From 842986fd5db9ff1791c4ab4d99d72e6e8ceaad01 Mon Sep 17 00:00:00 2001 From: Griboedoff Date: Sat, 26 Nov 2016 15:07:27 +0500 Subject: [PATCH 8/8] fix p tag in all test add trim p tag --- Markdown/Test/MD_Should.cs | 77 +++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/Markdown/Test/MD_Should.cs b/Markdown/Test/MD_Should.cs index 31e33d655..63f14d3ec 100644 --- a/Markdown/Test/MD_Should.cs +++ b/Markdown/Test/MD_Should.cs @@ -10,71 +10,71 @@ namespace Markdown.Test [TestFixture] internal class Md_Should { - [TestCase("qwe asd zxc", ExpectedResult = "

qwe asd zxc

")] + [TestCase("qwe asd zxc", ExpectedResult = "qwe asd zxc")] public string ParseNoMarkup(string plainMd) { - return new Md(plainMd).Render(); + return TrimPTag(new Md(plainMd).Render()); } - [TestCase("_asd_", ExpectedResult = "

asd

")] - [TestCase("_a s d_", ExpectedResult = "

a s d

")] - [TestCase("_1_2_3_", ExpectedResult = "

1_2_3

")] - [TestCase("_a_ _s d_", ExpectedResult = "

a s d

")] - [TestCase("_a_ _s d", ExpectedResult = "

a _s d

")] - [TestCase("_aas__abc__abc_", ExpectedResult = "

aas__abc__abc

")] + [TestCase("_asd_", ExpectedResult = "asd")] + [TestCase("_a s d_", ExpectedResult = "a s d")] + [TestCase("_1_2_3_", ExpectedResult = "1_2_3")] + [TestCase("_a_ _s d_", ExpectedResult = "a s d")] + [TestCase("_a_ _s d", ExpectedResult = "a _s d")] + [TestCase("_aas__abc__abc_", ExpectedResult = "aas__abc__abc")] public string ParseEmTagCorrectly(string plainMd) { - return new Md(plainMd).Render(); + return TrimPTag(new Md(plainMd).Render()); } - [TestCase("_ d_", ExpectedResult = "

_ d_

")] - [TestCase("_a _", ExpectedResult = "

_a _

")] - [TestCase("__ d__", ExpectedResult = "

__ d__

")] - [TestCase("__a __", ExpectedResult = "

__a __

")] - [TestCase("_ abc_abc_", ExpectedResult = "

_ abcabc

")] + [TestCase("_ d_", ExpectedResult = "_ d_")] + [TestCase("_a _", ExpectedResult = "_a _")] + [TestCase("__ d__", ExpectedResult = "__ d__")] + [TestCase("__a __", ExpectedResult = "__a __")] + [TestCase("_ abc_abc_", ExpectedResult = "_ abcabc")] public string ParseTrailingWhitespaceCorrectly(string plainMd) { - return new Md(plainMd).Render(); + return TrimPTag(new Md(plainMd).Render()); } - [TestCase("_ab cd", ExpectedResult = "

_ab cd

")] - [TestCase("__ab cd", ExpectedResult = "

__ab cd

")] + [TestCase("_ab cd", ExpectedResult = "_ab cd")] + [TestCase("__ab cd", ExpectedResult = "__ab cd")] public string ParseNoMarkup_IfMissingCloseTag(string plainMd) { - return new Md(plainMd).Render(); + return TrimPTag(new Md(plainMd).Render()); } - [TestCase(@"_a\_b_", ExpectedResult = "

a_b

")] - [TestCase(@"__a\_b__", ExpectedResult = "

a_b

")] - [TestCase(@"a\_b", ExpectedResult = "

a_b

")] + [TestCase(@"_a\_b_", ExpectedResult = "a_b")] + [TestCase(@"__a\_b__", ExpectedResult = "a_b")] + [TestCase(@"a\_b", ExpectedResult = "a_b")] public string ParseEscapedCorrectly(string plainMd) { - return new Md(plainMd).Render(); + return TrimPTag(new Md(plainMd).Render()); } - [TestCase("__abc_abc", ExpectedResult = "

__abc_abc

")] - [TestCase("__abc_abc_", ExpectedResult = "

__abcabc

")] - [TestCase("_abc__abc", ExpectedResult = "

_abc__abc

")] - [TestCase("_abc__abc__", ExpectedResult = "

_abc__abc__

")] + [TestCase("__abc_abc", ExpectedResult = "__abc_abc")] + [TestCase("__abc_abc_", ExpectedResult = "__abcabc")] + [TestCase("_abc__abc", ExpectedResult = "_abc__abc")] + [TestCase("_abc__abc__", ExpectedResult = "_abc__abc__")] public string ParseUnpairTags(string plainMd) { - return new Md(plainMd).Render(); + return TrimPTag(new Md(plainMd).Render()); } - [TestCase("__abc__", ExpectedResult = "

abc

")] - [TestCase("__abc_abc_abc__", ExpectedResult = "

abcabcabc

")] + [TestCase("__abc__", ExpectedResult = "abc")] + [TestCase("__abc_abc_abc__", ExpectedResult = "abcabcabc")] public string ParseStrongTagCorrectrly(string plainMd) { - return new Md(plainMd).Render(); + return TrimPTag(new Md(plainMd).Render()); } - [TestCase("[url](www.url.com)", "", ExpectedResult = "

url

")] - [TestCase("[url](/url)", "www.base.com", ExpectedResult = "

url

")] + [TestCase("[url](www.url.com)", "", ExpectedResult = "url")] + [TestCase("[url](/url)", "www.base.com", ExpectedResult = "url")] [TestCase("[url](www.url.com)\n[url](/url)", "www.base.com", - ExpectedResult = "

url\nurl

")] + ExpectedResult = "url\nurl")] public string ParseUrlTagCorrectrly(string plainMd, string baseUrl) { - return new Md(plainMd, baseUrl).Render(); + return TrimPTag(new Md(plainMd, baseUrl).Render()); } [TestCase("_asd_", "css", "", ExpectedResult = "

asd

", TestName = "No def") @@ -142,6 +142,15 @@ public string ParseOrderedListCorrectly(string plainMd) return new Md(plainMd).Render(); } + private static string TrimPTag(string html) + { + if (html.StartsWith("

")) + html = html.Substring(3); + if (html.EndsWith("

")) + html = html.Substring(0, html.Length - 4); + return html; + } + private static string GenerateMdTag(Tag tag, int length) { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";