mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-07 21:42:25 +00:00
Compare commits
221 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0ee97a803 | ||
|
|
63ce549ea2 | ||
|
|
0faf0ef430 | ||
|
|
cdd4b40469 | ||
|
|
54d85ebac6 | ||
|
|
64875c3dd9 | ||
|
|
4c92fe5a3b | ||
|
|
27f625f15b | ||
|
|
aca085703e | ||
|
|
8aa0948b20 | ||
|
|
8ce6f4d9ad | ||
|
|
3a47a5115a | ||
|
|
a8737e8481 | ||
|
|
f56b8e6ba7 | ||
|
|
0a0040450f | ||
|
|
fb12be5ab0 | ||
|
|
2277596e2e | ||
|
|
a10f6f6b8c | ||
|
|
203cfd6508 | ||
|
|
5fefcbb5b3 | ||
|
|
7ad7b55c48 | ||
|
|
e9ea103e32 | ||
|
|
c680910828 | ||
|
|
4526d886c8 | ||
|
|
397de86e2f | ||
|
|
f985750b82 | ||
|
|
00c175a79c | ||
|
|
6204095261 | ||
|
|
63fddf4511 | ||
|
|
c964659085 | ||
|
|
eabfe74e92 | ||
|
|
bd1dcd952c | ||
|
|
54e2514778 | ||
|
|
4b7a4d21de | ||
|
|
6b1399ba23 | ||
|
|
07467d6c30 | ||
|
|
2f9588498c | ||
|
|
80ed85e2a8 | ||
|
|
b2306db388 | ||
|
|
8db238797b | ||
|
|
780e16a9c9 | ||
|
|
8fdc0d59d7 | ||
|
|
f1cd0cb1b8 | ||
|
|
a724783e3f | ||
|
|
1c862a1e07 | ||
|
|
35d3160ad2 | ||
|
|
e0a2f9e52d | ||
|
|
c9ba236dbc | ||
|
|
0c9b5dddc9 | ||
|
|
bd2bb98631 | ||
|
|
da0ba34165 | ||
|
|
51c5bec315 | ||
|
|
13bdab4570 | ||
|
|
6b9433c7d8 | ||
|
|
eedbb494fc | ||
|
|
9d36a74312 | ||
|
|
4009c89321 | ||
|
|
6b433d9352 | ||
|
|
f6e6001d94 | ||
|
|
1474b7b29a | ||
|
|
61b29b6d41 | ||
|
|
6684c8257c | ||
|
|
247cd92926 | ||
|
|
404a94f284 | ||
|
|
1cc8a40473 | ||
|
|
dc4968d5ab | ||
|
|
b2b36038ff | ||
|
|
4a57035aec | ||
|
|
1752178631 | ||
|
|
39c05f34d1 | ||
|
|
640196a18f | ||
|
|
4f9119fc96 | ||
|
|
5eb600afc3 | ||
|
|
64ebff4012 | ||
|
|
9e5d30cd4c | ||
|
|
4324caaaea | ||
|
|
46c2d49243 | ||
|
|
80b1cf6020 | ||
|
|
264f7f2132 | ||
|
|
80ec8da7d3 | ||
|
|
1e2399669d | ||
|
|
ab53969f06 | ||
|
|
168217b4e0 | ||
|
|
a3ce1903c1 | ||
|
|
db1021a979 | ||
|
|
cbd00a45af | ||
|
|
fef4719e41 | ||
|
|
ae25a8f12c | ||
|
|
bb5403c795 | ||
|
|
325fb7158e | ||
|
|
67416e4b45 | ||
|
|
8b48accb7e | ||
|
|
bb42ee42ca | ||
|
|
d17660fe5d | ||
|
|
19e409ceca | ||
|
|
59df6d1a2e | ||
|
|
763ed32212 | ||
|
|
8aa522c4bf | ||
|
|
9031be96f8 | ||
|
|
4bc2e847d5 | ||
|
|
80d4e6f344 | ||
|
|
b6e38a0a96 | ||
|
|
1d79ad8436 | ||
|
|
383b197490 | ||
|
|
7aeee0681f | ||
|
|
e017adae84 | ||
|
|
7035aed74a | ||
|
|
21b6a3869a | ||
|
|
aa92e59b2c | ||
|
|
5f87e56651 | ||
|
|
9934c0033a | ||
|
|
5d1da2deac | ||
|
|
4e424c43fb | ||
|
|
9fbe5eb21d | ||
|
|
fb4abdfae3 | ||
|
|
7ab34d5cef | ||
|
|
b576b08332 | ||
|
|
9c43b802bd | ||
|
|
7dda864e4a | ||
|
|
b3953dbe23 | ||
|
|
453e8239d2 | ||
|
|
8e8b95c3bb | ||
|
|
35b64052d6 | ||
|
|
ef495da097 | ||
|
|
a6b9c9a41e | ||
|
|
5bb2d92180 | ||
|
|
929ec36c76 | ||
|
|
8e17d9c94e | ||
|
|
a11cd8c28c | ||
|
|
68d2a20d20 | ||
|
|
e3531413e1 | ||
|
|
ef534224f8 | ||
|
|
2f3e1451b8 | ||
|
|
a34e257d2c | ||
|
|
9d83631cfe | ||
|
|
61b3ffde91 | ||
|
|
bebdf0179e | ||
|
|
ab5e8ae9e2 | ||
|
|
c8da430134 | ||
|
|
0e8c312fda | ||
|
|
4814e9cea5 | ||
|
|
068a6e7af5 | ||
|
|
e4f2892a23 | ||
|
|
c2cfb05d8d | ||
|
|
813647ca10 | ||
|
|
83357dc929 | ||
|
|
420aa79a48 | ||
|
|
24be820827 | ||
|
|
694a764f96 | ||
|
|
bb1da73da2 | ||
|
|
e6b591c035 | ||
|
|
5a19cdfebb | ||
|
|
34e439f494 | ||
|
|
b0602a7bb0 | ||
|
|
d7558f0442 | ||
|
|
eac0ab3cca | ||
|
|
147cd48a8d | ||
|
|
1b44256fc0 | ||
|
|
db2856daf7 | ||
|
|
9beb0c1613 | ||
|
|
ef343158b7 | ||
|
|
de5526877d | ||
|
|
b00d75607b | ||
|
|
8c37a1bef5 | ||
|
|
7a9405ec9e | ||
|
|
6506e4594c | ||
|
|
7b20299d2b | ||
|
|
55eaadce67 | ||
|
|
87269d88b9 | ||
|
|
fb8162f5d7 | ||
|
|
a04aa8a4de | ||
|
|
352443d9cd | ||
|
|
cd798b8d95 | ||
|
|
c5b260f708 | ||
|
|
2583463ea2 | ||
|
|
777ac71bd1 | ||
|
|
810ae49cc7 | ||
|
|
225e308438 | ||
|
|
05dd543d16 | ||
|
|
c711201b34 | ||
|
|
47b3ac5d99 | ||
|
|
49aa856f52 | ||
|
|
5e15575929 | ||
|
|
c9fc608598 | ||
|
|
6ba3c3d683 | ||
|
|
d15edb79fa | ||
|
|
fa3b67342d | ||
|
|
033f156b2b | ||
|
|
f3db5e882e | ||
|
|
c9365f3551 | ||
|
|
9ecb5b9950 | ||
|
|
95fb53cbc9 | ||
|
|
9d82088e03 | ||
|
|
340b75b557 | ||
|
|
0b79389b14 | ||
|
|
111d3d3362 | ||
|
|
ac6ceb23e8 | ||
|
|
d31fa47cd1 | ||
|
|
d7f6a94f12 | ||
|
|
dbdd752b73 | ||
|
|
4c2b46e0fc | ||
|
|
68d12d0212 | ||
|
|
fa1c117011 | ||
|
|
6b1c5bc816 | ||
|
|
e2cafc6b3d | ||
|
|
aff7604b4b | ||
|
|
68530aa4e0 | ||
|
|
bfc1152b8a | ||
|
|
37af8f8ecb | ||
|
|
6792bffb5e | ||
|
|
976855a4c3 | ||
|
|
acf2ba9502 | ||
|
|
cadbc67825 | ||
|
|
0d86a93200 | ||
|
|
f78b5c83cd | ||
|
|
0234d60d74 | ||
|
|
cd18087e29 | ||
|
|
30f670bf5f | ||
|
|
c73785372b | ||
|
|
147698daab | ||
|
|
2b07e9a5b9 |
21
changelog.md
21
changelog.md
@@ -1,5 +1,18 @@
|
||||
# Changelog
|
||||
|
||||
## 0.26.0 (27 Aug 2021)
|
||||
- Fix rendering diff between line endings ([PR #560](https://github.com/lunet-io/markdig/pull/560))
|
||||
- Make Mathematics extension respect EnableHtml* options ([PR #570](https://github.com/lunet-io/markdig/pull/570))
|
||||
|
||||
## 0.25.0 (10 June 2021)
|
||||
- Fix regression when parsing link reference definitions (#543)
|
||||
- Make digits in JiraKey's posible ([PR #548](https://github.com/lunet-io/markdig/pull/548))
|
||||
|
||||
## 0.24.0 (20 Mar 2021)
|
||||
- Add support for roundtrip Markdown ([PR #481](https://github.com/lunet-io/markdig/pull/481))
|
||||
- Introduction of nullability ([PR #522](https://github.com/lunet-io/markdig/pull/522) [PR #524](https://github.com/lunet-io/markdig/pull/524) [PR #525](https://github.com/lunet-io/markdig/pull/525) [PR #526](https://github.com/lunet-io/markdig/pull/526) [PR #527](https://github.com/lunet-io/markdig/pull/527))
|
||||
- Various internal cleanup and small performance improvements ([PR #521](https://github.com/lunet-io/markdig/pull/521) [PR #524](https://github.com/lunet-io/markdig/pull/524) [PR #525](https://github.com/lunet-io/markdig/pull/525) [PR #529](https://github.com/lunet-io/markdig/pull/529) [PR #531](https://github.com/lunet-io/markdig/pull/531) [PR #532](https://github.com/lunet-io/markdig/pull/532))
|
||||
|
||||
## 0.23.0 (16 Jan 2021)
|
||||
- Add depth limits to avoid pathological-case parsing times/StackOverflows (#500)
|
||||
- Breaking change: rename AutolineInlineParser to AutolinkInlineParser
|
||||
@@ -9,10 +22,10 @@
|
||||
|
||||
## 0.22.0 (05 Oct 2020)
|
||||
- Fix Setext headings in block quotes.
|
||||
- Fix tel: treated as autolink ([PR #478](https://github.com/lunet-io/markdig/pull/478)
|
||||
- Make Inline.FirstParentOfType public ([PR #474](https://github.com/lunet-io/markdig/pull/474)
|
||||
- Fix `&` to be parsed as a punctuation while it was detected as a html entity in certain cases ([PR #471](https://github.com/lunet-io/markdig/pull/471)
|
||||
- Add ParentBlock property to ContainerInline ([PR #468](https://github.com/lunet-io/markdig/pull/468)
|
||||
- Fix tel: treated as autolink ([PR #478](https://github.com/lunet-io/markdig/pull/478))
|
||||
- Make Inline.FirstParentOfType public ([PR #474](https://github.com/lunet-io/markdig/pull/474))
|
||||
- Fix `&` to be parsed as a punctuation while it was detected as a html entity in certain cases ([PR #471](https://github.com/lunet-io/markdig/pull/471))
|
||||
- Add ParentBlock property to ContainerInline ([PR #468](https://github.com/lunet-io/markdig/pull/468))
|
||||
|
||||
## 0.21.1 (17 Aug 2020)
|
||||
- Fix Markdig.Signed on GitHub Actions
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Markdig [](https://github.com/lunet-io/markdig/actions) [](https://coveralls.io/github/lunet-io/markdig?branch=master) [](https://www.nuget.org/packages/Markdig/) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRGHXBTP442JL)
|
||||
# Markdig [](https://github.com/lunet-io/markdig/actions) [](https://coveralls.io/github/xoofx/markdig?branch=master) [](https://www.nuget.org/packages/Markdig/) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRGHXBTP442JL)
|
||||
|
||||
<img align="right" width="160px" height="160px" src="img/markdig.png">
|
||||
|
||||
@@ -19,6 +19,7 @@ You can **try Markdig online** and compare it to other implementations on [babel
|
||||
- including **GFM fenced code blocks**.
|
||||
- **Extensible** architecture
|
||||
- Even the core Markdown/CommonMark parsing is pluggable, so it allows to disable builtin Markdown/Commonmark parsing (e.g [Disable HTML parsing](https://github.com/lunet-io/markdig/blob/7964bd0160d4c18e4155127a4c863d61ebd8944a/src/Markdig/MarkdownExtensions.cs#L306)) or change behaviour (e.g change matching `#` of a headers with `@`)
|
||||
- [**Roundtrip support**](./src/Markdig/Roundtrip.md): Parses trivia (whitespace, newlines and other characters) to support lossless parse ⭢ render roundtrip. This enables changing markdown documents without introducing undesired trivia changes.
|
||||
- Built-in with **20+ extensions**, including:
|
||||
- 2 kind of tables:
|
||||
- [**Pipe tables**](src/Markdig.Tests/Specs/PipeTableSpecs.md) (inspired from GitHub tables and [PanDoc - Pipe Tables](http://pandoc.org/README.html#extension-pipe_tables))
|
||||
|
||||
15266
src/Markdig.Tests/RoundtripSpecs/CommonMark.generated.cs
Normal file
15266
src/Markdig.Tests/RoundtripSpecs/CommonMark.generated.cs
Normal file
File diff suppressed because it is too large
Load Diff
9710
src/Markdig.Tests/RoundtripSpecs/CommonMark.md
Normal file
9710
src/Markdig.Tests/RoundtripSpecs/CommonMark.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs.Inlines
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestAutoLinkInline
|
||||
{
|
||||
[TestCase("<http://a>")]
|
||||
[TestCase(" <http://a>")]
|
||||
[TestCase("<http://a> ")]
|
||||
[TestCase(" <http://a> ")]
|
||||
[TestCase("<example@example.com>")]
|
||||
[TestCase(" <example@example.com>")]
|
||||
[TestCase("<example@example.com> ")]
|
||||
[TestCase(" <example@example.com> ")]
|
||||
[TestCase("p http://a p")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs.Inlines
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestBackslashEscapeInline
|
||||
{
|
||||
[TestCase(@"\!")]
|
||||
[TestCase(@"\""")]
|
||||
[TestCase(@"\#")]
|
||||
[TestCase(@"\$")]
|
||||
[TestCase(@"\&")]
|
||||
[TestCase(@"\'")]
|
||||
[TestCase(@"\(")]
|
||||
[TestCase(@"\)")]
|
||||
[TestCase(@"\*")]
|
||||
[TestCase(@"\+")]
|
||||
[TestCase(@"\,")]
|
||||
[TestCase(@"\-")]
|
||||
[TestCase(@"\.")]
|
||||
[TestCase(@"\/")]
|
||||
[TestCase(@"\:")]
|
||||
[TestCase(@"\;")]
|
||||
[TestCase(@"\<")]
|
||||
[TestCase(@"\=")]
|
||||
[TestCase(@"\>")]
|
||||
[TestCase(@"\?")]
|
||||
[TestCase(@"\@")]
|
||||
[TestCase(@"\[")]
|
||||
[TestCase(@"\\")]
|
||||
[TestCase(@"\]")]
|
||||
[TestCase(@"\^")]
|
||||
[TestCase(@"\_")]
|
||||
[TestCase(@"\`")]
|
||||
[TestCase(@"\{")]
|
||||
[TestCase(@"\|")]
|
||||
[TestCase(@"\}")]
|
||||
[TestCase(@"\~")]
|
||||
|
||||
// below test breaks visual studio
|
||||
//[TestCase(@"\!\""\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(@"# \#\#h1")]
|
||||
[TestCase(@"# \#\#h1\#")]
|
||||
public void TestHeading(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(@"`\``")]
|
||||
[TestCase(@"` \``")]
|
||||
[TestCase(@"`\` `")]
|
||||
[TestCase(@"` \` `")]
|
||||
[TestCase(@" ` \` `")]
|
||||
[TestCase(@"` \` ` ")]
|
||||
[TestCase(@" ` \` ` ")]
|
||||
public void TestCodeSpanInline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
77
src/Markdig.Tests/RoundtripSpecs/Inlines/TestCodeInline.cs
Normal file
77
src/Markdig.Tests/RoundtripSpecs/Inlines/TestCodeInline.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs.Inlines
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCodeInline
|
||||
{
|
||||
[TestCase("``")]
|
||||
[TestCase(" ``")]
|
||||
[TestCase("`` ")]
|
||||
[TestCase(" `` ")]
|
||||
|
||||
[TestCase("`c`")]
|
||||
[TestCase(" `c`")]
|
||||
[TestCase("`c` ")]
|
||||
[TestCase(" `c` ")]
|
||||
|
||||
[TestCase("` c`")]
|
||||
[TestCase(" ` c`")]
|
||||
[TestCase("` c` ")]
|
||||
[TestCase(" ` c` ")]
|
||||
|
||||
[TestCase("`c `")]
|
||||
[TestCase(" `c `")]
|
||||
[TestCase("`c ` ")]
|
||||
[TestCase(" `c ` ")]
|
||||
|
||||
[TestCase("`c``")] // 1, 2
|
||||
[TestCase("``c`")] // 2, 1
|
||||
[TestCase("``c``")] // 2, 2
|
||||
|
||||
[TestCase("```c``")] // 2, 3
|
||||
[TestCase("``c```")] // 3, 2
|
||||
[TestCase("```c```")] // 3, 3
|
||||
|
||||
[TestCase("```c````")] // 3, 4
|
||||
[TestCase("````c```")] // 4, 3
|
||||
[TestCase("````c````")] // 4, 4
|
||||
|
||||
[TestCase("```a``` p")]
|
||||
[TestCase("```a`b`c```")]
|
||||
[TestCase("```a``` p\n```a``` p")]
|
||||
|
||||
[TestCase("` a `")]
|
||||
[TestCase(" ` a `")]
|
||||
[TestCase("` a ` ")]
|
||||
[TestCase(" ` a ` ")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("p `a` p")]
|
||||
[TestCase("p ``a`` p")]
|
||||
[TestCase("p ```a``` p")]
|
||||
[TestCase("p\n\n```a``` p")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("`\na\n`")]
|
||||
[TestCase("`\na\r`")]
|
||||
[TestCase("`\na\r\n`")]
|
||||
[TestCase("`\ra\r`")]
|
||||
[TestCase("`\ra\n`")]
|
||||
[TestCase("`\ra\r\n`")]
|
||||
[TestCase("`\r\na\n`")]
|
||||
[TestCase("`\r\na\r`")]
|
||||
[TestCase("`\r\na\r\n`")]
|
||||
public void Test_Newlines(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
132
src/Markdig.Tests/RoundtripSpecs/Inlines/TestEmphasisInline.cs
Normal file
132
src/Markdig.Tests/RoundtripSpecs/Inlines/TestEmphasisInline.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs.Inlines
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestEmphasisInline
|
||||
{
|
||||
[TestCase("_t_")]
|
||||
[TestCase("_t_t")]
|
||||
[TestCase("t_t_")]
|
||||
[TestCase("_t t_")]
|
||||
[TestCase("_t\tt_")]
|
||||
[TestCase("*t*")]
|
||||
[TestCase("t*t*")]
|
||||
[TestCase("*t*t")]
|
||||
[TestCase("*t t*")]
|
||||
[TestCase("*t\tt*")]
|
||||
|
||||
[TestCase(" _t_")]
|
||||
[TestCase(" _t_t")]
|
||||
[TestCase(" t_t_")]
|
||||
[TestCase(" _t t_")]
|
||||
[TestCase(" _t\tt_")]
|
||||
[TestCase(" *t*")]
|
||||
[TestCase(" t*t*")]
|
||||
[TestCase(" *t*t")]
|
||||
[TestCase(" *t t*")]
|
||||
[TestCase(" *t\tt*")]
|
||||
|
||||
[TestCase("_t_")]
|
||||
[TestCase("_t_t ")]
|
||||
[TestCase("t_t_ ")]
|
||||
[TestCase("_t t_ ")]
|
||||
[TestCase("_t\tt_ ")]
|
||||
[TestCase("*t* ")]
|
||||
[TestCase("t*t* ")]
|
||||
[TestCase("*t*t ")]
|
||||
[TestCase("*t t* ")]
|
||||
[TestCase("*t\tt* ")]
|
||||
|
||||
[TestCase(" _t_")]
|
||||
[TestCase(" _t_t ")]
|
||||
[TestCase(" t_t_ ")]
|
||||
[TestCase(" _t t_ ")]
|
||||
[TestCase(" _t\tt_ ")]
|
||||
[TestCase(" *t* ")]
|
||||
[TestCase(" t*t* ")]
|
||||
[TestCase(" *t*t ")]
|
||||
[TestCase(" *t t* ")]
|
||||
[TestCase(" *t\tt* ")]
|
||||
|
||||
[TestCase("_t_\t")]
|
||||
[TestCase("_t_t\t")]
|
||||
[TestCase("t_t_\t")]
|
||||
[TestCase("_t t_\t")]
|
||||
[TestCase("_t\tt_\t")]
|
||||
[TestCase("*t*\t")]
|
||||
[TestCase("t*t*\t")]
|
||||
[TestCase("*t*t\t")]
|
||||
[TestCase("*t t*\t")]
|
||||
[TestCase("*t\tt*\t")]
|
||||
public void Test_Emphasis(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("__t__")]
|
||||
[TestCase("__t__t")]
|
||||
[TestCase("t__t__")]
|
||||
[TestCase("__t t__")]
|
||||
[TestCase("__t\tt__")]
|
||||
[TestCase("**t**")]
|
||||
[TestCase("**t**t")]
|
||||
[TestCase("t**t**")]
|
||||
[TestCase("**t\tt**")]
|
||||
|
||||
[TestCase(" __t__")]
|
||||
[TestCase(" __t__t")]
|
||||
[TestCase(" t__t__")]
|
||||
[TestCase(" __t t__")]
|
||||
[TestCase(" __t\tt__")]
|
||||
[TestCase(" **t**")]
|
||||
[TestCase(" **t**t")]
|
||||
[TestCase(" t**t**")]
|
||||
[TestCase(" **t\tt**")]
|
||||
|
||||
[TestCase("__t__ ")]
|
||||
[TestCase("__t__t ")]
|
||||
[TestCase("t__t__ ")]
|
||||
[TestCase("__t t__ ")]
|
||||
[TestCase("__t\tt__ ")]
|
||||
[TestCase("**t** ")]
|
||||
[TestCase("**t**t ")]
|
||||
[TestCase("t**t** ")]
|
||||
[TestCase("**t\tt** ")]
|
||||
|
||||
[TestCase(" __t__ ")]
|
||||
[TestCase(" __t__t ")]
|
||||
[TestCase(" t__t__ ")]
|
||||
[TestCase(" __t t__ ")]
|
||||
[TestCase(" __t\tt__ ")]
|
||||
[TestCase(" **t** ")]
|
||||
[TestCase(" **t** t")]
|
||||
[TestCase(" t**t** ")]
|
||||
[TestCase(" **t\tt** ")]
|
||||
|
||||
[TestCase("__t__\t")]
|
||||
[TestCase("__t__t\t")]
|
||||
[TestCase("t__t__\t ")]
|
||||
[TestCase("__t t__\t ")]
|
||||
[TestCase("__t\tt__\t ")]
|
||||
[TestCase("**t**\t ")]
|
||||
[TestCase("**t**t\t ")]
|
||||
[TestCase("t**t**\t ")]
|
||||
[TestCase("**t\tt**\t ")]
|
||||
|
||||
[TestCase(" __t__\t ")]
|
||||
[TestCase(" __t__t\t ")]
|
||||
[TestCase(" t__t__\t ")]
|
||||
[TestCase(" __t t__\t ")]
|
||||
[TestCase(" __t\tt__\t ")]
|
||||
[TestCase(" **t**\t ")]
|
||||
[TestCase(" **t**\t t")]
|
||||
[TestCase(" t**t**\t ")]
|
||||
[TestCase(" **t\tt**\t ")]
|
||||
public void Test_StrongEmphasis(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs.Inlines
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <seealso cref="https://spec.commonmark.org/0.29/#entity-and-numeric-character-references"/>
|
||||
[TestFixture]
|
||||
public class TestHtmlEntityInline
|
||||
{
|
||||
[TestCase(">")]
|
||||
[TestCase("<")]
|
||||
[TestCase(" ")]
|
||||
[TestCase("♥")]
|
||||
[TestCase("*")]
|
||||
[TestCase("�")]
|
||||
[TestCase("Ӓ")]
|
||||
[TestCase("ಫ")]
|
||||
|
||||
[TestCase(" >")]
|
||||
[TestCase(" <")]
|
||||
[TestCase(" ")]
|
||||
[TestCase(" ♥")]
|
||||
[TestCase(" *")]
|
||||
[TestCase(" �")]
|
||||
[TestCase(" Ӓ")]
|
||||
[TestCase(" ಫ")]
|
||||
|
||||
[TestCase("> ")]
|
||||
[TestCase("< ")]
|
||||
[TestCase(" ")]
|
||||
[TestCase("♥ ")]
|
||||
[TestCase("* ")]
|
||||
[TestCase("� ")]
|
||||
[TestCase("Ӓ ")]
|
||||
[TestCase("ಫ ")]
|
||||
|
||||
[TestCase(" > ")]
|
||||
[TestCase(" < ")]
|
||||
[TestCase(" ")]
|
||||
[TestCase(" ♥ ")]
|
||||
[TestCase(" * ")]
|
||||
[TestCase(" � ")]
|
||||
[TestCase(" Ӓ ")]
|
||||
[TestCase(" ಫ ")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/Markdig.Tests/RoundtripSpecs/Inlines/TestHtmlInline.cs
Normal file
27
src/Markdig.Tests/RoundtripSpecs/Inlines/TestHtmlInline.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs.Inlines
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestHtmlInline
|
||||
{
|
||||
[TestCase("<em>f</em>")]
|
||||
[TestCase("<em> f</em>")]
|
||||
[TestCase("<em>f </em>")]
|
||||
[TestCase("<em> f </em>")]
|
||||
[TestCase("<b>p</b>")]
|
||||
[TestCase("<b></b>")]
|
||||
[TestCase("<b> </b>")]
|
||||
[TestCase("<b> </b>")]
|
||||
[TestCase("<b> </b>")]
|
||||
[TestCase("<b>\t</b>")]
|
||||
[TestCase("<b> \t</b>")]
|
||||
[TestCase("<b>\t </b>")]
|
||||
[TestCase("<b> \t </b>")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/Markdig.Tests/RoundtripSpecs/Inlines/TestImageInline.cs
Normal file
25
src/Markdig.Tests/RoundtripSpecs/Inlines/TestImageInline.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs.Inlines
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestImageInline
|
||||
{
|
||||
[TestCase("")]
|
||||
[TestCase(" ")]
|
||||
[TestCase(" ")]
|
||||
[TestCase("  ")]
|
||||
[TestCase(" ")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("paragraph ")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs.Inlines
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestLineBreakInline
|
||||
{
|
||||
[TestCase("p\n")]
|
||||
[TestCase("p\r\n")]
|
||||
[TestCase("p\r")]
|
||||
[TestCase("[]() ![]() `` ` ` ` ` ![]() ![]()")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
229
src/Markdig.Tests/RoundtripSpecs/Inlines/TestLinkInline.cs
Normal file
229
src/Markdig.Tests/RoundtripSpecs/Inlines/TestLinkInline.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs.Inlines
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestLinkInline
|
||||
{
|
||||
[TestCase("[a]")] // TODO: this is not a link but a paragraph
|
||||
[TestCase("[a]()")]
|
||||
|
||||
[TestCase("[](b)")]
|
||||
[TestCase(" [](b)")]
|
||||
[TestCase("[](b) ")]
|
||||
[TestCase(" [](b) ")]
|
||||
|
||||
[TestCase("[a](b)")]
|
||||
[TestCase(" [a](b)")]
|
||||
[TestCase("[a](b) ")]
|
||||
[TestCase(" [a](b) ")]
|
||||
|
||||
[TestCase("[ a](b)")]
|
||||
[TestCase(" [ a](b)")]
|
||||
[TestCase("[ a](b) ")]
|
||||
[TestCase(" [ a](b) ")]
|
||||
|
||||
[TestCase("[a ](b)")]
|
||||
[TestCase(" [a ](b)")]
|
||||
[TestCase("[a ](b) ")]
|
||||
[TestCase(" [a ](b) ")]
|
||||
|
||||
[TestCase("[ a ](b)")]
|
||||
[TestCase(" [ a ](b)")]
|
||||
[TestCase("[ a ](b) ")]
|
||||
[TestCase(" [ a ](b) ")]
|
||||
|
||||
// below cases are required for a full roundtrip but not have low prio for impl
|
||||
[TestCase("[]( b)")]
|
||||
[TestCase(" []( b)")]
|
||||
[TestCase("[]( b) ")]
|
||||
[TestCase(" []( b) ")]
|
||||
|
||||
[TestCase("[a]( b)")]
|
||||
[TestCase(" [a]( b)")]
|
||||
[TestCase("[a]( b) ")]
|
||||
[TestCase(" [a]( b) ")]
|
||||
|
||||
[TestCase("[ a]( b)")]
|
||||
[TestCase(" [ a]( b)")]
|
||||
[TestCase("[ a]( b) ")]
|
||||
[TestCase(" [ a]( b) ")]
|
||||
|
||||
[TestCase("[a ]( b)")]
|
||||
[TestCase(" [a ]( b)")]
|
||||
[TestCase("[a ]( b) ")]
|
||||
[TestCase(" [a ]( b) ")]
|
||||
|
||||
[TestCase("[ a ]( b)")]
|
||||
[TestCase(" [ a ]( b)")]
|
||||
[TestCase("[ a ]( b) ")]
|
||||
[TestCase(" [ a ]( b) ")]
|
||||
|
||||
[TestCase("[](b )")]
|
||||
[TestCase(" [](b )")]
|
||||
[TestCase("[](b ) ")]
|
||||
[TestCase(" [](b ) ")]
|
||||
|
||||
[TestCase("[a](b )")]
|
||||
[TestCase(" [a](b )")]
|
||||
[TestCase("[a](b ) ")]
|
||||
[TestCase(" [a](b ) ")]
|
||||
|
||||
[TestCase("[ a](b )")]
|
||||
[TestCase(" [ a](b )")]
|
||||
[TestCase("[ a](b ) ")]
|
||||
[TestCase(" [ a](b ) ")]
|
||||
|
||||
[TestCase("[a ](b )")]
|
||||
[TestCase(" [a ](b )")]
|
||||
[TestCase("[a ](b ) ")]
|
||||
[TestCase(" [a ](b ) ")]
|
||||
|
||||
[TestCase("[ a ](b )")]
|
||||
[TestCase(" [ a ](b )")]
|
||||
[TestCase("[ a ](b ) ")]
|
||||
[TestCase(" [ a ](b ) ")]
|
||||
|
||||
[TestCase("[]( b )")]
|
||||
[TestCase(" []( b )")]
|
||||
[TestCase("[]( b ) ")]
|
||||
[TestCase(" []( b ) ")]
|
||||
|
||||
[TestCase("[a]( b )")]
|
||||
[TestCase(" [a]( b )")]
|
||||
[TestCase("[a]( b ) ")]
|
||||
[TestCase(" [a]( b ) ")]
|
||||
|
||||
[TestCase("[ a]( b )")]
|
||||
[TestCase(" [ a]( b )")]
|
||||
[TestCase("[ a]( b ) ")]
|
||||
[TestCase(" [ a]( b ) ")]
|
||||
|
||||
[TestCase("[a ]( b )")]
|
||||
[TestCase(" [a ]( b )")]
|
||||
[TestCase("[a ]( b ) ")]
|
||||
[TestCase(" [a ]( b ) ")]
|
||||
|
||||
[TestCase("[ a ]( b )")]
|
||||
[TestCase(" [ a ]( b )")]
|
||||
[TestCase("[ a ]( b ) ")]
|
||||
[TestCase(" [ a ]( b ) ")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a](b \"t\") ")]
|
||||
[TestCase("[a](b \" t\") ")]
|
||||
[TestCase("[a](b \"t \") ")]
|
||||
[TestCase("[a](b \" t \") ")]
|
||||
|
||||
[TestCase("[a](b \"t\") ")]
|
||||
[TestCase("[a](b \" t\") ")]
|
||||
[TestCase("[a](b \"t \") ")]
|
||||
[TestCase("[a](b \" t \") ")]
|
||||
|
||||
[TestCase("[a](b \"t\" ) ")]
|
||||
[TestCase("[a](b \" t\" ) ")]
|
||||
[TestCase("[a](b \"t \" ) ")]
|
||||
[TestCase("[a](b \" t \" ) ")]
|
||||
|
||||
[TestCase("[a](b \"t\" ) ")]
|
||||
[TestCase("[a](b \" t\" ) ")]
|
||||
[TestCase("[a](b \"t \" ) ")]
|
||||
[TestCase("[a](b \" t \" ) ")]
|
||||
|
||||
[TestCase("[a](b 't') ")]
|
||||
[TestCase("[a](b ' t') ")]
|
||||
[TestCase("[a](b 't ') ")]
|
||||
[TestCase("[a](b ' t ') ")]
|
||||
|
||||
[TestCase("[a](b 't') ")]
|
||||
[TestCase("[a](b ' t') ")]
|
||||
[TestCase("[a](b 't ') ")]
|
||||
[TestCase("[a](b ' t ') ")]
|
||||
|
||||
[TestCase("[a](b 't' ) ")]
|
||||
[TestCase("[a](b ' t' ) ")]
|
||||
[TestCase("[a](b 't ' ) ")]
|
||||
[TestCase("[a](b ' t ' ) ")]
|
||||
|
||||
[TestCase("[a](b 't' ) ")]
|
||||
[TestCase("[a](b ' t' ) ")]
|
||||
[TestCase("[a](b 't ' ) ")]
|
||||
[TestCase("[a](b ' t ' ) ")]
|
||||
|
||||
[TestCase("[a](b (t)) ")]
|
||||
[TestCase("[a](b ( t)) ")]
|
||||
[TestCase("[a](b (t )) ")]
|
||||
[TestCase("[a](b ( t )) ")]
|
||||
|
||||
[TestCase("[a](b (t)) ")]
|
||||
[TestCase("[a](b ( t)) ")]
|
||||
[TestCase("[a](b (t )) ")]
|
||||
[TestCase("[a](b ( t )) ")]
|
||||
|
||||
[TestCase("[a](b (t) ) ")]
|
||||
[TestCase("[a](b ( t) ) ")]
|
||||
[TestCase("[a](b (t ) ) ")]
|
||||
[TestCase("[a](b ( t ) ) ")]
|
||||
|
||||
[TestCase("[a](b (t) ) ")]
|
||||
[TestCase("[a](b ( t) ) ")]
|
||||
[TestCase("[a](b (t ) ) ")]
|
||||
[TestCase("[a](b ( t ) ) ")]
|
||||
public void Test_Title(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a](<>)")]
|
||||
[TestCase("[a]( <>)")]
|
||||
[TestCase("[a](<> )")]
|
||||
[TestCase("[a]( <> )")]
|
||||
|
||||
[TestCase("[a](< >)")]
|
||||
[TestCase("[a]( < >)")]
|
||||
[TestCase("[a](< > )")]
|
||||
[TestCase("[a]( < > )")]
|
||||
|
||||
[TestCase("[a](<b>)")]
|
||||
[TestCase("[a](<b >)")]
|
||||
[TestCase("[a](< b>)")]
|
||||
[TestCase("[a](< b >)")]
|
||||
|
||||
[TestCase("[a](<b b>)")]
|
||||
[TestCase("[a](<b b >)")]
|
||||
[TestCase("[a](< b b >)")]
|
||||
public void Test_PointyBrackets(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[*a*][a]")]
|
||||
[TestCase("[a][b]")]
|
||||
[TestCase("[a][]")]
|
||||
[TestCase("[a]")]
|
||||
public void Test_Inlines(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
// | [ a ]( b " t " ) |
|
||||
[TestCase(" [ a ]( b \" t \" ) ")]
|
||||
[TestCase("\v[\va\v](\vb\v\"\vt\v\"\v)\v")]
|
||||
[TestCase("\f[\fa\f](\fb\f\"\ft\f\"\f)\f")]
|
||||
[TestCase("\t[\ta\t](\tb\t\"\tt\t\"\t)\t")]
|
||||
public void Test_UncommonWhitespace(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[x]: https://example.com\r\n")]
|
||||
public void Test_LinkReferenceDefinitionWithCarriageReturnLineFeed(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Markdig.Renderers.Roundtrip;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs.Inlines
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestNullCharacterInline
|
||||
{
|
||||
[TestCase("\0", "\uFFFD")]
|
||||
[TestCase("\0p", "\uFFFDp")]
|
||||
[TestCase("p\0", "p\uFFFD")]
|
||||
[TestCase("p\0p", "p\uFFFDp")]
|
||||
[TestCase("p\0\0p", "p\uFFFD\uFFFDp")] // I promise you, this was not intentional
|
||||
public void Test(string value, string expected)
|
||||
{
|
||||
RoundTrip(value, expected);
|
||||
}
|
||||
|
||||
// this method is copied intentionally to ensure all other tests
|
||||
// do not unintentionally use the expected parameter
|
||||
private static void RoundTrip(string markdown, string expected)
|
||||
{
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
pipelineBuilder.EnableTrackTrivia();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var sw = new StringWriter();
|
||||
var rr = new RoundtripRenderer(sw);
|
||||
|
||||
rr.Write(markdownDocument);
|
||||
|
||||
Assert.AreEqual(expected, sw.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
55
src/Markdig.Tests/RoundtripSpecs/TestAtxHeading.cs
Normal file
55
src/Markdig.Tests/RoundtripSpecs/TestAtxHeading.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestAtxHeading
|
||||
{
|
||||
[TestCase("# h")]
|
||||
[TestCase("# h ")]
|
||||
[TestCase("# h\n#h")]
|
||||
[TestCase("# h\n #h")]
|
||||
[TestCase("# h\n # h")]
|
||||
[TestCase("# h\n # h ")]
|
||||
[TestCase(" # h \n # h ")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("\n# h\n\np")]
|
||||
[TestCase("\n# h\n\np\n")]
|
||||
[TestCase("\n# h\n\np\n\n")]
|
||||
[TestCase("\n\n# h\n\np\n\n")]
|
||||
[TestCase("\n\n# h\np\n\n")]
|
||||
[TestCase("\n\n# h\np\n\n")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("\n# h")]
|
||||
[TestCase("\n# h\n")]
|
||||
[TestCase("\n# h\r")]
|
||||
[TestCase("\n# h\r\n")]
|
||||
|
||||
[TestCase("\r# h")]
|
||||
[TestCase("\r# h\n")]
|
||||
[TestCase("\r# h\r")]
|
||||
[TestCase("\r# h\r\n")]
|
||||
|
||||
[TestCase("\r\n# h")]
|
||||
[TestCase("\r\n# h\n")]
|
||||
[TestCase("\r\n# h\r")]
|
||||
[TestCase("\r\n# h\r\n")]
|
||||
|
||||
[TestCase("# h\n\n ")]
|
||||
[TestCase("# h\n\n ")]
|
||||
[TestCase("# h\n\n ")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/Markdig.Tests/RoundtripSpecs/TestExample.cs
Normal file
58
src/Markdig.Tests/RoundtripSpecs/TestExample.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Renderers.Roundtrip;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestExample
|
||||
{
|
||||
[Test]
|
||||
public void Test()
|
||||
{
|
||||
var markdown = $@"
|
||||
# Test document
|
||||
This document contains an unordered list. It uses tabs to indent. This test demonstrates
|
||||
a method of making the input markdown uniform without altering any other markdown in the
|
||||
resulting output file.
|
||||
|
||||
- item1
|
||||
|
||||
>look, ma:
|
||||
> my space is not normalized!
|
||||
";
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, trackTrivia: true);
|
||||
var listBlock = markdownDocument[2] as ListBlock;
|
||||
var listItem = listBlock[0] as ListItemBlock;
|
||||
var paragraph = listItem[0] as ParagraphBlock;
|
||||
var containerInline = new ContainerInline();
|
||||
containerInline.AppendChild(new LiteralInline(" my own text!"));
|
||||
containerInline.AppendChild(new LineBreakInline { NewLine = NewLine.CarriageReturnLineFeed });
|
||||
paragraph.Inline = containerInline;
|
||||
|
||||
var sw = new StringWriter();
|
||||
var rr = new RoundtripRenderer(sw);
|
||||
rr.Write(markdownDocument);
|
||||
var outputMarkdown = sw.ToString();
|
||||
var expected = $@"
|
||||
# Test document
|
||||
This document contains an unordered list. It uses tabs to indent. This test demonstrates
|
||||
a method of making the input markdown uniform without altering any other markdown in the
|
||||
resulting output file.
|
||||
|
||||
- my own text!
|
||||
|
||||
>look, ma:
|
||||
> my space is not normalized!
|
||||
";
|
||||
|
||||
expected = expected.Replace("\r\n", "\n").Replace("\r", "\n");
|
||||
outputMarkdown = outputMarkdown.Replace("\r\n", "\n").Replace("\r", "\n");
|
||||
|
||||
Assert.AreEqual(expected, outputMarkdown);
|
||||
}
|
||||
}
|
||||
}
|
||||
107
src/Markdig.Tests/RoundtripSpecs/TestFencedCodeBlock.cs
Normal file
107
src/Markdig.Tests/RoundtripSpecs/TestFencedCodeBlock.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestFencedCodeBlock
|
||||
{
|
||||
[TestCase("```\nc\n```")]
|
||||
[TestCase("```\nc\n```\n")]
|
||||
[TestCase("\n```\nc\n```")]
|
||||
[TestCase("\n\n```\nc\n```")]
|
||||
[TestCase("```\nc\n```\n")]
|
||||
[TestCase("```\nc\n```\n\n")]
|
||||
[TestCase("\n```\nc\n```\n")]
|
||||
[TestCase("\n```\nc\n```\n\n")]
|
||||
[TestCase("\n\n```\nc\n```\n")]
|
||||
[TestCase("\n\n```\nc\n```\n\n")]
|
||||
|
||||
[TestCase(" ```\nc\n````")]
|
||||
[TestCase("```\nc\n````")]
|
||||
[TestCase("p\n\n```\nc\n```")]
|
||||
|
||||
[TestCase("```\n c\n```")]
|
||||
[TestCase("```\nc \n```")]
|
||||
[TestCase("```\n c \n```")]
|
||||
|
||||
[TestCase(" ``` \n c \n ``` ")]
|
||||
[TestCase("\t```\t\n\tc\t\n\t```\t")]
|
||||
[TestCase("\v```\v\n\vc\v\n\v```\v")]
|
||||
[TestCase("\f```\f\n\fc\f\n\f```\f")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("~~~ aa ``` ~~~\nfoo\n~~~")]
|
||||
[TestCase("~~~ aa ``` ~~~\nfoo\n~~~ ")]
|
||||
public void TestTilde(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("```\n c \n```")]
|
||||
[TestCase("```\n c \r```")]
|
||||
[TestCase("```\n c \r\n```")]
|
||||
[TestCase("```\r c \n```")]
|
||||
[TestCase("```\r c \r```")]
|
||||
[TestCase("```\r c \r\n```")]
|
||||
[TestCase("```\r\n c \n```")]
|
||||
[TestCase("```\r\n c \r```")]
|
||||
[TestCase("```\r\n c \r\n```")]
|
||||
|
||||
[TestCase("```\n c \n```\n")]
|
||||
[TestCase("```\n c \r```\n")]
|
||||
[TestCase("```\n c \r\n```\n")]
|
||||
[TestCase("```\r c \n```\n")]
|
||||
[TestCase("```\r c \r```\n")]
|
||||
[TestCase("```\r c \r\n```\n")]
|
||||
[TestCase("```\r\n c \n```\n")]
|
||||
[TestCase("```\r\n c \r```\n")]
|
||||
[TestCase("```\r\n c \r\n```\n")]
|
||||
|
||||
[TestCase("```\n c \n```\r")]
|
||||
[TestCase("```\n c \r```\r")]
|
||||
[TestCase("```\n c \r\n```\r")]
|
||||
[TestCase("```\r c \n```\r")]
|
||||
[TestCase("```\r c \r```\r")]
|
||||
[TestCase("```\r c \r\n```\r")]
|
||||
[TestCase("```\r\n c \n```\r")]
|
||||
[TestCase("```\r\n c \r```\r")]
|
||||
[TestCase("```\r\n c \r\n```\r")]
|
||||
|
||||
[TestCase("```\n c \n```\r\n")]
|
||||
[TestCase("```\n c \r```\r\n")]
|
||||
[TestCase("```\n c \r\n```\r\n")]
|
||||
[TestCase("```\r c \n```\r\n")]
|
||||
[TestCase("```\r c \r```\r\n")]
|
||||
[TestCase("```\r c \r\n```\r\n")]
|
||||
[TestCase("```\r\n c \n```\r\n")]
|
||||
[TestCase("```\r\n c \r```\r\n")]
|
||||
[TestCase("```\r\n c \r\n```\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("```i a\n```")]
|
||||
[TestCase("```i a a2\n```")]
|
||||
[TestCase("```i a a2 a3\n```")]
|
||||
[TestCase("```i a a2 a3 a4\n```")]
|
||||
|
||||
[TestCase("```i\ta\n```")]
|
||||
[TestCase("```i\ta a2\n```")]
|
||||
[TestCase("```i\ta a2 a3\n```")]
|
||||
[TestCase("```i\ta a2 a3 a4\n```")]
|
||||
|
||||
[TestCase("```i\ta \n```")]
|
||||
[TestCase("```i\ta a2 \n```")]
|
||||
[TestCase("```i\ta a2 a3 \n```")]
|
||||
[TestCase("```i\ta a2 a3 a4 \n```")]
|
||||
public void TestInfoArguments(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/Markdig.Tests/RoundtripSpecs/TestHtmlBlock.cs
Normal file
20
src/Markdig.Tests/RoundtripSpecs/TestHtmlBlock.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestHtmlBlock
|
||||
{
|
||||
[TestCase("<br>")]
|
||||
[TestCase("<br>\n")]
|
||||
[TestCase("<br>\n\n")]
|
||||
[TestCase("<div></div>\n\n# h")]
|
||||
[TestCase("p\n\n<div></div>\n")]
|
||||
[TestCase("<div></div>\n\n# h")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
86
src/Markdig.Tests/RoundtripSpecs/TestIndentedCodeBlock.cs
Normal file
86
src/Markdig.Tests/RoundtripSpecs/TestIndentedCodeBlock.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestIndentedCodeBlock
|
||||
{
|
||||
// A codeblock is indented with 4 spaces. After the 4th space, whitespace is interpreted as content.
|
||||
// l = line
|
||||
[TestCase(" l")]
|
||||
[TestCase(" l")]
|
||||
[TestCase("\tl")]
|
||||
[TestCase("\t\tl")]
|
||||
[TestCase("\tl1\n l1")]
|
||||
|
||||
[TestCase("\n l")]
|
||||
[TestCase("\n\n l")]
|
||||
[TestCase("\n l\n")]
|
||||
[TestCase("\n l\n\n")]
|
||||
[TestCase("\n\n l\n")]
|
||||
[TestCase("\n\n l\n\n")]
|
||||
|
||||
[TestCase(" l\n l")]
|
||||
[TestCase(" l\n l\n l")]
|
||||
|
||||
|
||||
// two newlines are needed for indented codeblock start after paragraph
|
||||
[TestCase("p\n\n l")]
|
||||
[TestCase("p\n\n l\n")]
|
||||
[TestCase("p\n\n l\n\n")]
|
||||
|
||||
[TestCase("p\n\n l\n l")]
|
||||
[TestCase("p\n\n l\n l")]
|
||||
|
||||
[TestCase(" l\n\np\n\n l")]
|
||||
[TestCase(" l l\n\np\n\n l l")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(" l\n")]
|
||||
[TestCase(" l\r")]
|
||||
[TestCase(" l\r\n")]
|
||||
|
||||
[TestCase(" l\n l")]
|
||||
[TestCase(" l\n l\n")]
|
||||
[TestCase(" l\n l\r")]
|
||||
[TestCase(" l\n l\r\n")]
|
||||
|
||||
[TestCase(" l\r l")]
|
||||
[TestCase(" l\r l\n")]
|
||||
[TestCase(" l\r l\r")]
|
||||
[TestCase(" l\r l\r\n")]
|
||||
|
||||
[TestCase(" l\r\n l")]
|
||||
[TestCase(" l\r\n l\n")]
|
||||
[TestCase(" l\r\n l\r")]
|
||||
[TestCase(" l\r\n l\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(" l\n\n l\n")]
|
||||
[TestCase(" l\n\n\n l\n")]
|
||||
public void TestNewlinesInBetweenResultInOneCodeBlock(string value)
|
||||
{
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
pipelineBuilder.EnableTrackTrivia();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
var markdownDocument = Markdown.Parse(value, pipeline);
|
||||
|
||||
Assert.AreEqual(1, markdownDocument.Count);
|
||||
}
|
||||
|
||||
[TestCase(" l\n\np")]
|
||||
[TestCase(" l\n\n\np")]
|
||||
[TestCase(" l\n\n\n\np")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
214
src/Markdig.Tests/RoundtripSpecs/TestLinkReferenceDefinition.cs
Normal file
214
src/Markdig.Tests/RoundtripSpecs/TestLinkReferenceDefinition.cs
Normal file
@@ -0,0 +1,214 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestLinkReferenceDefinition
|
||||
{
|
||||
[TestCase(@"[a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
|
||||
[TestCase(@"[a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
|
||||
[TestCase(@"[a]: /r ")]
|
||||
[TestCase(@" [a]: /r ")]
|
||||
[TestCase(@" [a]: /r ")]
|
||||
[TestCase(@" [a]: /r ")]
|
||||
|
||||
[TestCase(@"[a]: /r ""l""")]
|
||||
[TestCase(@"[a]: /r ""l""")]
|
||||
[TestCase(@"[a]: /r ""l""")]
|
||||
[TestCase(@"[a]: /r ""l"" ")]
|
||||
[TestCase(@"[a]: /r ""l""")]
|
||||
[TestCase(@"[a]: /r ""l"" ")]
|
||||
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
|
||||
[TestCase("[a]:\t/r")]
|
||||
[TestCase("[a]:\t/r\t")]
|
||||
[TestCase("[a]:\t/r\t\"l\"")]
|
||||
[TestCase("[a]:\t/r\t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t/r")]
|
||||
[TestCase("[a]: \t/r\t")]
|
||||
[TestCase("[a]: \t/r\t\"l\"")]
|
||||
[TestCase("[a]: \t/r\t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t /r")]
|
||||
[TestCase("[a]:\t /r\t")]
|
||||
[TestCase("[a]:\t /r\t\"l\"")]
|
||||
[TestCase("[a]:\t /r\t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t /r")]
|
||||
[TestCase("[a]: \t /r\t")]
|
||||
[TestCase("[a]: \t /r\t\"l\"")]
|
||||
[TestCase("[a]: \t /r\t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t/r \t")]
|
||||
[TestCase("[a]:\t/r \t\"l\"")]
|
||||
[TestCase("[a]:\t/r \t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t/r")]
|
||||
[TestCase("[a]: \t/r \t")]
|
||||
[TestCase("[a]: \t/r \t\"l\"")]
|
||||
[TestCase("[a]: \t/r \t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t /r")]
|
||||
[TestCase("[a]:\t /r \t")]
|
||||
[TestCase("[a]:\t /r \t\"l\"")]
|
||||
[TestCase("[a]:\t /r \t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t /r")]
|
||||
[TestCase("[a]: \t /r \t")]
|
||||
[TestCase("[a]: \t /r \t\"l\"")]
|
||||
[TestCase("[a]: \t /r \t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t/r\t ")]
|
||||
[TestCase("[a]:\t/r\t \"l\"")]
|
||||
[TestCase("[a]:\t/r\t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t/r")]
|
||||
[TestCase("[a]: \t/r\t ")]
|
||||
[TestCase("[a]: \t/r\t \"l\"")]
|
||||
[TestCase("[a]: \t/r\t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t /r")]
|
||||
[TestCase("[a]:\t /r\t ")]
|
||||
[TestCase("[a]:\t /r\t \"l\"")]
|
||||
[TestCase("[a]:\t /r\t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t /r")]
|
||||
[TestCase("[a]: \t /r\t ")]
|
||||
[TestCase("[a]: \t /r\t \"l\"")]
|
||||
[TestCase("[a]: \t /r\t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t/r \t ")]
|
||||
[TestCase("[a]:\t/r \t \"l\"")]
|
||||
[TestCase("[a]:\t/r \t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t/r")]
|
||||
[TestCase("[a]: \t/r \t ")]
|
||||
[TestCase("[a]: \t/r \t \"l\"")]
|
||||
[TestCase("[a]: \t/r \t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t /r")]
|
||||
[TestCase("[a]:\t /r \t ")]
|
||||
[TestCase("[a]:\t /r \t \"l\"")]
|
||||
[TestCase("[a]:\t /r \t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t /r")]
|
||||
[TestCase("[a]: \t /r \t ")]
|
||||
[TestCase("[a]: \t /r \t \"l\"")]
|
||||
[TestCase("[a]: \t /r \t \"l\"\t")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]: /r\n[b]: /r\n")]
|
||||
[TestCase("[a]: /r\n[b]: /r\n[c] /r\n")]
|
||||
public void TestMultiple(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]:\f/r\f\"l\"")]
|
||||
[TestCase("[a]:\v/r\v\"l\"")]
|
||||
public void TestUncommonWhitespace(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]:\n/r\n\"t\"")]
|
||||
[TestCase("[a]:\n/r\r\"t\"")]
|
||||
[TestCase("[a]:\n/r\r\n\"t\"")]
|
||||
|
||||
[TestCase("[a]:\r/r\n\"t\"")]
|
||||
[TestCase("[a]:\r/r\r\"t\"")]
|
||||
[TestCase("[a]:\r/r\r\n\"t\"")]
|
||||
|
||||
[TestCase("[a]:\r\n/r\n\"t\"")]
|
||||
[TestCase("[a]:\r\n/r\r\"t\"")]
|
||||
[TestCase("[a]:\r\n/r\r\n\"t\"")]
|
||||
|
||||
[TestCase("[a]:\n/r\n\"t\nt\"")]
|
||||
[TestCase("[a]:\n/r\n\"t\rt\"")]
|
||||
[TestCase("[a]:\n/r\n\"t\r\nt\"")]
|
||||
|
||||
[TestCase("[a]:\r\n /r\t \n \t \"t\r\nt\" ")]
|
||||
[TestCase("[a]:\n/r\n\n[a],")]
|
||||
[TestCase("[a]: /r\n[b]: /r\n\n[a],")]
|
||||
public void TestNewlines(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[ a]: /r")]
|
||||
[TestCase("[a ]: /r")]
|
||||
[TestCase("[ a ]: /r")]
|
||||
[TestCase("[ a]: /r")]
|
||||
[TestCase("[ a ]: /r")]
|
||||
[TestCase("[a ]: /r")]
|
||||
[TestCase("[ a ]: /r")]
|
||||
[TestCase("[ a ]: /r")]
|
||||
[TestCase("[a a]: /r")]
|
||||
[TestCase("[a\va]: /r")]
|
||||
[TestCase("[a\fa]: /r")]
|
||||
[TestCase("[a\ta]: /r")]
|
||||
[TestCase("[\va]: /r")]
|
||||
[TestCase("[\fa]: /r")]
|
||||
[TestCase("[\ta]: /r")]
|
||||
[TestCase(@"[\]]: /r")]
|
||||
public void TestLabel(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]: /r ()")]
|
||||
[TestCase("[a]: /r (t)")]
|
||||
[TestCase("[a]: /r ( t)")]
|
||||
[TestCase("[a]: /r (t )")]
|
||||
[TestCase("[a]: /r ( t )")]
|
||||
|
||||
[TestCase("[a]: /r ''")]
|
||||
[TestCase("[a]: /r 't'")]
|
||||
[TestCase("[a]: /r ' t'")]
|
||||
[TestCase("[a]: /r 't '")]
|
||||
[TestCase("[a]: /r ' t '")]
|
||||
public void Test_Title(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]: /r\n===\n[a]")]
|
||||
public void TestSetextHeader(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/Markdig.Tests/RoundtripSpecs/TestNoBlocksFoundBlock.cs
Normal file
23
src/Markdig.Tests/RoundtripSpecs/TestNoBlocksFoundBlock.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestNoBlocksFoundBlock
|
||||
{
|
||||
[TestCase("\r")]
|
||||
[TestCase("\n")]
|
||||
[TestCase("\r\n")]
|
||||
[TestCase("\t")]
|
||||
[TestCase("\v")]
|
||||
[TestCase("\f")]
|
||||
[TestCase(" ")]
|
||||
[TestCase(" ")]
|
||||
[TestCase(" ")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
191
src/Markdig.Tests/RoundtripSpecs/TestOrderedList.cs
Normal file
191
src/Markdig.Tests/RoundtripSpecs/TestOrderedList.cs
Normal file
@@ -0,0 +1,191 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestOrderedList
|
||||
{
|
||||
[TestCase("1. i")]
|
||||
[TestCase("1. i")]
|
||||
[TestCase("1. i ")]
|
||||
[TestCase("1. i ")]
|
||||
[TestCase("1. i ")]
|
||||
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
|
||||
[TestCase("1. i\n")]
|
||||
[TestCase("1. i\n")]
|
||||
[TestCase("1. i \n")]
|
||||
[TestCase("1. i \n")]
|
||||
[TestCase("1. i \n")]
|
||||
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
|
||||
[TestCase("1. i\n2. j")]
|
||||
[TestCase("1. i\n2. j")]
|
||||
[TestCase("1. i \n2. j")]
|
||||
[TestCase("1. i \n2. j")]
|
||||
[TestCase("1. i \n2. j")]
|
||||
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
|
||||
[TestCase("1. i\n2. j\n")]
|
||||
[TestCase("1. i\n2. j\n")]
|
||||
[TestCase("1. i \n2. j\n")]
|
||||
[TestCase("1. i \n2. j\n")]
|
||||
[TestCase("1. i \n2. j\n")]
|
||||
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
|
||||
[TestCase("1. i\n2. j\n3. k")]
|
||||
[TestCase("1. i\n2. j\n3. k\n")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("10. i")]
|
||||
[TestCase("11. i")]
|
||||
[TestCase("10. i\n12. i")]
|
||||
[TestCase("2. i\n3. i")]
|
||||
public void Test_MoreThenOneStart(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
|
||||
[TestCase("\n1. i")]
|
||||
[TestCase("\r1. i")]
|
||||
[TestCase("\r\n1. i")]
|
||||
|
||||
[TestCase("\n1. i\n")]
|
||||
[TestCase("\r1. i\n")]
|
||||
[TestCase("\r\n1. i\n")]
|
||||
|
||||
[TestCase("\n1. i\r")]
|
||||
[TestCase("\r1. i\r")]
|
||||
[TestCase("\r\n1. i\r")]
|
||||
|
||||
[TestCase("\n1. i\r\n")]
|
||||
[TestCase("\r1. i\r\n")]
|
||||
[TestCase("\r\n1. i\r\n")]
|
||||
|
||||
[TestCase("1. i\n2. i")]
|
||||
[TestCase("\n1. i\n2. i")]
|
||||
[TestCase("\r1. i\n2. i")]
|
||||
[TestCase("\r\n1. i\n2. i")]
|
||||
|
||||
[TestCase("1. i\r2. i")]
|
||||
[TestCase("\n1. i\r2. i")]
|
||||
[TestCase("\r1. i\r2. i")]
|
||||
[TestCase("\r\n1. i\r2. i")]
|
||||
|
||||
[TestCase("1. i\r\n2. i")]
|
||||
[TestCase("\n1. i\r\n2. i")]
|
||||
[TestCase("\r1. i\r\n2. i")]
|
||||
[TestCase("\r\n1. i\r\n2. i")]
|
||||
|
||||
[TestCase("1. i\n2. i\n")]
|
||||
[TestCase("\n1. i\n2. i\n")]
|
||||
[TestCase("\r1. i\n2. i\n")]
|
||||
[TestCase("\r\n1. i\n2. i\n")]
|
||||
|
||||
[TestCase("1. i\r2. i\r")]
|
||||
[TestCase("\n1. i\r2. i\r")]
|
||||
[TestCase("\r1. i\r2. i\r")]
|
||||
[TestCase("\r\n1. i\r2. i\r")]
|
||||
|
||||
[TestCase("1. i\r\n2. i\r\n")]
|
||||
[TestCase("\n1. i\r\n2. i\r\n")]
|
||||
[TestCase("\r1. i\r\n2. i\r\n")]
|
||||
[TestCase("\r\n1. i\r\n2. i\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("1. i\n 1. i")]
|
||||
[TestCase("1. i\n 1. i\n")]
|
||||
[TestCase("1. i\n 1. i\n 2. i")]
|
||||
[TestCase("1. i\n 2. i\n 3. i")]
|
||||
|
||||
[TestCase("1. i\n\t1. i")]
|
||||
[TestCase("1. i\n\t1. i\n2. i")]
|
||||
public void TestMultipleLevels(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("1. c")]
|
||||
[TestCase("1. c")]
|
||||
public void Test_IndentedCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
247
src/Markdig.Tests/RoundtripSpecs/TestParagraph.cs
Normal file
247
src/Markdig.Tests/RoundtripSpecs/TestParagraph.cs
Normal file
@@ -0,0 +1,247 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestParagraph
|
||||
{
|
||||
[TestCase("p")]
|
||||
[TestCase(" p")]
|
||||
[TestCase("p ")]
|
||||
[TestCase(" p ")]
|
||||
|
||||
[TestCase("p\np")]
|
||||
[TestCase(" p\np")]
|
||||
[TestCase("p \np")]
|
||||
[TestCase(" p \np")]
|
||||
|
||||
[TestCase("p\n p")]
|
||||
[TestCase(" p\n p")]
|
||||
[TestCase("p \n p")]
|
||||
[TestCase(" p \n p")]
|
||||
|
||||
[TestCase("p\np ")]
|
||||
[TestCase(" p\np ")]
|
||||
[TestCase("p \np ")]
|
||||
[TestCase(" p \np ")]
|
||||
|
||||
[TestCase("p\n\n p ")]
|
||||
[TestCase(" p\n\n p ")]
|
||||
[TestCase("p \n\n p ")]
|
||||
[TestCase(" p \n\n p ")]
|
||||
|
||||
[TestCase("p\n\np")]
|
||||
[TestCase(" p\n\np")]
|
||||
[TestCase("p \n\np")]
|
||||
[TestCase(" p \n\np")]
|
||||
|
||||
[TestCase("p\n\n p")]
|
||||
[TestCase(" p\n\n p")]
|
||||
[TestCase("p \n\n p")]
|
||||
[TestCase(" p \n\n p")]
|
||||
|
||||
[TestCase("p\n\np ")]
|
||||
[TestCase(" p\n\np ")]
|
||||
[TestCase("p \n\np ")]
|
||||
[TestCase(" p \n\np ")]
|
||||
|
||||
[TestCase("p\n\n p ")]
|
||||
[TestCase(" p\n\n p ")]
|
||||
[TestCase("p \n\n p ")]
|
||||
[TestCase(" p \n\n p ")]
|
||||
|
||||
[TestCase("\np")]
|
||||
[TestCase("\n p")]
|
||||
[TestCase("\np ")]
|
||||
[TestCase("\n p ")]
|
||||
|
||||
[TestCase("\np\np")]
|
||||
[TestCase("\n p\np")]
|
||||
[TestCase("\np \np")]
|
||||
[TestCase("\n p \np")]
|
||||
|
||||
[TestCase("\np\n p")]
|
||||
[TestCase("\n p\n p")]
|
||||
[TestCase("\np \n p")]
|
||||
[TestCase("\n p \n p")]
|
||||
|
||||
[TestCase("\np\np ")]
|
||||
[TestCase("\n p\np ")]
|
||||
[TestCase("\np \np ")]
|
||||
[TestCase("\n p \np ")]
|
||||
|
||||
[TestCase("\np\n\n p ")]
|
||||
[TestCase("\n p\n\n p ")]
|
||||
[TestCase("\np \n\n p ")]
|
||||
[TestCase("\n p \n\n p ")]
|
||||
|
||||
[TestCase("\np\n\np")]
|
||||
[TestCase("\n p\n\np")]
|
||||
[TestCase("\np \n\np")]
|
||||
[TestCase("\n p \n\np")]
|
||||
|
||||
[TestCase("\np\n\n p")]
|
||||
[TestCase("\n p\n\n p")]
|
||||
[TestCase("\np \n\n p")]
|
||||
[TestCase("\n p \n\n p")]
|
||||
|
||||
[TestCase("\np\n\np ")]
|
||||
[TestCase("\n p\n\np ")]
|
||||
[TestCase("\np \n\np ")]
|
||||
[TestCase("\n p \n\np ")]
|
||||
|
||||
[TestCase("\np\n\n p ")]
|
||||
[TestCase("\n p\n\n p ")]
|
||||
[TestCase("\np \n\n p ")]
|
||||
[TestCase("\n p \n\n p ")]
|
||||
|
||||
[TestCase("p p")]
|
||||
[TestCase("p\tp")]
|
||||
[TestCase("p \tp")]
|
||||
[TestCase("p \t p")]
|
||||
[TestCase("p \tp")]
|
||||
|
||||
// special cases
|
||||
[TestCase(" p \n\n\n\n p \n\n")]
|
||||
[TestCase("\n\np")]
|
||||
[TestCase("p\n")]
|
||||
[TestCase("p\n\n")]
|
||||
[TestCase("p\np\n p")]
|
||||
[TestCase("p\np\n p\n p")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
|
||||
[TestCase("\n")]
|
||||
[TestCase("\r\n")]
|
||||
[TestCase("\r")]
|
||||
|
||||
[TestCase("p\n")]
|
||||
[TestCase("p\r")]
|
||||
[TestCase("p\r\n")]
|
||||
|
||||
[TestCase("p\np")]
|
||||
[TestCase("p\rp")]
|
||||
[TestCase("p\r\np")]
|
||||
|
||||
[TestCase("p\np\n")]
|
||||
[TestCase("p\rp\n")]
|
||||
[TestCase("p\r\np\n")]
|
||||
|
||||
[TestCase("p\np\r")]
|
||||
[TestCase("p\rp\r")]
|
||||
[TestCase("p\r\np\r")]
|
||||
|
||||
[TestCase("p\np\r\n")]
|
||||
[TestCase("p\rp\r\n")]
|
||||
[TestCase("p\r\np\r\n")]
|
||||
|
||||
[TestCase("\np\n")]
|
||||
[TestCase("\np\r")]
|
||||
[TestCase("\np\r\n")]
|
||||
|
||||
[TestCase("\np\np")]
|
||||
[TestCase("\np\rp")]
|
||||
[TestCase("\np\r\np")]
|
||||
|
||||
[TestCase("\np\np\n")]
|
||||
[TestCase("\np\rp\n")]
|
||||
[TestCase("\np\r\np\n")]
|
||||
|
||||
[TestCase("\np\np\r")]
|
||||
[TestCase("\np\rp\r")]
|
||||
[TestCase("\np\r\np\r")]
|
||||
|
||||
[TestCase("\np\np\r\n")]
|
||||
[TestCase("\np\rp\r\n")]
|
||||
[TestCase("\np\r\np\r\n")]
|
||||
|
||||
[TestCase("\rp\n")]
|
||||
[TestCase("\rp\r")]
|
||||
[TestCase("\rp\r\n")]
|
||||
|
||||
[TestCase("\rp\np")]
|
||||
[TestCase("\rp\rp")]
|
||||
[TestCase("\rp\r\np")]
|
||||
|
||||
[TestCase("\rp\np\n")]
|
||||
[TestCase("\rp\rp\n")]
|
||||
[TestCase("\rp\r\np\n")]
|
||||
|
||||
[TestCase("\rp\np\r")]
|
||||
[TestCase("\rp\rp\r")]
|
||||
[TestCase("\rp\r\np\r")]
|
||||
|
||||
[TestCase("\rp\np\r\n")]
|
||||
[TestCase("\rp\rp\r\n")]
|
||||
[TestCase("\rp\r\np\r\n")]
|
||||
|
||||
[TestCase("\r\np\n")]
|
||||
[TestCase("\r\np\r")]
|
||||
[TestCase("\r\np\r\n")]
|
||||
|
||||
[TestCase("\r\np\np")]
|
||||
[TestCase("\r\np\rp")]
|
||||
[TestCase("\r\np\r\np")]
|
||||
|
||||
[TestCase("\r\np\np\n")]
|
||||
[TestCase("\r\np\rp\n")]
|
||||
[TestCase("\r\np\r\np\n")]
|
||||
|
||||
[TestCase("\r\np\np\r")]
|
||||
[TestCase("\r\np\rp\r")]
|
||||
[TestCase("\r\np\r\np\r")]
|
||||
|
||||
[TestCase("\r\np\np\r\n")]
|
||||
[TestCase("\r\np\rp\r\n")]
|
||||
[TestCase("\r\np\r\np\r\n")]
|
||||
|
||||
[TestCase("p\n")]
|
||||
[TestCase("p\n\n")]
|
||||
[TestCase("p\n\n\n")]
|
||||
[TestCase("p\n\n\n\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(" \n")]
|
||||
[TestCase(" \r")]
|
||||
[TestCase(" \r\n")]
|
||||
|
||||
[TestCase(" \np")]
|
||||
[TestCase(" \rp")]
|
||||
[TestCase(" \r\np")]
|
||||
|
||||
[TestCase(" \np")]
|
||||
[TestCase(" \rp")]
|
||||
[TestCase(" \r\np")]
|
||||
|
||||
[TestCase(" \np")]
|
||||
[TestCase(" \rp")]
|
||||
[TestCase(" \r\np")]
|
||||
|
||||
[TestCase(" \n ")]
|
||||
[TestCase(" \r ")]
|
||||
[TestCase(" \r\n ")]
|
||||
|
||||
[TestCase(" \np ")]
|
||||
[TestCase(" \rp ")]
|
||||
[TestCase(" \r\np ")]
|
||||
|
||||
[TestCase(" \np ")]
|
||||
[TestCase(" \rp ")]
|
||||
[TestCase(" \r\np ")]
|
||||
|
||||
[TestCase(" \np ")]
|
||||
[TestCase(" \rp ")]
|
||||
[TestCase(" \r\np ")]
|
||||
public void Test_WhitespaceWithNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
286
src/Markdig.Tests/RoundtripSpecs/TestQuoteBlock.cs
Normal file
286
src/Markdig.Tests/RoundtripSpecs/TestQuoteBlock.cs
Normal file
@@ -0,0 +1,286 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestQuoteBlock
|
||||
{
|
||||
[TestCase(">q")]
|
||||
[TestCase(" >q")]
|
||||
[TestCase(" >q")]
|
||||
[TestCase(" >q")]
|
||||
[TestCase("> q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase("> q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase(" > q")]
|
||||
|
||||
[TestCase(">q\n>q")]
|
||||
[TestCase(">q\n >q")]
|
||||
[TestCase(">q\n >q")]
|
||||
[TestCase(">q\n >q")]
|
||||
[TestCase(">q\n> q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n> q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n > q")]
|
||||
|
||||
[TestCase(" >q\n>q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n> q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n> q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
|
||||
[TestCase(" >q\n>q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n> q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n> q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
|
||||
[TestCase("> q\n>q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n> q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n> q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
|
||||
[TestCase("> q\n>q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n> q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n> q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
|
||||
[TestCase(">q\n>q\n>q")]
|
||||
[TestCase(">q\n>\n>q")]
|
||||
[TestCase(">q\np\n>q")]
|
||||
[TestCase(">q\n>\n>\n>q")]
|
||||
[TestCase(">q\n>\n>\n>\n>q")]
|
||||
[TestCase(">q\n>\n>q\n>\n>q")]
|
||||
[TestCase("p\n\n> **q**\n>p\n")]
|
||||
|
||||
[TestCase("> q\np\n> q")] // lazy
|
||||
[TestCase("> q\n> q\np")] // lazy
|
||||
|
||||
[TestCase(">>q")]
|
||||
[TestCase(" > > q")]
|
||||
|
||||
[TestCase("> **q**\n>p\n")]
|
||||
[TestCase("> **q**")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("> q")] // 5
|
||||
[TestCase("> q")] // 6
|
||||
[TestCase(" > q")] //5
|
||||
[TestCase(" > q")] //6
|
||||
[TestCase(" > \tq")]
|
||||
[TestCase("> q\n> q")] // 5, 5
|
||||
[TestCase("> q\n> q")] // 5, 6
|
||||
[TestCase("> q\n> q")] // 6, 5
|
||||
[TestCase("> q\n> q")] // 6, 6
|
||||
[TestCase("> q\n\n> 5")] // 5, 5
|
||||
public void TestIndentedCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("\n> q")]
|
||||
[TestCase("\n> q\n")]
|
||||
[TestCase("\n> q\n\n")]
|
||||
[TestCase("> q\n\np")]
|
||||
[TestCase("p\n\n> q\n\n# h")]
|
||||
|
||||
//https://github.com/lunet-io/markdig/issues/480
|
||||
//[TestCase(">\np")]
|
||||
//[TestCase(">**b**\n>\n>p\n>\np\n")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("> q\n\n# h\n")]
|
||||
public void TestAtxHeader(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(">- i")]
|
||||
[TestCase("> - i")]
|
||||
[TestCase(">- i\n>- i")]
|
||||
[TestCase(">- >p")]
|
||||
[TestCase("> - >p")]
|
||||
[TestCase(">- i1\n>- i2\n")]
|
||||
[TestCase("> **p** p\n>- i1\n>- i2\n")]
|
||||
public void TestUnorderedList(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("> *q*\n>p\n")]
|
||||
[TestCase("> *q*")]
|
||||
public void TestEmphasis(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("> **q**\n>p\n")]
|
||||
[TestCase("> **q**")]
|
||||
public void TestStrongEmphasis(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(">p\n")]
|
||||
[TestCase(">p\r")]
|
||||
[TestCase(">p\r\n")]
|
||||
|
||||
[TestCase(">p\n>p")]
|
||||
[TestCase(">p\r>p")]
|
||||
[TestCase(">p\r\n>p")]
|
||||
|
||||
[TestCase(">p\n>p\n")]
|
||||
[TestCase(">p\r>p\n")]
|
||||
[TestCase(">p\r\n>p\n")]
|
||||
|
||||
[TestCase(">p\n>p\r")]
|
||||
[TestCase(">p\r>p\r")]
|
||||
[TestCase(">p\r\n>p\r")]
|
||||
|
||||
[TestCase(">p\n>p\r\n")]
|
||||
[TestCase(">p\r>p\r\n")]
|
||||
[TestCase(">p\r\n>p\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(">\n>q")]
|
||||
[TestCase(">\n>\n>q")]
|
||||
[TestCase(">q\n>\n>q")]
|
||||
[TestCase(">q\n>\n>\n>q")]
|
||||
[TestCase(">q\n> \n>q")]
|
||||
[TestCase(">q\n> \n>q")]
|
||||
[TestCase(">q\n> \n>q")]
|
||||
[TestCase(">q\n>\t\n>q")]
|
||||
[TestCase(">q\n>\v\n>q")]
|
||||
[TestCase(">q\n>\f\n>q")]
|
||||
public void TestEmptyLines(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/Markdig.Tests/RoundtripSpecs/TestSetextHeading.cs
Normal file
53
src/Markdig.Tests/RoundtripSpecs/TestSetextHeading.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSetextHeading
|
||||
{
|
||||
[TestCase("h1\n===")] //3
|
||||
[TestCase("h1\n ===")] //3
|
||||
[TestCase("h1\n ===")] //3
|
||||
[TestCase("h1\n ===")] //3
|
||||
[TestCase("h1\n=== ")] //3
|
||||
[TestCase("h1 \n===")] //3
|
||||
[TestCase("h1\\\n===")] //3
|
||||
[TestCase("h1\n === ")] //3
|
||||
[TestCase("h1\nh1 l2\n===")] //3
|
||||
[TestCase("h1\n====")] // 4
|
||||
[TestCase("h1\n ====")] // 4
|
||||
[TestCase("h1\n==== ")] // 4
|
||||
[TestCase("h1\n ==== ")] // 4
|
||||
[TestCase("h1\n===\nh1\n===")] //3
|
||||
[TestCase("\\>h1\n===")] //3
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("h1\r===")]
|
||||
[TestCase("h1\n===")]
|
||||
[TestCase("h1\r\n===")]
|
||||
|
||||
[TestCase("h1\r===\r")]
|
||||
[TestCase("h1\n===\r")]
|
||||
[TestCase("h1\r\n===\r")]
|
||||
|
||||
[TestCase("h1\r===\n")]
|
||||
[TestCase("h1\n===\n")]
|
||||
[TestCase("h1\r\n===\n")]
|
||||
|
||||
[TestCase("h1\r===\r\n")]
|
||||
[TestCase("h1\n===\r\n")]
|
||||
[TestCase("h1\r\n===\r\n")]
|
||||
|
||||
[TestCase("h1\n===\n\n\nh2---\n\n")]
|
||||
[TestCase("h1\r===\r\r\rh2---\r\r")]
|
||||
[TestCase("h1\r\n===\r\n\r\n\r\nh2---\r\n\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
src/Markdig.Tests/RoundtripSpecs/TestThematicBreak.cs
Normal file
53
src/Markdig.Tests/RoundtripSpecs/TestThematicBreak.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestThematicBreak
|
||||
{
|
||||
[TestCase("---")]
|
||||
[TestCase(" ---")]
|
||||
[TestCase(" ---")]
|
||||
[TestCase(" ---")]
|
||||
[TestCase("--- ")]
|
||||
[TestCase(" --- ")]
|
||||
[TestCase(" --- ")]
|
||||
[TestCase(" --- ")]
|
||||
[TestCase("- - -")]
|
||||
[TestCase(" - - -")]
|
||||
[TestCase(" - - - ")]
|
||||
[TestCase("-- -")]
|
||||
[TestCase("---\n")]
|
||||
[TestCase("---\n\n")]
|
||||
[TestCase("---\np")]
|
||||
[TestCase("---\n\np")]
|
||||
[TestCase("---\n# h")]
|
||||
[TestCase("p\n\n---")]
|
||||
// Note: "p\n---" is parsed as setext heading
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("\n---")]
|
||||
[TestCase("\r---")]
|
||||
[TestCase("\r\n---")]
|
||||
|
||||
[TestCase("\n---\n")]
|
||||
[TestCase("\r---\n")]
|
||||
[TestCase("\r\n---\n")]
|
||||
|
||||
[TestCase("\n---\r")]
|
||||
[TestCase("\r---\r")]
|
||||
[TestCase("\r\n---\r")]
|
||||
|
||||
[TestCase("\n---\r\n")]
|
||||
[TestCase("\r---\r\n")]
|
||||
[TestCase("\r\n---\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
183
src/Markdig.Tests/RoundtripSpecs/TestUnorderedList.cs
Normal file
183
src/Markdig.Tests/RoundtripSpecs/TestUnorderedList.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestUnorderedList
|
||||
{
|
||||
// i = item
|
||||
[TestCase("- i1")]
|
||||
[TestCase("- i1 ")]
|
||||
[TestCase("- i1\n")]
|
||||
[TestCase("- i1\n\n")]
|
||||
[TestCase("- i1\n- i2")]
|
||||
[TestCase("- i1\n - i2")]
|
||||
[TestCase("- i1\n - i1.1\n - i1.2")]
|
||||
[TestCase("- i1 \n- i2 \n")]
|
||||
[TestCase("- i1 \n- i2 \n")]
|
||||
[TestCase(" - i1")]
|
||||
[TestCase(" - i1")]
|
||||
[TestCase(" - i1")]
|
||||
[TestCase("- i1\n\n- i1")]
|
||||
[TestCase("- i1\n\n\n- i1")]
|
||||
[TestCase("- i1\n - i1.1\n - i1.1.1\n")]
|
||||
|
||||
[TestCase("-\ti1")]
|
||||
[TestCase("-\ti1\n-\ti2")]
|
||||
[TestCase("-\ti1\n- i2\n-\ti3")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q1\n - > q2")]
|
||||
public void TestBlockQuote(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("- i1\n\np\n")] // TODO: listblock should render newline, apparently last paragraph of last listitem dont have newline
|
||||
[TestCase("- i1\n\n\np\n")]
|
||||
[TestCase("- i1\n\np")]
|
||||
[TestCase("- i1\n\np\n")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("- i1\n\n---\n")]
|
||||
[TestCase("- i1\n\n\n---\n")]
|
||||
public void TestThematicBreak(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("- c")] // 5
|
||||
[TestCase("- c\n c")] // 5, 6
|
||||
[TestCase(" - c\n c")] // 5, 6
|
||||
[TestCase(" - c\n c")] // 5, 7
|
||||
[TestCase("- c\n c")] // 6, 6
|
||||
[TestCase(" - c\n c")] // 6, 6
|
||||
[TestCase(" - c\n c")] // 6, 7
|
||||
public void TestIndentedCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("- ```a```")]
|
||||
[TestCase("- ```\n a\n```")]
|
||||
[TestCase("- i1\n - i1.1\n ```\n c\n ```")]
|
||||
[TestCase("- i1\n - i1.1\n ```\nc\n```")]
|
||||
[TestCase("- i1\n - i1.1\n ```\nc\n```\n")]
|
||||
public void TestFencedCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("\n- i")]
|
||||
[TestCase("\r- i")]
|
||||
[TestCase("\r\n- i")]
|
||||
|
||||
[TestCase("\n- i\n")]
|
||||
[TestCase("\r- i\n")]
|
||||
[TestCase("\r\n- i\n")]
|
||||
|
||||
[TestCase("\n- i\r")]
|
||||
[TestCase("\r- i\r")]
|
||||
[TestCase("\r\n- i\r")]
|
||||
|
||||
[TestCase("\n- i\r\n")]
|
||||
[TestCase("\r- i\r\n")]
|
||||
[TestCase("\r\n- i\r\n")]
|
||||
|
||||
[TestCase("- i\n- j")]
|
||||
[TestCase("- i\r- j")]
|
||||
[TestCase("- i\r\n- j")]
|
||||
|
||||
[TestCase("\n- i\n- j")]
|
||||
[TestCase("\n- i\r- j")]
|
||||
[TestCase("\n- i\r\n- j")]
|
||||
|
||||
[TestCase("\r- i\n- j")]
|
||||
[TestCase("\r- i\r- j")]
|
||||
[TestCase("\r- i\r\n- j")]
|
||||
|
||||
[TestCase("\r\n- i\n- j")]
|
||||
[TestCase("\r\n- i\r- j")]
|
||||
[TestCase("\r\n- i\r\n- j")]
|
||||
|
||||
[TestCase("- i\n- j\n")]
|
||||
[TestCase("- i\r- j\n")]
|
||||
[TestCase("- i\r\n- j\n")]
|
||||
|
||||
[TestCase("\n- i\n- j\n")]
|
||||
[TestCase("\n- i\r- j\n")]
|
||||
[TestCase("\n- i\r\n- j\n")]
|
||||
|
||||
[TestCase("\r- i\n- j\n")]
|
||||
[TestCase("\r- i\r- j\n")]
|
||||
[TestCase("\r- i\r\n- j\n")]
|
||||
|
||||
[TestCase("\r\n- i\n- j\n")]
|
||||
[TestCase("\r\n- i\r- j\n")]
|
||||
[TestCase("\r\n- i\r\n- j\n")]
|
||||
|
||||
[TestCase("- i\n- j\r")]
|
||||
[TestCase("- i\r- j\r")]
|
||||
[TestCase("- i\r\n- j\r")]
|
||||
|
||||
[TestCase("\n- i\n- j\r")]
|
||||
[TestCase("\n- i\r- j\r")]
|
||||
[TestCase("\n- i\r\n- j\r")]
|
||||
|
||||
[TestCase("\r- i\n- j\r")]
|
||||
[TestCase("\r- i\r- j\r")]
|
||||
[TestCase("\r- i\r\n- j\r")]
|
||||
|
||||
[TestCase("\r\n- i\n- j\r")]
|
||||
[TestCase("\r\n- i\r- j\r")]
|
||||
[TestCase("\r\n- i\r\n- j\r")]
|
||||
|
||||
[TestCase("- i\n- j\r\n")]
|
||||
[TestCase("- i\r- j\r\n")]
|
||||
[TestCase("- i\r\n- j\r\n")]
|
||||
|
||||
[TestCase("\n- i\n- j\r\n")]
|
||||
[TestCase("\n- i\r- j\r\n")]
|
||||
[TestCase("\n- i\r\n- j\r\n")]
|
||||
|
||||
[TestCase("\r- i\n- j\r\n")]
|
||||
[TestCase("\r- i\r- j\r\n")]
|
||||
[TestCase("\r- i\r\n- j\r\n")]
|
||||
|
||||
[TestCase("\r\n- i\n- j\r\n")]
|
||||
[TestCase("\r\n- i\r- j\r\n")]
|
||||
[TestCase("\r\n- i\r\n- j\r\n")]
|
||||
|
||||
[TestCase("- i\n")]
|
||||
[TestCase("- i\n\n")]
|
||||
[TestCase("- i\n\n\n")]
|
||||
[TestCase("- i\n\n\n\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
//
|
||||
// The rules for detecting a link are:
|
||||
//
|
||||
// - The project key must be composed of one or more capitalized ASCII letter `[A-Z]+`
|
||||
// - The project key must be composed of one or more capitalized ASCII letters or digits `[A-Z,0-9]+`
|
||||
// - A single hyphen `-` must separate the project key and issue number.
|
||||
// - The issue number is composed of 1 or more digits `[0, 9]+`
|
||||
// - The reference must be preceded by either `(` or whitespace or EOF.
|
||||
@@ -53,13 +53,13 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a KIRA-1 issue
|
||||
// This is a ABC4-123 issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a <a href="http://your.company.abc/browse/KIRA-1" target="blank">KIRA-1</a> issue</p>
|
||||
// <p>This is a <a href="http://your.company.abc/browse/ABC4-123" target="blank">ABC4-123</a> issue</p>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a KIRA-1 issue", "<p>This is a <a href=\"http://your.company.abc/browse/KIRA-1\" target=\"blank\">KIRA-1</a> issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a ABC4-123 issue", "<p>This is a <a href=\"http://your.company.abc/browse/ABC4-123\" target=\"blank\">ABC4-123</a> issue</p>", "jiralinks");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -69,16 +69,15 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a Z-1 issue
|
||||
// This is a ABC45-123 issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a <a href="http://your.company.abc/browse/Z-1" target="blank">Z-1</a> issue</p>
|
||||
// <p>This is a <a href="http://your.company.abc/browse/ABC45-123" target="blank">ABC45-123</a> issue</p>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a Z-1 issue", "<p>This is a <a href=\"http://your.company.abc/browse/Z-1\" target=\"blank\">Z-1</a> issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a ABC45-123 issue", "<p>This is a <a href=\"http://your.company.abc/browse/ABC45-123\" target=\"blank\">ABC45-123</a> issue</p>", "jiralinks");
|
||||
}
|
||||
|
||||
// These are also valid links with `(` and `)`:
|
||||
[Test]
|
||||
public void JiraLinks_Example004()
|
||||
{
|
||||
@@ -86,13 +85,13 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a (ABCD-123) issue
|
||||
// This is a KIRA-1 issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a (<a href="http://your.company.abc/browse/ABCD-123" target="blank">ABCD-123</a>) issue</p>
|
||||
// <p>This is a <a href="http://your.company.abc/browse/KIRA-1" target="blank">KIRA-1</a> issue</p>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a (ABCD-123) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/ABCD-123\" target=\"blank\">ABCD-123</a>) issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a KIRA-1 issue", "<p>This is a <a href=\"http://your.company.abc/browse/KIRA-1\" target=\"blank\">KIRA-1</a> issue</p>", "jiralinks");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -102,15 +101,16 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a (KIRA-1) issue
|
||||
// This is a Z-1 issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a (<a href="http://your.company.abc/browse/KIRA-1" target="blank">KIRA-1</a>) issue</p>
|
||||
// <p>This is a <a href="http://your.company.abc/browse/Z-1" target="blank">Z-1</a> issue</p>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a (KIRA-1) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/KIRA-1\" target=\"blank\">KIRA-1</a>) issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a Z-1 issue", "<p>This is a <a href=\"http://your.company.abc/browse/Z-1\" target=\"blank\">Z-1</a> issue</p>", "jiralinks");
|
||||
}
|
||||
|
||||
// These are also valid links with `(` and `)`:
|
||||
[Test]
|
||||
public void JiraLinks_Example006()
|
||||
{
|
||||
@@ -118,16 +118,15 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a (Z-1) issue
|
||||
// This is a (ABCD-123) issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a (<a href="http://your.company.abc/browse/Z-1" target="blank">Z-1</a>) issue</p>
|
||||
// <p>This is a (<a href="http://your.company.abc/browse/ABCD-123" target="blank">ABCD-123</a>) issue</p>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a (Z-1) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/Z-1\" target=\"blank\">Z-1</a>) issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a (ABCD-123) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/ABCD-123\" target=\"blank\">ABCD-123</a>) issue</p>", "jiralinks");
|
||||
}
|
||||
|
||||
// These are not valid links:
|
||||
[Test]
|
||||
public void JiraLinks_Example007()
|
||||
{
|
||||
@@ -135,13 +134,13 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is not aJIRA-123 issue
|
||||
// This is a (ABC4-123) issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is not aJIRA-123 issue</p>
|
||||
// <p>This is a (<a href="http://your.company.abc/browse/ABC4-123" target="blank">ABC4-123</a>) issue</p>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is not aJIRA-123 issue", "<p>This is not aJIRA-123 issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a (ABC4-123) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/ABC4-123\" target=\"blank\">ABC4-123</a>) issue</p>", "jiralinks");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -151,13 +150,13 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is not JIRA-123a issue
|
||||
// This is a (KIRA-1) issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is not JIRA-123a issue</p>
|
||||
// <p>This is a (<a href="http://your.company.abc/browse/KIRA-1" target="blank">KIRA-1</a>) issue</p>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is not JIRA-123a issue", "<p>This is not JIRA-123a issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a (KIRA-1) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/KIRA-1\" target=\"blank\">KIRA-1</a>) issue</p>", "jiralinks");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -167,13 +166,94 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a (Z-1) issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a (<a href="http://your.company.abc/browse/Z-1" target="blank">Z-1</a>) issue</p>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a (Z-1) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/Z-1\" target=\"blank\">Z-1</a>) issue</p>", "jiralinks");
|
||||
}
|
||||
|
||||
// These are not valid links:
|
||||
[Test]
|
||||
public void JiraLinks_Example010()
|
||||
{
|
||||
// Example 10
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is not aJIRA-123 issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is not aJIRA-123 issue</p>
|
||||
|
||||
Console.WriteLine("Example 10\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is not aJIRA-123 issue", "<p>This is not aJIRA-123 issue</p>", "jiralinks");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void JiraLinks_Example011()
|
||||
{
|
||||
// Example 11
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is not 4JIRA-123 issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is not 4JIRA-123 issue</p>
|
||||
|
||||
Console.WriteLine("Example 11\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is not 4JIRA-123 issue", "<p>This is not 4JIRA-123 issue</p>", "jiralinks");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void JiraLinks_Example012()
|
||||
{
|
||||
// Example 12
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is not JIRA-123a issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is not JIRA-123a issue</p>
|
||||
|
||||
Console.WriteLine("Example 12\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is not JIRA-123a issue", "<p>This is not JIRA-123a issue</p>", "jiralinks");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void JiraLinks_Example013()
|
||||
{
|
||||
// Example 13
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is not JIRA- issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is not JIRA- issue</p>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Jira Links\n");
|
||||
Console.WriteLine("Example 13\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is not JIRA- issue", "<p>This is not JIRA- issue</p>", "jiralinks");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void JiraLinks_Example014()
|
||||
{
|
||||
// Example 14
|
||||
// Section: Jira Links
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is not JIR4- issue
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is not JIR4- issue</p>
|
||||
|
||||
Console.WriteLine("Example 14\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is not JIR4- issue", "<p>This is not JIR4- issue</p>", "jiralinks");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ var pipeline = new MarkdownPipelineBuilder()
|
||||
|
||||
The rules for detecting a link are:
|
||||
|
||||
- The project key must be composed of one or more capitalized ASCII letter `[A-Z]+`
|
||||
- The project key must be composed of one or more capitalized ASCII letters or digits `[A-Z,0-9]+`
|
||||
- A single hyphen `-` must separate the project key and issue number.
|
||||
- The issue number is composed of 1 or more digits `[0, 9]+`
|
||||
- The reference must be preceded by either `(` or whitespace or EOF.
|
||||
@@ -24,6 +24,18 @@ This is a ABCD-123 issue
|
||||
<p>This is a <a href="http://your.company.abc/browse/ABCD-123" target="blank">ABCD-123</a> issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a ABC4-123 issue
|
||||
.
|
||||
<p>This is a <a href="http://your.company.abc/browse/ABC4-123" target="blank">ABC4-123</a> issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a ABC45-123 issue
|
||||
.
|
||||
<p>This is a <a href="http://your.company.abc/browse/ABC45-123" target="blank">ABC45-123</a> issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a KIRA-1 issue
|
||||
.
|
||||
@@ -44,6 +56,12 @@ This is a (ABCD-123) issue
|
||||
<p>This is a (<a href="http://your.company.abc/browse/ABCD-123" target="blank">ABCD-123</a>) issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a (ABC4-123) issue
|
||||
.
|
||||
<p>This is a (<a href="http://your.company.abc/browse/ABC4-123" target="blank">ABC4-123</a>) issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a (KIRA-1) issue
|
||||
.
|
||||
@@ -64,6 +82,12 @@ This is not aJIRA-123 issue
|
||||
<p>This is not aJIRA-123 issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not 4JIRA-123 issue
|
||||
.
|
||||
<p>This is not 4JIRA-123 issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not JIRA-123a issue
|
||||
.
|
||||
@@ -75,3 +99,9 @@ This is not JIRA- issue
|
||||
.
|
||||
<p>This is not JIRA- issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not JIR4- issue
|
||||
.
|
||||
<p>This is not JIR4- issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Markdig.Tests
|
||||
{
|
||||
var inputTag = "<a>";
|
||||
var text = new StringSlice(inputTag);
|
||||
Assert.True(HtmlHelper.TryParseHtmlTag(text, out string outputTag));
|
||||
Assert.True(HtmlHelper.TryParseHtmlTag(ref text, out string outputTag));
|
||||
Assert.AreEqual(inputTag, outputTag);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Markdig.Tests
|
||||
{
|
||||
var inputTag = "<a href='http://google.com'>";
|
||||
var text = new StringSlice(inputTag);
|
||||
Assert.True(HtmlHelper.TryParseHtmlTag(text, out string outputTag));
|
||||
Assert.True(HtmlHelper.TryParseHtmlTag(ref text, out string outputTag));
|
||||
Assert.AreEqual(inputTag, outputTag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Markdig.Tests
|
||||
public void TestUrlSimple()
|
||||
{
|
||||
var text = new StringSlice("toto tutu");
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link));
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
Assert.AreEqual("toto", link);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
@@ -24,7 +24,7 @@ namespace Markdig.Tests
|
||||
public void TestUrlUrl()
|
||||
{
|
||||
var text = new StringSlice("http://google.com)");
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link));
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual(')', text.CurrentChar);
|
||||
}
|
||||
@@ -35,7 +35,7 @@ namespace Markdig.Tests
|
||||
public void TestUrlTrailingFullStop(string uri)
|
||||
{
|
||||
var text = new StringSlice(uri);
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, true));
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _, true));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual('.', text.CurrentChar);
|
||||
}
|
||||
@@ -44,7 +44,7 @@ namespace Markdig.Tests
|
||||
public void TestUrlNestedParenthesis()
|
||||
{
|
||||
var text = new StringSlice("(toto)tutu(tata) nooo");
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link));
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
Assert.AreEqual("(toto)tutu(tata)", link);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
@@ -53,7 +53,7 @@ namespace Markdig.Tests
|
||||
public void TestUrlAlternate()
|
||||
{
|
||||
var text = new StringSlice("<toto_tata_tutu> nooo");
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link));
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
Assert.AreEqual("toto_tata_tutu", link);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
@@ -62,14 +62,14 @@ namespace Markdig.Tests
|
||||
public void TestUrlAlternateInvalid()
|
||||
{
|
||||
var text = new StringSlice("<toto_tata_tutu");
|
||||
Assert.False(LinkHelper.TryParseUrl(ref text, out string link));
|
||||
Assert.False(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTitleSimple()
|
||||
{
|
||||
var text = new StringSlice(@"'tata\tutu\''");
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title));
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title, out _));
|
||||
Assert.AreEqual(@"tata\tutu'", title);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Markdig.Tests
|
||||
public void TestTitleSimpleAlternate()
|
||||
{
|
||||
var text = new StringSlice(@"""tata\tutu\"""" ");
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title));
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title, out _));
|
||||
Assert.AreEqual(@"tata\tutu""", title);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,14 @@ namespace Markdig.Tests
|
||||
[Test]
|
||||
public void TestToHtml()
|
||||
{
|
||||
string html = Markdown.ToHtml("This is a text with some *emphasis*");
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string html = Markdown.ToHtml("This is a text with some *emphasis*");
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
|
||||
html = Markdown.ToHtml("This is a text with a https://link.tld/");
|
||||
Assert.AreNotEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
html = Markdown.ToHtml("This is a text with a https://link.tld/");
|
||||
Assert.AreNotEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -24,49 +27,66 @@ namespace Markdig.Tests
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.Build();
|
||||
|
||||
string html = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string html = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
|
||||
html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
|
||||
Assert.AreNotEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
|
||||
Assert.AreNotEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
}
|
||||
|
||||
pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToHtmlWithWriter()
|
||||
{
|
||||
StringWriter writer = new StringWriter();
|
||||
var writer = new StringWriter();
|
||||
|
||||
_ = Markdown.ToHtml("This is a text with some *emphasis*", writer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_ = Markdown.ToHtml("This is a text with some *emphasis*", writer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
|
||||
writer = new StringWriter();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
_ = Markdown.ToHtml("This is a text with a https://link.tld/", writer, pipeline);
|
||||
html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_ = Markdown.ToHtml("This is a text with a https://link.tld/", writer, pipeline);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConvert()
|
||||
{
|
||||
StringWriter writer = new StringWriter();
|
||||
HtmlRenderer renderer = new HtmlRenderer(writer);
|
||||
var writer = new StringWriter();
|
||||
var renderer = new HtmlRenderer(writer);
|
||||
|
||||
_ = Markdown.Convert("This is a text with some *emphasis*", renderer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_ = Markdown.Convert("This is a text with some *emphasis*", renderer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
|
||||
writer = new StringWriter();
|
||||
renderer = new HtmlRenderer(writer);
|
||||
@@ -74,9 +94,13 @@ namespace Markdig.Tests
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
_ = Markdown.Convert("This is a text with a https://link.tld/", renderer, pipeline);
|
||||
html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_ = Markdown.Convert("This is a text with a https://link.tld/", renderer, pipeline);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -88,62 +112,77 @@ namespace Markdig.Tests
|
||||
.UsePreciseSourceLocation()
|
||||
.Build();
|
||||
|
||||
MarkdownDocument document = Markdown.Parse(markdown, pipeline);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse(markdown, pipeline);
|
||||
|
||||
Assert.AreEqual(1, document.LineCount);
|
||||
Assert.AreEqual(markdown.Length, document.Span.Length);
|
||||
Assert.AreEqual(1, document.LineStartIndexes.Count);
|
||||
Assert.AreEqual(0, document.LineStartIndexes[0]);
|
||||
Assert.AreEqual(1, document.LineCount);
|
||||
Assert.AreEqual(markdown.Length, document.Span.Length);
|
||||
Assert.AreEqual(1, document.LineStartIndexes.Count);
|
||||
Assert.AreEqual(0, document.LineStartIndexes[0]);
|
||||
|
||||
Assert.AreEqual(1, document.Count);
|
||||
ParagraphBlock paragraph = document[0] as ParagraphBlock;
|
||||
Assert.NotNull(paragraph);
|
||||
Assert.AreEqual(markdown.Length, paragraph.Span.Length);
|
||||
LiteralInline literal = paragraph.Inline.FirstChild as LiteralInline;
|
||||
Assert.NotNull(literal);
|
||||
Assert.AreEqual("This is a text with some ", literal.ToString());
|
||||
EmphasisInline emphasis = literal.NextSibling as EmphasisInline;
|
||||
Assert.NotNull(emphasis);
|
||||
Assert.AreEqual("*emphasis*".Length, emphasis.Span.Length);
|
||||
LiteralInline emphasisLiteral = emphasis.FirstChild as LiteralInline;
|
||||
Assert.NotNull(emphasisLiteral);
|
||||
Assert.AreEqual("emphasis", emphasisLiteral.ToString());
|
||||
Assert.Null(emphasisLiteral.NextSibling);
|
||||
Assert.Null(emphasis.NextSibling);
|
||||
Assert.AreEqual(1, document.Count);
|
||||
ParagraphBlock paragraph = document[0] as ParagraphBlock;
|
||||
Assert.NotNull(paragraph);
|
||||
Assert.AreEqual(markdown.Length, paragraph.Span.Length);
|
||||
LiteralInline literal = paragraph.Inline.FirstChild as LiteralInline;
|
||||
Assert.NotNull(literal);
|
||||
Assert.AreEqual("This is a text with some ", literal.ToString());
|
||||
EmphasisInline emphasis = literal.NextSibling as EmphasisInline;
|
||||
Assert.NotNull(emphasis);
|
||||
Assert.AreEqual("*emphasis*".Length, emphasis.Span.Length);
|
||||
LiteralInline emphasisLiteral = emphasis.FirstChild as LiteralInline;
|
||||
Assert.NotNull(emphasisLiteral);
|
||||
Assert.AreEqual("emphasis", emphasisLiteral.ToString());
|
||||
Assert.Null(emphasisLiteral.NextSibling);
|
||||
Assert.Null(emphasis.NextSibling);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormalize()
|
||||
{
|
||||
string normalized = Markdown.Normalize("Heading\n=======");
|
||||
Assert.AreEqual("# Heading", normalized);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string normalized = Markdown.Normalize("Heading\n=======");
|
||||
Assert.AreEqual("# Heading", normalized);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormalizeWithWriter()
|
||||
{
|
||||
StringWriter writer = new StringWriter();
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
|
||||
_ = Markdown.Normalize("Heading\n=======", writer);
|
||||
string normalized = writer.ToString();
|
||||
Assert.AreEqual("# Heading", normalized);
|
||||
_ = Markdown.Normalize("Heading\n=======", writer);
|
||||
string normalized = writer.ToString();
|
||||
Assert.AreEqual("# Heading", normalized);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToPlainText()
|
||||
{
|
||||
string plainText = Markdown.ToPlainText("*Hello*, [world](http://example.com)!");
|
||||
Assert.AreEqual("Hello, world!\n", plainText);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string plainText = Markdown.ToPlainText("*Hello*, [world](http://example.com)!");
|
||||
Assert.AreEqual("Hello, world!\n", plainText);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToPlainTextWithWriter()
|
||||
{
|
||||
StringWriter writer = new StringWriter();
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
|
||||
_ = Markdown.ToPlainText("*Hello*, [world](http://example.com)!", writer);
|
||||
string plainText = writer.ToString();
|
||||
Assert.AreEqual("Hello, world!\n", plainText);
|
||||
_ = Markdown.ToPlainText("*Hello*, [world](http://example.com)!", writer);
|
||||
string plainText = writer.ToString();
|
||||
Assert.AreEqual("Hello, world!\n", plainText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
src/Markdig.Tests/TestNewLine.cs
Normal file
17
src/Markdig.Tests/TestNewLine.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestNewLine
|
||||
{
|
||||
[TestCase("a \nb", "<p>a<br />\nb</p>\n")]
|
||||
[TestCase("a\\\nb", "<p>a<br />\nb</p>\n")]
|
||||
[TestCase("a `b\nc`", "<p>a <code>b c</code></p>\n")]
|
||||
public void Test(string value, string expectedHtml)
|
||||
{
|
||||
Assert.AreEqual(expectedHtml, Markdown.ToHtml(value));
|
||||
Assert.AreEqual(expectedHtml, Markdown.ToHtml(value.Replace("\n", "\r\n")));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,8 @@ namespace Markdig.Tests
|
||||
AssertSyntax("````csharp\npublic void HelloWorld()\n{\n}\n````", new FencedCodeBlock(null)
|
||||
{
|
||||
FencedChar = '`',
|
||||
FencedCharCount = 4,
|
||||
OpeningFencedCharCount = 4,
|
||||
ClosingFencedCharCount = 4,
|
||||
Info = "csharp",
|
||||
Lines = new StringLineGroup(4)
|
||||
{
|
||||
|
||||
26
src/Markdig.Tests/TestPipeTable.cs
Normal file
26
src/Markdig.Tests/TestPipeTable.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Markdig.Extensions.Tables;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
using System.Linq;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class TestPipeTable
|
||||
{
|
||||
[TestCase("| S | T |\r\n|---|---| \r\n| G | H |")]
|
||||
[TestCase("| S | T |\r\n|---|---|\t\r\n| G | H |")]
|
||||
[TestCase("| S | T |\r\n|---|---|\v\r\n| G | H |")]
|
||||
[TestCase("| S | T |\r\n|---|---|\f\r\n| G | H |")]
|
||||
[TestCase("| S | T |\r\n|---|---|\f\v\t \r\n| G | H |")]
|
||||
[TestCase("| S | \r\n|---|\r\n| G |\r\n\r\n| D | D |\r\n| ---| ---| \r\n| V | V |", 2)]
|
||||
public void TestTableBug(string markdown, int tableCount = 1)
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse(markdown, new MarkdownPipelineBuilder().UseAdvancedExtensions().Build());
|
||||
|
||||
Table[] tables = document.Descendants().OfType<Table>().ToArray();
|
||||
|
||||
Assert.AreEqual(tableCount, tables.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,16 @@ namespace Markdig.Tests
|
||||
[TestFixture]
|
||||
public class TestPlayParser
|
||||
{
|
||||
[Test]
|
||||
public void TestLinksWithCarriageReturn()
|
||||
{
|
||||
{
|
||||
var text = "[Link 1][link-1], [link 2][link-2].\r\n\r\n[link-1]: https://example.com\r\n[link-2]: https://example.com";
|
||||
var result = Markdown.ToHtml(text).TrimEnd();
|
||||
Assert.AreEqual("<p><a href=\"https://example.com\">Link 1</a>, <a href=\"https://example.com\">link 2</a>.</p>", result);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLink()
|
||||
{
|
||||
|
||||
@@ -37,20 +37,6 @@ namespace Markdig.Tests
|
||||
Assert.That(html, Contains.Substring($"rel=\"{expected}\""));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void TestLinksWithNullRel()
|
||||
{
|
||||
var markdown = "[world](http://example.com)";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseReferralLinks(null)
|
||||
.Build();
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
Assert.That(html, !Contains.Substring("rel="));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(new[] { "noopener" }, "noopener")]
|
||||
[TestCase(new[] { "nofollow" }, "nofollow")]
|
||||
|
||||
30
src/Markdig.Tests/TestRoundtrip.cs
Normal file
30
src/Markdig.Tests/TestRoundtrip.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Markdig.Renderers.Roundtrip;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
internal static class TestRoundtrip
|
||||
{
|
||||
internal static void TestSpec(string markdownText, string expected, string extensions)
|
||||
{
|
||||
RoundTrip(markdownText);
|
||||
}
|
||||
|
||||
internal static void RoundTrip(string markdown)
|
||||
{
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
pipelineBuilder.EnableTrackTrivia();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var sw = new StringWriter();
|
||||
var nr = new RoundtripRenderer(sw);
|
||||
|
||||
nr.Write(markdownDocument);
|
||||
|
||||
var result = sw.ToString();
|
||||
Assert.AreEqual(markdown, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,8 @@ namespace Markdig.Tests
|
||||
{
|
||||
var text = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("ABC"),
|
||||
new StringSlice("E"),
|
||||
new StringSlice("ABC", NewLine.LineFeed),
|
||||
new StringSlice("E", NewLine.LineFeed),
|
||||
new StringSlice("F")
|
||||
};
|
||||
|
||||
@@ -35,8 +35,8 @@ namespace Markdig.Tests
|
||||
{
|
||||
var text = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("XABC") { Start = 1},
|
||||
new StringSlice("YYE") { Start = 2},
|
||||
new StringSlice("XABC", NewLine.LineFeed) { Start = 1},
|
||||
new StringSlice("YYE", NewLine.LineFeed) { Start = 2},
|
||||
new StringSlice("ZZZF") { Start = 3 }
|
||||
};
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Markdig.Tests
|
||||
{
|
||||
var text = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("ABCD"),
|
||||
new StringSlice("ABCD", NewLine.LineFeed),
|
||||
new StringSlice("EF"),
|
||||
}.ToCharIterator();
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace Markdig.Tests
|
||||
[Test]
|
||||
public void TestStringLineGroupWithModifiedStart()
|
||||
{
|
||||
var line1 = new StringSlice(" ABC");
|
||||
var line1 = new StringSlice(" ABC", NewLine.LineFeed);
|
||||
line1.NextChar();
|
||||
line1.NextChar();
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace Markdig.Tests
|
||||
[Test]
|
||||
public void TestStringLineGroupWithTrim()
|
||||
{
|
||||
var line1 = new StringSlice(" ABC ");
|
||||
var line1 = new StringSlice(" ABC ", NewLine.LineFeed);
|
||||
line1.NextChar();
|
||||
line1.NextChar();
|
||||
|
||||
@@ -133,8 +133,8 @@ namespace Markdig.Tests
|
||||
{
|
||||
var iterator = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("ABC"),
|
||||
new StringSlice("E"),
|
||||
new StringSlice("ABC", NewLine.LineFeed),
|
||||
new StringSlice("E", NewLine.LineFeed),
|
||||
new StringSlice("F")
|
||||
}.ToCharIterator();
|
||||
|
||||
@@ -153,5 +153,33 @@ namespace Markdig.Tests
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => iterator.PeekChar(-1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIteratorSkipChar()
|
||||
{
|
||||
var lineGroup = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("ABC", NewLine.LineFeed),
|
||||
new StringSlice("E", NewLine.LineFeed)
|
||||
};
|
||||
|
||||
Test(lineGroup.ToCharIterator());
|
||||
|
||||
Test(new StringSlice("ABC\nE\n"));
|
||||
|
||||
Test(new StringSlice("Foo\nABC\nE\n", 4, 9));
|
||||
|
||||
static void Test<T>(T iterator) where T : ICharIterator
|
||||
{
|
||||
Assert.AreEqual('A', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('B', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('C', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('\n', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('E', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('\n', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('\0', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('\0', iterator.CurrentChar); iterator.SkipChar();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
/// <summary>
|
||||
/// Gets or sets the label.
|
||||
/// </summary>
|
||||
public string Label { get; set; }
|
||||
public string? Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The text associated to this label.
|
||||
|
||||
@@ -22,12 +22,12 @@ namespace Markdig.Extensions.Abbreviations
|
||||
|
||||
public static void AddAbbreviation(this MarkdownDocument document, string label, Abbreviation abbr)
|
||||
{
|
||||
if (document == null) ThrowHelper.ArgumentNullException(nameof(document));
|
||||
if (label == null) ThrowHelper.ArgumentNullException_label();
|
||||
if (abbr == null) ThrowHelper.ArgumentNullException(nameof(abbr));
|
||||
if (document is null) ThrowHelper.ArgumentNullException(nameof(document));
|
||||
if (label is null) ThrowHelper.ArgumentNullException_label();
|
||||
if (abbr is null) ThrowHelper.ArgumentNullException(nameof(abbr));
|
||||
|
||||
var map = document.GetAbbreviations();
|
||||
if (map == null)
|
||||
if (map is null)
|
||||
{
|
||||
map = new Dictionary<string, Abbreviation>();
|
||||
document.SetData(DocumentKey, map);
|
||||
@@ -35,7 +35,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
map[label] = abbr;
|
||||
}
|
||||
|
||||
public static Dictionary<string, Abbreviation> GetAbbreviations(this MarkdownDocument document)
|
||||
public static Dictionary<string, Abbreviation>? GetAbbreviations(this MarkdownDocument document)
|
||||
{
|
||||
return document.GetData(DocumentKey) as Dictionary<string, Abbreviation>;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
@@ -14,13 +14,6 @@ namespace Markdig.Extensions.Abbreviations
|
||||
[DebuggerDisplay("{Abbreviation}")]
|
||||
public class AbbreviationInline : LeafInline
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbbreviationInline"/> class.
|
||||
/// </summary>
|
||||
public AbbreviationInline()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbbreviationInline"/> class.
|
||||
/// </summary>
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
if (!LinkHelper.TryParseLabel(ref slice, out string label, out SourceSpan labelSpan))
|
||||
if (!LinkHelper.TryParseLabel(ref slice, out string? label, out SourceSpan labelSpan))
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
@@ -51,7 +51,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
slice.NextChar();
|
||||
slice.SkipChar();
|
||||
|
||||
slice.Trim();
|
||||
|
||||
@@ -73,13 +73,13 @@ namespace Markdig.Extensions.Abbreviations
|
||||
return BlockState.BreakDiscard;
|
||||
}
|
||||
|
||||
private void DocumentOnProcessInlinesBegin(InlineProcessor inlineProcessor, Inline inline)
|
||||
private void DocumentOnProcessInlinesBegin(InlineProcessor inlineProcessor, Inline? inline)
|
||||
{
|
||||
inlineProcessor.Document.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;
|
||||
|
||||
var abbreviations = inlineProcessor.Document.GetAbbreviations();
|
||||
// Should not happen, but another extension could decide to remove them, so...
|
||||
if (abbreviations == null)
|
||||
if (abbreviations is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -89,10 +89,10 @@ namespace Markdig.Extensions.Abbreviations
|
||||
|
||||
inlineProcessor.LiteralInlineParser.PostMatch += (InlineProcessor processor, ref StringSlice slice) =>
|
||||
{
|
||||
var literal = (LiteralInline)processor.Inline;
|
||||
var literal = (LiteralInline)processor.Inline!;
|
||||
var originalLiteral = literal;
|
||||
|
||||
ContainerInline container = null;
|
||||
ContainerInline? container = null;
|
||||
|
||||
// This is slow, but we don't have much the choice
|
||||
var content = literal.Content;
|
||||
@@ -127,7 +127,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
var indexAfterMatch = i + match.Length;
|
||||
|
||||
// If we don't have a container, create a new one
|
||||
if (container == null)
|
||||
if (container is null)
|
||||
{
|
||||
container = literal.Parent ??
|
||||
new ContainerInline
|
||||
@@ -150,7 +150,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
abbrInline.Span.End = abbrInline.Span.Start + match.Length - 1;
|
||||
|
||||
// Append the previous literal
|
||||
if (i > content.Start && literal.Parent == null)
|
||||
if (i > content.Start && literal.Parent is null)
|
||||
{
|
||||
container.AppendChild(literal);
|
||||
}
|
||||
|
||||
@@ -74,15 +74,14 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
|
||||
var text = headingLine.ToString();
|
||||
|
||||
var linkRef = new HeadingLinkReferenceDefinition()
|
||||
var linkRef = new HeadingLinkReferenceDefinition(headingBlock)
|
||||
{
|
||||
Heading = headingBlock,
|
||||
CreateLinkInline = CreateLinkInlineForHeading
|
||||
};
|
||||
|
||||
var doc = processor.Document;
|
||||
var dictionary = doc.GetData(this) as Dictionary<string, HeadingLinkReferenceDefinition>;
|
||||
if (dictionary == null)
|
||||
if (dictionary is null)
|
||||
{
|
||||
dictionary = new Dictionary<string, HeadingLinkReferenceDefinition>();
|
||||
doc.SetData(this, dictionary);
|
||||
@@ -95,11 +94,11 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
headingBlock.ProcessInlinesEnd += HeadingBlock_ProcessInlinesEnd;
|
||||
}
|
||||
|
||||
private void DocumentOnProcessInlinesBegin(InlineProcessor processor, Inline inline)
|
||||
private void DocumentOnProcessInlinesBegin(InlineProcessor processor, Inline? inline)
|
||||
{
|
||||
var doc = processor.Document;
|
||||
doc.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;
|
||||
var dictionary = (Dictionary<string, HeadingLinkReferenceDefinition>)doc.GetData(this);
|
||||
var dictionary = (Dictionary<string, HeadingLinkReferenceDefinition>)doc.GetData(this)!;
|
||||
foreach (var keyPair in dictionary)
|
||||
{
|
||||
// Here we make sure that auto-identifiers will not override an existing link definition
|
||||
@@ -107,7 +106,7 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
// If it is the case, we skip the auto identifier for the Heading
|
||||
if (!doc.TryGetLinkReferenceDefinition(keyPair.Key, out var linkDef))
|
||||
{
|
||||
doc.SetLinkReferenceDefinition(keyPair.Key, keyPair.Value);
|
||||
doc.SetLinkReferenceDefinition(keyPair.Key, keyPair.Value, true);
|
||||
}
|
||||
}
|
||||
// Once we are done, we don't need to keep the intermediate dictionary around
|
||||
@@ -118,7 +117,7 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
/// Callback when there is a reference to found to a heading.
|
||||
/// Note that reference are only working if they are declared after.
|
||||
/// </summary>
|
||||
private Inline CreateLinkInlineForHeading(InlineProcessor inlineState, LinkReferenceDefinition linkRef, Inline child)
|
||||
private Inline CreateLinkInlineForHeading(InlineProcessor inlineState, LinkReferenceDefinition linkRef, Inline? child)
|
||||
{
|
||||
var headingRef = (HeadingLinkReferenceDefinition) linkRef;
|
||||
return new LinkInline()
|
||||
@@ -135,23 +134,23 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
/// </summary>
|
||||
/// <param name="processor">The processor.</param>
|
||||
/// <param name="inline">The inline.</param>
|
||||
private void HeadingBlock_ProcessInlinesEnd(InlineProcessor processor, Inline inline)
|
||||
private void HeadingBlock_ProcessInlinesEnd(InlineProcessor processor, Inline? inline)
|
||||
{
|
||||
var identifiers = processor.Document.GetData(AutoIdentifierKey) as HashSet<string>;
|
||||
if (identifiers == null)
|
||||
if (identifiers is null)
|
||||
{
|
||||
identifiers = new HashSet<string>();
|
||||
processor.Document.SetData(AutoIdentifierKey, identifiers);
|
||||
}
|
||||
|
||||
var headingBlock = (HeadingBlock) processor.Block;
|
||||
if (headingBlock.Inline == null)
|
||||
var headingBlock = (HeadingBlock) processor.Block!;
|
||||
if (headingBlock.Inline is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If id is already set, don't try to modify it
|
||||
var attributes = processor.Block.GetAttributes();
|
||||
var attributes = processor.Block!.GetAttributes();
|
||||
if (attributes.Id != null)
|
||||
{
|
||||
return;
|
||||
@@ -161,7 +160,7 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
var stripRenderer = rendererCache.Get();
|
||||
|
||||
stripRenderer.Render(headingBlock.Inline);
|
||||
var headingText = stripRenderer.Writer.ToString();
|
||||
var headingText = stripRenderer.Writer.ToString()!;
|
||||
rendererCache.Release(stripRenderer);
|
||||
|
||||
// Urilize the link
|
||||
|
||||
@@ -12,6 +12,11 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
/// <seealso cref="LinkReferenceDefinition" />
|
||||
public class HeadingLinkReferenceDefinition : LinkReferenceDefinition
|
||||
{
|
||||
public HeadingLinkReferenceDefinition(HeadingBlock headling)
|
||||
{
|
||||
Heading = headling;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the heading related to this link reference definition.
|
||||
/// </summary>
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Markdig.Extensions.AutoLinks
|
||||
{
|
||||
public readonly AutoLinkOptions Options;
|
||||
|
||||
public AutoLinkExtension(AutoLinkOptions options)
|
||||
public AutoLinkExtension(AutoLinkOptions? options)
|
||||
{
|
||||
Options = options ?? new AutoLinkOptions();
|
||||
}
|
||||
|
||||
@@ -105,7 +105,8 @@ namespace Markdig.Extensions.AutoLinks
|
||||
break;
|
||||
}
|
||||
|
||||
if (!LinkHelper.TryParseUrl(ref slice, out string link, true))
|
||||
// Parse URL
|
||||
if (!LinkHelper.TryParseUrl(ref slice, out string? link, out _, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using Markdig.Parsers.Inlines;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html.Inlines;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Markdig.Extensions.Citations
|
||||
{
|
||||
@@ -40,7 +40,7 @@ namespace Markdig.Extensions.Citations
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetTag(EmphasisInline emphasisInline)
|
||||
private static string? GetTag(EmphasisInline emphasisInline)
|
||||
{
|
||||
Debug.Assert(emphasisInline.DelimiterCount <= 2);
|
||||
return emphasisInline.DelimiterCount == 2 && emphasisInline.DelimiterChar == '"' ? "cite" : null;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Syntax;
|
||||
|
||||
@@ -22,12 +23,40 @@ namespace Markdig.Extensions.CustomContainers
|
||||
{
|
||||
}
|
||||
|
||||
public string Info { get; set; }
|
||||
|
||||
public string Arguments { get; set; }
|
||||
|
||||
public int FencedCharCount { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public char FencedChar { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int OpeningFencedCharCount { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public StringSlice TriviaAfterFencedChar { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? Info { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public StringSlice UnescapedInfo { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public StringSlice TriviaAfterInfo { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? Arguments { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public StringSlice UnescapedArguments { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public StringSlice TriviaAfterArguments { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public NewLine InfoNewLine { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public StringSlice TriviaBeforeClosingFence { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int ClosingFencedCharCount { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Markdig.Extensions.CustomContainers
|
||||
{
|
||||
protected override void Write(HtmlRenderer renderer, CustomContainerInline obj)
|
||||
{
|
||||
renderer.Write("<span").WriteAttributes(obj).Write(">");
|
||||
renderer.Write("<span").WriteAttributes(obj).Write('>');
|
||||
renderer.WriteChildren(obj);
|
||||
renderer.Write("</span>");
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Markdig.Extensions.CustomContainers
|
||||
renderer.EnsureLine();
|
||||
if (renderer.EnableHtmlForBlock)
|
||||
{
|
||||
renderer.Write("<div").WriteAttributes(obj).Write(">");
|
||||
renderer.Write("<div").WriteAttributes(obj).Write('>');
|
||||
}
|
||||
// We don't escape a CustomContainer
|
||||
renderer.WriteChildren(obj);
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
public override BlockState TryOpen(BlockProcessor processor)
|
||||
{
|
||||
var paragraphBlock = processor.LastBlock as ParagraphBlock;
|
||||
if (processor.IsCodeIndent || paragraphBlock == null || paragraphBlock.LastLine - processor.LineIndex > 1)
|
||||
if (processor.IsCodeIndent || paragraphBlock is null || paragraphBlock.LastLine - processor.LineIndex > 1)
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
@@ -50,7 +50,7 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
processor.GoToColumn(column + 4);
|
||||
}
|
||||
|
||||
var previousParent = paragraphBlock.Parent;
|
||||
var previousParent = paragraphBlock.Parent!;
|
||||
var currentDefinitionList = GetCurrentDefinitionList(paragraphBlock, previousParent);
|
||||
|
||||
processor.Discard(paragraphBlock);
|
||||
@@ -61,7 +61,7 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
paragraphBlock.Parent.Remove(paragraphBlock);
|
||||
}
|
||||
|
||||
if (currentDefinitionList == null)
|
||||
if (currentDefinitionList is null)
|
||||
{
|
||||
currentDefinitionList = new DefinitionList(this)
|
||||
{
|
||||
@@ -90,7 +90,7 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
Span = new SourceSpan(paragraphBlock.Span.Start, paragraphBlock.Span.End),
|
||||
IsOpen = false
|
||||
};
|
||||
term.AppendLine(ref line.Slice, line.Column, line.Line, line.Position);
|
||||
term.AppendLine(ref line.Slice, line.Column, line.Line, line.Position, processor.TrackTrivia);
|
||||
definitionItem.Add(term);
|
||||
}
|
||||
currentDefinitionList.Add(definitionItem);
|
||||
@@ -102,7 +102,7 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
return BlockState.Continue;
|
||||
}
|
||||
|
||||
private static DefinitionList GetCurrentDefinitionList(ParagraphBlock paragraphBlock, ContainerBlock previousParent)
|
||||
private static DefinitionList? GetCurrentDefinitionList(ParagraphBlock paragraphBlock, ContainerBlock previousParent)
|
||||
{
|
||||
var index = previousParent.IndexOf(paragraphBlock) - 1;
|
||||
if (index < 0) return null;
|
||||
@@ -124,11 +124,11 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
return BlockState.Continue;
|
||||
}
|
||||
|
||||
var list = (DefinitionList)definitionItem.Parent;
|
||||
var list = (DefinitionList)definitionItem.Parent!;
|
||||
var lastBlankLine = definitionItem.LastChild as BlankLineBlock;
|
||||
|
||||
// Check if we have another definition list
|
||||
if (Array.IndexOf(OpeningCharacters, processor.CurrentChar) >= 0)
|
||||
if (Array.IndexOf(OpeningCharacters!, processor.CurrentChar) >= 0)
|
||||
{
|
||||
var startPosition = processor.Start;
|
||||
var column = processor.ColumnBeforeIndent;
|
||||
@@ -145,7 +145,7 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
definitionItem.RemoveAt(definitionItem.Count - 1);
|
||||
}
|
||||
|
||||
list.Span.End = list.LastChild.Span.End;
|
||||
list.Span.End = list.LastChild!.Span.End;
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
var isBreakable = definitionItem.LastChild?.IsBreakable ?? true;
|
||||
if (processor.IsBlankLine)
|
||||
{
|
||||
if (lastBlankLine == null && isBreakable)
|
||||
if (lastBlankLine is null && isBreakable)
|
||||
{
|
||||
definitionItem.Add(new BlankLineBlock());
|
||||
}
|
||||
@@ -179,7 +179,7 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
}
|
||||
|
||||
var paragraphBlock = definitionItem.LastChild as ParagraphBlock;
|
||||
if (lastBlankLine == null && paragraphBlock != null)
|
||||
if (lastBlankLine is null && paragraphBlock != null)
|
||||
{
|
||||
return BlockState.Continue;
|
||||
}
|
||||
@@ -190,7 +190,7 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
definitionItem.RemoveAt(definitionItem.Count - 1);
|
||||
}
|
||||
|
||||
list.Span.End = list.LastChild.Span.End;
|
||||
list.Span.End = list.LastChild!.Span.End;
|
||||
return BlockState.Break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
protected override void Write(HtmlRenderer renderer, DefinitionList list)
|
||||
{
|
||||
renderer.EnsureLine();
|
||||
renderer.Write("<dl").WriteAttributes(list).WriteLine(">");
|
||||
renderer.Write("<dl").WriteAttributes(list).WriteLine('>');
|
||||
foreach (var item in list)
|
||||
{
|
||||
bool hasOpendd = false;
|
||||
@@ -41,7 +41,7 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
hasOpendd = false;
|
||||
countdd = 0;
|
||||
}
|
||||
renderer.Write("<dt").WriteAttributes(definitionTerm).Write(">");
|
||||
renderer.Write("<dt").WriteAttributes(definitionTerm).Write('>');
|
||||
renderer.WriteLeafInline(definitionTerm);
|
||||
renderer.WriteLine("</dt>");
|
||||
}
|
||||
@@ -49,13 +49,13 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
{
|
||||
if (!hasOpendd)
|
||||
{
|
||||
renderer.Write("<dd").WriteAttributes(definitionItem).Write(">");
|
||||
renderer.Write("<dd").WriteAttributes(definitionItem).Write('>');
|
||||
countdd = 0;
|
||||
hasOpendd = true;
|
||||
}
|
||||
|
||||
var nextTerm = i + 1 < definitionItem.Count ? definitionItem[i + 1] : null;
|
||||
bool isSimpleParagraph = (nextTerm == null || nextTerm is DefinitionItem) && countdd == 0 &&
|
||||
bool isSimpleParagraph = (nextTerm is null || nextTerm is DefinitionItem) && countdd == 0 &&
|
||||
definitionTermOrContent is ParagraphBlock;
|
||||
|
||||
var saveImplicitParagraph = renderer.ImplicitParagraph;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Markdig.Extensions.Diagrams
|
||||
{
|
||||
if (renderer is HtmlRenderer htmlRenderer)
|
||||
{
|
||||
var codeRenderer = htmlRenderer.ObjectRenderers.FindExact<CodeBlockRenderer>();
|
||||
var codeRenderer = htmlRenderer.ObjectRenderers.FindExact<CodeBlockRenderer>()!;
|
||||
// TODO: Add other well known diagram languages
|
||||
codeRenderer.BlocksAsDiv.Add("mermaid");
|
||||
codeRenderer.BlocksAsDiv.Add("nomnoml");
|
||||
|
||||
@@ -34,6 +34,6 @@ namespace Markdig.Extensions.Emoji
|
||||
/// <summary>
|
||||
/// Gets or sets the original match string (either an emoji shortcode or a text smiley)
|
||||
/// </summary>
|
||||
public string Match { get; set; }
|
||||
public string? Match { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1745,10 +1745,10 @@ namespace Markdig.Extensions.Emoji
|
||||
/// </summary>
|
||||
public EmojiMapping(IDictionary<string, string> shortcodeToUnicode, IDictionary<string, string> smileyToShortcode)
|
||||
{
|
||||
if (shortcodeToUnicode == null)
|
||||
if (shortcodeToUnicode is null)
|
||||
ThrowHelper.ArgumentNullException(nameof(shortcodeToUnicode));
|
||||
|
||||
if (smileyToShortcode == null)
|
||||
if (smileyToShortcode is null)
|
||||
ThrowHelper.ArgumentNullException(nameof(smileyToShortcode));
|
||||
|
||||
// Build emojis and smileys CompactPrefixTree
|
||||
@@ -1778,7 +1778,7 @@ namespace Markdig.Extensions.Emoji
|
||||
if (string.IsNullOrEmpty(smiley.Key) || string.IsNullOrEmpty(smiley.Value))
|
||||
ThrowHelper.ArgumentException("The dictionaries cannot contain null or empty keys/values", nameof(smileyToShortcode));
|
||||
|
||||
if (!shortcodeToUnicode.TryGetValue(smiley.Value, out string unicode))
|
||||
if (!shortcodeToUnicode.TryGetValue(smiley.Value, out string? unicode))
|
||||
ThrowHelper.ArgumentException(string.Format("Invalid smiley target: {0} is not present in the emoji shortcodes dictionary", smiley.Value));
|
||||
|
||||
firstChars.Add(smiley.Key[0]);
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Markdig.Extensions.EmphasisExtras
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTag(EmphasisInline emphasisInline)
|
||||
private string? GetTag(EmphasisInline emphasisInline)
|
||||
{
|
||||
var c = emphasisInline.DelimiterChar;
|
||||
switch (c)
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Markdig.Extensions.Figures
|
||||
Column = column + line.Start - startPosition,
|
||||
IsOpen = false
|
||||
};
|
||||
caption.AppendLine(ref line, caption.Column, processor.LineIndex, processor.CurrentLineStartPosition);
|
||||
caption.AppendLine(ref line, caption.Column, processor.LineIndex, processor.CurrentLineStartPosition, processor.TrackTrivia);
|
||||
figure.Add(caption);
|
||||
}
|
||||
processor.NewBlocks.Push(figure);
|
||||
@@ -96,7 +96,7 @@ namespace Markdig.Extensions.Figures
|
||||
Column = column + line.Start - startPosition,
|
||||
IsOpen = false
|
||||
};
|
||||
caption.AppendLine(ref line, caption.Column, processor.LineIndex, processor.CurrentLineStartPosition);
|
||||
caption.AppendLine(ref line, caption.Column, processor.LineIndex, processor.CurrentLineStartPosition, processor.TrackTrivia);
|
||||
figure.Add(caption);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Markdig.Extensions.Figures
|
||||
protected override void Write(HtmlRenderer renderer, FigureCaption obj)
|
||||
{
|
||||
renderer.EnsureLine();
|
||||
renderer.Write("<figcaption").WriteAttributes(obj).Write(">");
|
||||
renderer.Write("<figcaption").WriteAttributes(obj).Write('>');
|
||||
renderer.WriteLeafInline(obj);
|
||||
renderer.WriteLine("</figcaption>");
|
||||
}
|
||||
|
||||
@@ -16,14 +16,13 @@ namespace Markdig.Extensions.Footnotes
|
||||
{
|
||||
public Footnote(BlockParser parser) : base(parser)
|
||||
{
|
||||
Links = new List<FootnoteLink>();
|
||||
Order = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the label used by this footnote.
|
||||
/// </summary>
|
||||
public string Label { get; set; }
|
||||
public string? Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the order of this footnote (determined by the order of the <see cref="FootnoteLink"/> in the document)
|
||||
@@ -33,7 +32,7 @@ namespace Markdig.Extensions.Footnotes
|
||||
/// <summary>
|
||||
/// Gets the links referencing this footnote.
|
||||
/// </summary>
|
||||
public List<FootnoteLink> Links { get; private set; }
|
||||
public List<FootnoteLink> Links { get; } = new ();
|
||||
|
||||
/// <summary>
|
||||
/// The label span
|
||||
|
||||
@@ -12,6 +12,11 @@ namespace Markdig.Extensions.Footnotes
|
||||
/// <seealso cref="Inline" />
|
||||
public class FootnoteLink : Inline
|
||||
{
|
||||
public FootnoteLink(Footnote footnote)
|
||||
{
|
||||
Footnote = footnote;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this instance is back link (from a footnote to the link)
|
||||
/// </summary>
|
||||
|
||||
@@ -12,6 +12,11 @@ namespace Markdig.Extensions.Footnotes
|
||||
/// <seealso cref="LinkReferenceDefinition" />
|
||||
public class FootnoteLinkReferenceDefinition : LinkReferenceDefinition
|
||||
{
|
||||
public FootnoteLinkReferenceDefinition(Footnote footnote)
|
||||
{
|
||||
Footnote = footnote;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the footnote related to this link reference definition.
|
||||
/// </summary>
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Markdig.Extensions.Footnotes
|
||||
|
||||
var saved = processor.Column;
|
||||
int start = processor.Start;
|
||||
if (!LinkHelper.TryParseLabel(ref processor.Line, false, out string label, out SourceSpan labelSpan) || !label.StartsWith("^") || processor.CurrentChar != ':')
|
||||
if (!LinkHelper.TryParseLabel(ref processor.Line, false, out string? label, out SourceSpan labelSpan) || !label.StartsWith("^") || processor.CurrentChar != ':')
|
||||
{
|
||||
processor.GoToColumn(saved);
|
||||
return BlockState.None;
|
||||
@@ -70,16 +70,15 @@ namespace Markdig.Extensions.Footnotes
|
||||
}
|
||||
footnotes.Add(footnote);
|
||||
|
||||
var linkRef = new FootnoteLinkReferenceDefinition()
|
||||
var linkRef = new FootnoteLinkReferenceDefinition(footnote)
|
||||
{
|
||||
Footnote = footnote,
|
||||
CreateLinkInline = CreateLinkToFootnote,
|
||||
Line = processor.LineIndex,
|
||||
Span = new SourceSpan(start, processor.Start - 2), // account for ]:
|
||||
LabelSpan = labelSpan,
|
||||
Label = label
|
||||
};
|
||||
processor.Document.SetLinkReferenceDefinition(footnote.Label, linkRef);
|
||||
processor.Document.SetLinkReferenceDefinition(footnote.Label, linkRef, true);
|
||||
processor.NewBlocks.Push(footnote);
|
||||
return BlockState.Continue;
|
||||
}
|
||||
@@ -130,12 +129,12 @@ namespace Markdig.Extensions.Footnotes
|
||||
/// </summary>
|
||||
/// <param name="state">The processor.</param>
|
||||
/// <param name="inline">The inline.</param>
|
||||
private void Document_ProcessInlinesEnd(InlineProcessor state, Inline inline)
|
||||
private void Document_ProcessInlinesEnd(InlineProcessor state, Inline? inline)
|
||||
{
|
||||
// Unregister
|
||||
state.Document.ProcessInlinesEnd -= Document_ProcessInlinesEnd;
|
||||
|
||||
var footnotes = ((FootnoteGroup)state.Document.GetData(DocumentKey));
|
||||
var footnotes = (FootnoteGroup)state.Document.GetData(DocumentKey)!;
|
||||
// Remove the footnotes from the document and readd them at the end
|
||||
state.Document.Remove(footnotes);
|
||||
state.Document.Add(footnotes);
|
||||
@@ -166,7 +165,7 @@ namespace Markdig.Extensions.Footnotes
|
||||
|
||||
// Insert all footnote backlinks
|
||||
var paragraphBlock = footnote.LastChild as ParagraphBlock;
|
||||
if (paragraphBlock == null)
|
||||
if (paragraphBlock is null)
|
||||
{
|
||||
paragraphBlock = new ParagraphBlock();
|
||||
footnote.Add(paragraphBlock);
|
||||
@@ -180,28 +179,27 @@ namespace Markdig.Extensions.Footnotes
|
||||
{
|
||||
linkIndex++;
|
||||
link.Index = linkIndex;
|
||||
var backLink = new FootnoteLink()
|
||||
var backLink = new FootnoteLink(footnote)
|
||||
{
|
||||
Index = linkIndex,
|
||||
IsBackLink = true,
|
||||
Footnote = footnote
|
||||
IsBackLink = true
|
||||
};
|
||||
paragraphBlock.Inline.AppendChild(backLink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Inline CreateLinkToFootnote(InlineProcessor state, LinkReferenceDefinition linkRef, Inline child)
|
||||
private static Inline CreateLinkToFootnote(InlineProcessor state, LinkReferenceDefinition linkRef, Inline? child)
|
||||
{
|
||||
var footnote = ((FootnoteLinkReferenceDefinition)linkRef).Footnote;
|
||||
if (footnote.Order < 0)
|
||||
{
|
||||
var footnotes = (FootnoteGroup)state.Document.GetData(DocumentKey);
|
||||
var footnotes = (FootnoteGroup)state.Document.GetData(DocumentKey)!;
|
||||
footnotes.CurrentOrder++;
|
||||
footnote.Order = footnotes.CurrentOrder;
|
||||
}
|
||||
|
||||
var link = new FootnoteLink() {Footnote = footnote};
|
||||
var link = new FootnoteLink(footnote);
|
||||
footnote.Links.Add(link);
|
||||
|
||||
return link;
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
var copy = line;
|
||||
copy.Start = indexOfAttributes;
|
||||
var startOfAttributes = copy.Start;
|
||||
if (GenericAttributesParser.TryParse(ref copy, out HtmlAttributes attributes))
|
||||
if (GenericAttributesParser.TryParse(ref copy, out HtmlAttributes? attributes))
|
||||
{
|
||||
var htmlAttributes = block.GetAttributes();
|
||||
attributes.CopyTo(htmlAttributes);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Renderers.Html;
|
||||
@@ -28,7 +30,7 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
var startPosition = slice.Start;
|
||||
if (TryParse(ref slice, out HtmlAttributes attributes))
|
||||
if (TryParse(ref slice, out HtmlAttributes? attributes))
|
||||
{
|
||||
var inline = processor.Inline;
|
||||
|
||||
@@ -45,16 +47,16 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
}
|
||||
}
|
||||
}
|
||||
var objectToAttach = inline == null || inline == processor.Root ? (MarkdownObject) processor.Block : inline;
|
||||
var objectToAttach = inline is null || inline == processor.Root ? (MarkdownObject)processor.Block! : inline;
|
||||
|
||||
// If the current block is a Paragraph, but only the HtmlAttributes is used,
|
||||
// Try to attach the attributes to the following block
|
||||
if (objectToAttach is ParagraphBlock paragraph &&
|
||||
paragraph.Inline.FirstChild == null &&
|
||||
processor.Inline == null &&
|
||||
paragraph.Inline!.FirstChild is null &&
|
||||
processor.Inline is null &&
|
||||
slice.IsEmptyOrWhitespace())
|
||||
{
|
||||
var parent = paragraph.Parent;
|
||||
var parent = paragraph.Parent!;
|
||||
var indexOfParagraph = parent.IndexOf(paragraph);
|
||||
if (indexOfParagraph + 1 < parent.Count)
|
||||
{
|
||||
@@ -86,7 +88,7 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
/// <param name="slice">The slice to parse.</param>
|
||||
/// <param name="attributes">The output attributes or null if not found or invalid</param>
|
||||
/// <returns><c>true</c> if parsing the HTML attributes was successful</returns>
|
||||
public static bool TryParse(ref StringSlice slice, out HtmlAttributes attributes)
|
||||
public static bool TryParse(ref StringSlice slice, [NotNullWhen(true)] out HtmlAttributes? attributes)
|
||||
{
|
||||
attributes = null;
|
||||
if (slice.PeekCharExtra(-1) == '{')
|
||||
@@ -96,9 +98,9 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
|
||||
var line = slice;
|
||||
|
||||
string id = null;
|
||||
List<string> classes = null;
|
||||
List<KeyValuePair<string, string>> properties = null;
|
||||
string? id = null;
|
||||
List<string>? classes = null;
|
||||
List<KeyValuePair<string, string?>>? properties = null;
|
||||
|
||||
bool isValid = false;
|
||||
var c = line.NextChar();
|
||||
@@ -107,7 +109,7 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
if (c == '}')
|
||||
{
|
||||
isValid = true;
|
||||
line.NextChar(); // skip }
|
||||
line.SkipChar(); // skip }
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -135,7 +137,7 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
var text = slice.Text.Substring(start, end - start + 1);
|
||||
if (isClass)
|
||||
{
|
||||
if (classes == null)
|
||||
if (classes is null)
|
||||
{
|
||||
classes = new List<string>();
|
||||
}
|
||||
@@ -175,12 +177,10 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
// Handle boolean properties that are not followed by =
|
||||
if ((hasSpace && (c == '.' || c == '#' || IsStartAttributeName(c))) || c == '}')
|
||||
{
|
||||
if (properties == null)
|
||||
{
|
||||
properties = new List<KeyValuePair<string, string>>();
|
||||
}
|
||||
properties ??= new ();
|
||||
|
||||
// Add a null value for the property
|
||||
properties.Add(new KeyValuePair<string, string>(name, null));
|
||||
properties.Add(new KeyValuePair<string, string?>(name, null));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
}
|
||||
|
||||
// Go to next char, skip any spaces
|
||||
line.NextChar();
|
||||
line.SkipChar();
|
||||
line.TrimStart();
|
||||
|
||||
int startValue = -1;
|
||||
@@ -245,11 +245,8 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
|
||||
var value = slice.Text.Substring(startValue, endValue - startValue + 1);
|
||||
|
||||
if (properties == null)
|
||||
{
|
||||
properties = new List<KeyValuePair<string, string>>();
|
||||
}
|
||||
properties.Add(new KeyValuePair<string, string>(name, value));
|
||||
properties ??= new();
|
||||
properties.Add(new KeyValuePair<string, string?>(name, value));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Markdig.Extensions.Globalization
|
||||
{
|
||||
@@ -51,7 +52,7 @@ namespace Markdig.Extensions.Globalization
|
||||
|
||||
}
|
||||
|
||||
private bool ShouldBeRightToLeft(MarkdownObject item)
|
||||
private static bool ShouldBeRightToLeft(MarkdownObject item)
|
||||
{
|
||||
if (item is IEnumerable<MarkdownObject> container)
|
||||
{
|
||||
@@ -67,7 +68,7 @@ namespace Markdig.Extensions.Globalization
|
||||
}
|
||||
else if (item is LeafBlock leaf)
|
||||
{
|
||||
return ShouldBeRightToLeft(leaf.Inline);
|
||||
return ShouldBeRightToLeft(leaf.Inline!);
|
||||
}
|
||||
else if (item is LiteralInline literal)
|
||||
{
|
||||
@@ -76,7 +77,7 @@ namespace Markdig.Extensions.Globalization
|
||||
|
||||
foreach (var paragraph in item.Descendants<ParagraphBlock>())
|
||||
{
|
||||
foreach (var inline in paragraph.Inline)
|
||||
foreach (var inline in paragraph.Inline!)
|
||||
{
|
||||
if (inline is LiteralInline literal)
|
||||
{
|
||||
@@ -88,14 +89,30 @@ namespace Markdig.Extensions.Globalization
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool StartsWithRtlCharacter(StringSlice slice)
|
||||
private static bool StartsWithRtlCharacter(StringSlice slice)
|
||||
{
|
||||
foreach (var c in CharHelper.ToUtf32(slice))
|
||||
for (int i = slice.Start; i <= slice.End; i++)
|
||||
{
|
||||
if (CharHelper.IsRightToLeft(c))
|
||||
if (slice[i] < 128)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int rune;
|
||||
if (CharHelper.IsHighSurrogate(slice[i]) && i < slice.End && CharHelper.IsLowSurrogate(slice[i + 1]))
|
||||
{
|
||||
Debug.Assert(char.IsSurrogatePair(slice[i], slice[i + 1]));
|
||||
rune = char.ConvertToUtf32(slice[i], slice[i + 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
rune = slice[i];
|
||||
}
|
||||
|
||||
if (CharHelper.IsRightToLeft(rune))
|
||||
return true;
|
||||
|
||||
else if (CharHelper.IsLeftToRight(c))
|
||||
if (CharHelper.IsLeftToRight(rune))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,8 +40,14 @@ namespace Markdig.Extensions.JiraLinks
|
||||
var startKey = slice.Start;
|
||||
var endKey = slice.Start;
|
||||
|
||||
//read as many uppercase characters as required - project key
|
||||
while (current.IsAlphaUpper())
|
||||
// the first character of the key can not be a digit.
|
||||
if (current.IsDigit())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// read as many uppercase characters or digits as required - project key
|
||||
while (current.IsAlphaUpper() || current.IsDigit())
|
||||
{
|
||||
endKey = slice.Start;
|
||||
current = slice.NextChar();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Renderers;
|
||||
@@ -16,11 +16,19 @@ namespace Markdig.Extensions.Mathematics
|
||||
protected override void Write(HtmlRenderer renderer, MathBlock obj)
|
||||
{
|
||||
renderer.EnsureLine();
|
||||
renderer.Write("<div").WriteAttributes(obj).WriteLine(">");
|
||||
renderer.WriteLine("\\[");
|
||||
renderer.WriteLeafRawLines(obj, true, true);
|
||||
renderer.Write("\\]");
|
||||
renderer.WriteLine("</div>");
|
||||
if (renderer.EnableHtmlForBlock)
|
||||
{
|
||||
renderer.Write("<div").WriteAttributes(obj).WriteLine(">");
|
||||
renderer.WriteLine("\\[");
|
||||
}
|
||||
|
||||
renderer.WriteLeafRawLines(obj, true, renderer.EnableHtmlEscape);
|
||||
|
||||
if (renderer.EnableHtmlForBlock)
|
||||
{
|
||||
renderer.Write("\\]");
|
||||
renderer.WriteLine("</div>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Renderers;
|
||||
@@ -15,9 +15,24 @@ namespace Markdig.Extensions.Mathematics
|
||||
{
|
||||
protected override void Write(HtmlRenderer renderer, MathInline obj)
|
||||
{
|
||||
renderer.Write("<span").WriteAttributes(obj).Write(">\\(");
|
||||
renderer.WriteEscape(ref obj.Content);
|
||||
renderer.Write("\\)</span>");
|
||||
if (renderer.EnableHtmlForInline)
|
||||
{
|
||||
renderer.Write("<span").WriteAttributes(obj).Write(">\\(");
|
||||
}
|
||||
|
||||
if (renderer.EnableHtmlEscape)
|
||||
{
|
||||
renderer.WriteEscape(ref obj.Content);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.Write(ref obj.Content);
|
||||
}
|
||||
|
||||
if (renderer.EnableHtmlForInline)
|
||||
{
|
||||
renderer.Write("\\)</span>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
using Markdig.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Markdig.Extensions.MediaLinks
|
||||
@@ -13,12 +14,23 @@ namespace Markdig.Extensions.MediaLinks
|
||||
{
|
||||
private sealed class DelegateProvider : IHostProvider
|
||||
{
|
||||
public string HostPrefix { get; set; }
|
||||
public Func<Uri, string> Delegate { get; set; }
|
||||
public bool AllowFullScreen { get; set; } = true;
|
||||
public string Class { get; set; }
|
||||
public DelegateProvider(string hostPrefix, Func<Uri, string?> handler, bool allowFullscreen = true, string? className = null)
|
||||
{
|
||||
HostPrefix = hostPrefix;
|
||||
Delegate = handler;
|
||||
AllowFullScreen = allowFullscreen;
|
||||
Class = className;
|
||||
}
|
||||
|
||||
public bool TryHandle(Uri mediaUri, bool isSchemaRelative, out string iframeUrl)
|
||||
public string HostPrefix { get; }
|
||||
|
||||
public Func<Uri, string?> Delegate { get; }
|
||||
|
||||
public bool AllowFullScreen { get; }
|
||||
|
||||
public string? Class { get; }
|
||||
|
||||
public bool TryHandle(Uri mediaUri, bool isSchemaRelative, [NotNullWhen(true)] out string? iframeUrl)
|
||||
{
|
||||
if (!mediaUri.Host.StartsWith(HostPrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@@ -38,14 +50,14 @@ namespace Markdig.Extensions.MediaLinks
|
||||
/// <param name="allowFullScreen">Should the generated iframe has allowfullscreen attribute.</param>
|
||||
/// <param name="iframeClass">"class" attribute of generated iframe.</param>
|
||||
/// <returns>A <see cref="IHostProvider"/> with delegate handler.</returns>
|
||||
public static IHostProvider Create(string hostPrefix, Func<Uri, string> handler, bool allowFullScreen = true, string iframeClass = null)
|
||||
public static IHostProvider Create(string hostPrefix, Func<Uri, string?> handler, bool allowFullScreen = true, string? iframeClass = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hostPrefix))
|
||||
ThrowHelper.ArgumentException("hostPrefix is null or empty.", nameof(hostPrefix));
|
||||
if (handler == null)
|
||||
if (handler is null)
|
||||
ThrowHelper.ArgumentNullException(nameof(handler));
|
||||
|
||||
return new DelegateProvider { HostPrefix = hostPrefix, Delegate = handler, AllowFullScreen = allowFullScreen, Class = iframeClass };
|
||||
return new DelegateProvider(hostPrefix, handler, allowFullScreen, iframeClass);
|
||||
}
|
||||
|
||||
internal static Dictionary<string, IHostProvider> KnownHosts { get; }
|
||||
@@ -67,7 +79,7 @@ namespace Markdig.Extensions.MediaLinks
|
||||
return query.Split(SplitAnd, StringSplitOptions.RemoveEmptyEntries);
|
||||
}
|
||||
|
||||
private static string YouTube(Uri uri)
|
||||
private static string? YouTube(Uri uri)
|
||||
{
|
||||
string uriPath = uri.AbsolutePath;
|
||||
if (string.Equals(uriPath, "/embed", StringComparison.OrdinalIgnoreCase) || uriPath.StartsWith("/embed/", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -80,20 +92,20 @@ namespace Markdig.Extensions.MediaLinks
|
||||
}
|
||||
var queryParams = SplitQuery(uri);
|
||||
return BuildYouTubeIframeUrl(
|
||||
queryParams.FirstOrDefault(p => p.StartsWith("v="))?.Substring(2),
|
||||
queryParams.FirstOrDefault(p => p.StartsWith("t="))?.Substring(2)
|
||||
queryParams.FirstOrDefault(p => p.StartsWith("v=", StringComparison.Ordinal))?.Substring(2),
|
||||
queryParams.FirstOrDefault(p => p.StartsWith("t=", StringComparison.Ordinal))?.Substring(2)
|
||||
);
|
||||
}
|
||||
|
||||
private static string YouTubeShortened(Uri uri)
|
||||
private static string? YouTubeShortened(Uri uri)
|
||||
{
|
||||
return BuildYouTubeIframeUrl(
|
||||
uri.AbsolutePath.Substring(1),
|
||||
SplitQuery(uri).FirstOrDefault(p => p.StartsWith("t="))?.Substring(2)
|
||||
SplitQuery(uri).FirstOrDefault(p => p.StartsWith("t=", StringComparison.Ordinal))?.Substring(2)
|
||||
);
|
||||
}
|
||||
|
||||
private static string BuildYouTubeIframeUrl(string videoId, string startTime)
|
||||
private static string? BuildYouTubeIframeUrl(string? videoId, string? startTime)
|
||||
{
|
||||
if (string.IsNullOrEmpty(videoId))
|
||||
{
|
||||
@@ -103,31 +115,27 @@ namespace Markdig.Extensions.MediaLinks
|
||||
return string.IsNullOrEmpty(startTime) ? url : $"{url}?start={startTime}";
|
||||
}
|
||||
|
||||
private static string Vimeo(Uri uri)
|
||||
private static string? Vimeo(Uri uri)
|
||||
{
|
||||
var items = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped).Split('/');
|
||||
return items.Length > 0 ? $"https://player.vimeo.com/video/{ items[items.Length - 1] }" : null;
|
||||
}
|
||||
|
||||
private static string Odnoklassniki(Uri uri)
|
||||
private static string? Odnoklassniki(Uri uri)
|
||||
{
|
||||
var items = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped).Split('/');
|
||||
return items.Length > 0 ? $"https://ok.ru/videoembed/{ items[items.Length - 1] }" : null;
|
||||
}
|
||||
|
||||
private static string Yandex(Uri uri)
|
||||
private static string? Yandex(Uri uri)
|
||||
{
|
||||
var items = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped).Split('/');
|
||||
var albumKeyword
|
||||
= items.Skip(0).FirstOrDefault();
|
||||
var albumId
|
||||
= items.Skip(1).FirstOrDefault();
|
||||
var trackKeyword
|
||||
= items.Skip(2).FirstOrDefault();
|
||||
var trackId
|
||||
= items.Skip(3).FirstOrDefault();
|
||||
string[] items = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped).Split('/');
|
||||
string? albumKeyword = items.Skip(0).FirstOrDefault();
|
||||
string? albumId = items.Skip(1).FirstOrDefault();
|
||||
string? trackKeyword = items.Skip(2).FirstOrDefault();
|
||||
string? trackId = items.Skip(3).FirstOrDefault();
|
||||
|
||||
if (albumKeyword != "album" || albumId == null || trackKeyword != "track" || trackId == null)
|
||||
if (albumKeyword != "album" || albumId is null || trackKeyword != "track" || trackId is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Markdig.Extensions.MediaLinks
|
||||
{
|
||||
@@ -14,7 +15,7 @@ namespace Markdig.Extensions.MediaLinks
|
||||
/// <summary>
|
||||
/// "class" attribute of generated iframe.
|
||||
/// </summary>
|
||||
string Class { get; }
|
||||
string? Class { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Generate url for iframe.
|
||||
@@ -23,7 +24,7 @@ namespace Markdig.Extensions.MediaLinks
|
||||
/// <param name="isSchemaRelative"><see langword="true"/> if <paramref name="mediaUri"/> is a schema relative uri, i.e. uri starts with "//".</param>
|
||||
/// <param name="iframeUrl">Generated url for iframe.</param>
|
||||
/// <seealso href="https://tools.ietf.org/html/rfc3986#section-4.2"/>
|
||||
bool TryHandle(Uri mediaUri, bool isSchemaRelative, out string iframeUrl);
|
||||
bool TryHandle(Uri mediaUri, bool isSchemaRelative, [NotNullWhen(true)] out string? iframeUrl);
|
||||
|
||||
/// <summary>
|
||||
/// Should the generated iframe has allowfullscreen attribute.
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Markdig.Extensions.MediaLinks
|
||||
{
|
||||
}
|
||||
|
||||
public MediaLinkExtension(MediaOptions options)
|
||||
public MediaLinkExtension(MediaOptions? options)
|
||||
{
|
||||
Options = options ?? new MediaOptions();
|
||||
}
|
||||
@@ -46,18 +46,18 @@ namespace Markdig.Extensions.MediaLinks
|
||||
|
||||
private bool TryLinkInlineRenderer(HtmlRenderer renderer, LinkInline linkInline)
|
||||
{
|
||||
if (!linkInline.IsImage || linkInline.Url == null)
|
||||
if (!linkInline.IsImage || linkInline.Url is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isSchemaRelative = false;
|
||||
// Only process absolute Uri
|
||||
if (!Uri.TryCreate(linkInline.Url, UriKind.RelativeOrAbsolute, out Uri uri) || !uri.IsAbsoluteUri)
|
||||
if (!Uri.TryCreate(linkInline.Url, UriKind.RelativeOrAbsolute, out Uri? uri) || !uri.IsAbsoluteUri)
|
||||
{
|
||||
// see https://tools.ietf.org/html/rfc3986#section-4.2
|
||||
// since relative uri doesn't support many properties, "http" is used as a placeholder here.
|
||||
if (linkInline.Url.StartsWith("//") && Uri.TryCreate("http:" + linkInline.Url, UriKind.Absolute, out uri))
|
||||
if (linkInline.Url.StartsWith("//", StringComparison.Ordinal) && Uri.TryCreate("http:" + linkInline.Url, UriKind.Absolute, out uri))
|
||||
{
|
||||
isSchemaRelative = true;
|
||||
}
|
||||
@@ -98,10 +98,10 @@ namespace Markdig.Extensions.MediaLinks
|
||||
// Otherwise try to detect if we have an audio/video from the file extension
|
||||
var lastDot = path.LastIndexOf('.');
|
||||
if (lastDot >= 0 &&
|
||||
Options.ExtensionToMimeType.TryGetValue(path.Substring(lastDot), out string mimeType))
|
||||
Options.ExtensionToMimeType.TryGetValue(path.Substring(lastDot), out string? mimeType))
|
||||
{
|
||||
var htmlAttributes = GetHtmlAttributes(linkInline);
|
||||
var isAudio = mimeType.StartsWith("audio");
|
||||
var isAudio = mimeType.StartsWith("audio", StringComparison.Ordinal);
|
||||
var tagType = isAudio ? "audio" : "video";
|
||||
|
||||
renderer.Write($"<{tagType}");
|
||||
@@ -126,9 +126,8 @@ namespace Markdig.Extensions.MediaLinks
|
||||
|
||||
private bool TryRenderIframeFromKnownProviders(Uri uri, bool isSchemaRelative, HtmlRenderer renderer, LinkInline linkInline)
|
||||
{
|
||||
|
||||
IHostProvider foundProvider = null;
|
||||
string iframeUrl = null;
|
||||
IHostProvider? foundProvider = null;
|
||||
string? iframeUrl = null;
|
||||
foreach (var provider in Options.Hosts)
|
||||
{
|
||||
if (!provider.TryHandle(uri, isSchemaRelative, out iframeUrl))
|
||||
@@ -137,7 +136,7 @@ namespace Markdig.Extensions.MediaLinks
|
||||
break;
|
||||
}
|
||||
|
||||
if (foundProvider == null)
|
||||
if (foundProvider is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -145,7 +144,7 @@ namespace Markdig.Extensions.MediaLinks
|
||||
var htmlAttributes = GetHtmlAttributes(linkInline);
|
||||
renderer.Write("<iframe src=\"");
|
||||
renderer.WriteEscapeUrl(iframeUrl);
|
||||
renderer.Write("\"");
|
||||
renderer.Write('"');
|
||||
|
||||
if (!string.IsNullOrEmpty(Options.Width))
|
||||
htmlAttributes.AddPropertyIfNotExist("width", Options.Width);
|
||||
@@ -156,8 +155,8 @@ namespace Markdig.Extensions.MediaLinks
|
||||
if (!string.IsNullOrEmpty(Options.Class))
|
||||
htmlAttributes.AddClass(Options.Class);
|
||||
|
||||
if (!string.IsNullOrEmpty(foundProvider.Class))
|
||||
htmlAttributes.AddClass(foundProvider.Class);
|
||||
if (foundProvider.Class is { Length: > 0 } className)
|
||||
htmlAttributes.AddClass(className);
|
||||
|
||||
htmlAttributes.AddPropertyIfNotExist("frameborder", "0");
|
||||
if (foundProvider.AllowFullScreen)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Markdig.Extensions.PragmaLines
|
||||
{
|
||||
var attribute = block.GetAttributes();
|
||||
var pragmaId = GetPragmaId(block);
|
||||
if ( attribute.Id == null)
|
||||
if ( attribute.Id is null)
|
||||
{
|
||||
attribute.Id = pragmaId;
|
||||
}
|
||||
@@ -49,8 +49,7 @@ namespace Markdig.Extensions.PragmaLines
|
||||
var tag = $"<a id=\"{pragmaId}\"></a>";
|
||||
if (heading?.Inline?.FirstChild != null)
|
||||
{
|
||||
heading.Inline.FirstChild.InsertBefore(new HtmlInline() { Tag = tag });
|
||||
|
||||
heading.Inline.FirstChild.InsertBefore(new HtmlInline(tag));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html.Inlines;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.ReferralLinks
|
||||
{
|
||||
@@ -15,7 +14,7 @@ namespace Markdig.Extensions.ReferralLinks
|
||||
{
|
||||
public ReferralLinksExtension(string[] rels)
|
||||
{
|
||||
Rels = rels?.ToList();
|
||||
Rels = rels?.ToList() ?? throw new ArgumentNullException(nameof(rels));
|
||||
}
|
||||
|
||||
public List<string> Rels { get; }
|
||||
@@ -26,8 +25,7 @@ namespace Markdig.Extensions.ReferralLinks
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
string relString = Rels == null? null :
|
||||
string.Join(" ", Rels.Where(r => !string.IsNullOrEmpty(r)));
|
||||
string relString = string.Join(" ", Rels.Where(r => !string.IsNullOrEmpty(r)));
|
||||
|
||||
var linkRenderer = renderer.ObjectRenderers.Find<LinkInlineRenderer>();
|
||||
if (linkRenderer != null)
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Markdig.Extensions.SelfPipeline
|
||||
/// <param name="tag">The matching start tag.</param>
|
||||
/// <param name="defaultExtensions">The default extensions.</param>
|
||||
/// <exception cref="ArgumentException">Tag cannot contain angle brackets</exception>
|
||||
public SelfPipelineExtension(string tag = null, string defaultExtensions = null)
|
||||
public SelfPipelineExtension(string? tag = null, string? defaultExtensions = null)
|
||||
{
|
||||
tag = tag?.Trim();
|
||||
tag = string.IsNullOrEmpty(tag) ? DefaultTag : tag;
|
||||
@@ -46,7 +46,7 @@ namespace Markdig.Extensions.SelfPipeline
|
||||
/// <summary>
|
||||
/// Gets the default pipeline to configure if no tag was found in the input text. Default is <c>null</c> (core pipeline).
|
||||
/// </summary>
|
||||
public string DefaultExtensions { get; }
|
||||
public string? DefaultExtensions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the self pipeline hint tag start that will be matched.
|
||||
@@ -75,10 +75,10 @@ namespace Markdig.Extensions.SelfPipeline
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public MarkdownPipeline CreatePipelineFromInput(string inputText)
|
||||
{
|
||||
if (inputText == null) ThrowHelper.ArgumentNullException(nameof(inputText));
|
||||
if (inputText is null) ThrowHelper.ArgumentNullException(nameof(inputText));
|
||||
|
||||
var builder = new MarkdownPipelineBuilder();
|
||||
string defaultConfig = DefaultExtensions;
|
||||
string? defaultConfig = DefaultExtensions;
|
||||
var indexOfSelfPipeline = inputText.IndexOf(SelfPipelineHintTagStart, StringComparison.OrdinalIgnoreCase);
|
||||
if (indexOfSelfPipeline >= 0)
|
||||
{
|
||||
@@ -90,7 +90,7 @@ namespace Markdig.Extensions.SelfPipeline
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(defaultConfig))
|
||||
if (defaultConfig is { Length: > 0 })
|
||||
{
|
||||
builder.Configure(defaultConfig);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
@@ -22,14 +23,14 @@ namespace Markdig.Extensions.SmartyPants
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public HtmlSmartyPantRenderer(SmartyPantOptions options)
|
||||
public HtmlSmartyPantRenderer(SmartyPantOptions? options)
|
||||
{
|
||||
this.options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
protected override void Write(HtmlRenderer renderer, SmartyPant obj)
|
||||
{
|
||||
if (!options.Mapping.TryGetValue(obj.Type, out string text))
|
||||
if (!options.Mapping.TryGetValue(obj.Type, out string? text))
|
||||
{
|
||||
DefaultOptions.Mapping.TryGetValue(obj.Type, out text);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Markdig.Extensions.SmartyPants
|
||||
/// Initializes a new instance of the <see cref="SmartyPantsExtension"/> class.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
public SmartyPantsExtension(SmartyPantOptions options)
|
||||
public SmartyPantsExtension(SmartyPantOptions? options)
|
||||
{
|
||||
Options = options ?? new SmartyPantOptions();
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Markdig.Extensions.SmartyPants
|
||||
type = SmartyPantType.Quote; // We will resolve them at the end of parsing all inlines
|
||||
if (slice.PeekChar() == '\'')
|
||||
{
|
||||
slice.NextChar();
|
||||
slice.SkipChar();
|
||||
type = SmartyPantType.DoubleQuote; // We will resolve them at the end of parsing all inlines
|
||||
}
|
||||
break;
|
||||
@@ -171,9 +171,9 @@ namespace Markdig.Extensions.SmartyPants
|
||||
var quotePants = GetOrCreateState(processor);
|
||||
|
||||
// Register only if we don't have yet any quotes
|
||||
if (quotePants.Count == 0)
|
||||
if (quotePants.Count is 0)
|
||||
{
|
||||
processor.Block.ProcessInlinesEnd += BlockOnProcessInlinesEnd;
|
||||
processor.Block!.ProcessInlinesEnd += BlockOnProcessInlinesEnd;
|
||||
}
|
||||
quotePants.Add(pant);
|
||||
}
|
||||
@@ -203,13 +203,13 @@ namespace Markdig.Extensions.SmartyPants
|
||||
}
|
||||
}
|
||||
|
||||
private void BlockOnProcessInlinesEnd(InlineProcessor processor, Inline inline)
|
||||
private void BlockOnProcessInlinesEnd(InlineProcessor processor, Inline? inline)
|
||||
{
|
||||
processor.Block.ProcessInlinesEnd -= BlockOnProcessInlinesEnd;
|
||||
processor.Block!.ProcessInlinesEnd -= BlockOnProcessInlinesEnd;
|
||||
|
||||
var pants = (ListSmartyPants) processor.ParserStates[Index];
|
||||
|
||||
Stack<Opener> openers = new Stack<Opener>(4);
|
||||
var openers = new Stack<Opener>(4);
|
||||
|
||||
for (int i = 0; i < pants.Count; i++)
|
||||
{
|
||||
@@ -219,17 +219,17 @@ namespace Markdig.Extensions.SmartyPants
|
||||
int type;
|
||||
bool isLeft;
|
||||
|
||||
if (quoteType == SmartyPantType.LeftQuote || quoteType == SmartyPantType.RightQuote)
|
||||
if (quoteType is SmartyPantType.LeftQuote or SmartyPantType.RightQuote)
|
||||
{
|
||||
type = 0;
|
||||
isLeft = quoteType == SmartyPantType.LeftQuote;
|
||||
}
|
||||
else if (quoteType == SmartyPantType.LeftDoubleQuote || quoteType == SmartyPantType.RightDoubleQuote)
|
||||
else if (quoteType is SmartyPantType.LeftDoubleQuote or SmartyPantType.RightDoubleQuote)
|
||||
{
|
||||
type = 1;
|
||||
isLeft = quoteType == SmartyPantType.LeftDoubleQuote;
|
||||
}
|
||||
else if (quoteType == SmartyPantType.LeftAngleQuote || quoteType == SmartyPantType.RightAngleQuote)
|
||||
else if (quoteType is SmartyPantType.LeftAngleQuote or SmartyPantType.RightAngleQuote)
|
||||
{
|
||||
type = 2;
|
||||
isLeft = quoteType == SmartyPantType.LeftAngleQuote;
|
||||
@@ -280,7 +280,11 @@ namespace Markdig.Extensions.SmartyPants
|
||||
pants.Clear();
|
||||
}
|
||||
|
||||
bool IPostInlineProcessor.PostProcess(InlineProcessor state, Inline root, Inline lastChild, int postInlineProcessorIndex,
|
||||
bool IPostInlineProcessor.PostProcess(
|
||||
InlineProcessor state,
|
||||
Inline? root,
|
||||
Inline? lastChild,
|
||||
int postInlineProcessorIndex,
|
||||
bool isFinalProcessing)
|
||||
{
|
||||
// Don't try to process anything if there are no dash
|
||||
@@ -339,9 +343,9 @@ namespace Markdig.Extensions.SmartyPants
|
||||
next = postLiteral;
|
||||
}
|
||||
}
|
||||
else if (child is ContainerInline)
|
||||
else if (child is ContainerInline childContainer)
|
||||
{
|
||||
pendingContainers.Push(((ContainerInline)child).FirstChild);
|
||||
pendingContainers.Push(childContainer.FirstChild!);
|
||||
}
|
||||
|
||||
child = next;
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
|
||||
var line = processor.Line;
|
||||
GridTableState tableState = null;
|
||||
GridTableState? tableState = null;
|
||||
|
||||
// Match the first row that should be of the minimal form: +---------------
|
||||
var c = line.CurrentChar;
|
||||
@@ -33,7 +33,7 @@ namespace Markdig.Extensions.Tables
|
||||
while (c == '+')
|
||||
{
|
||||
var columnStart = line.Start;
|
||||
line.NextChar();
|
||||
line.SkipChar();
|
||||
line.TrimStart();
|
||||
|
||||
// if we have reached the end of the line, exit
|
||||
@@ -49,13 +49,13 @@ namespace Markdig.Extensions.Tables
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
tableState ??= new GridTableState { Start = processor.Start, ExpectRow = true };
|
||||
tableState ??= new GridTableState(start: processor.Start, expectRow: true);
|
||||
tableState.AddColumn(columnStart - lineStart, line.Start - lineStart, columnAlign);
|
||||
|
||||
c = line.CurrentChar;
|
||||
}
|
||||
|
||||
if (c != 0 || tableState == null)
|
||||
if (c != 0 || tableState is null)
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
@@ -66,7 +66,7 @@ namespace Markdig.Extensions.Tables
|
||||
|
||||
// Calculate the total width of all columns
|
||||
int totalWidth = 0;
|
||||
foreach (var columnSlice in tableState.ColumnSlices)
|
||||
foreach (var columnSlice in tableState.ColumnSlices!)
|
||||
{
|
||||
totalWidth += columnSlice.End - columnSlice.Start - 1;
|
||||
}
|
||||
@@ -91,7 +91,7 @@ namespace Markdig.Extensions.Tables
|
||||
public override BlockState TryContinue(BlockProcessor processor, Block block)
|
||||
{
|
||||
var gridTable = (Table)block;
|
||||
var tableState = (GridTableState)block.GetData(typeof(GridTableState));
|
||||
var tableState = (GridTableState)block.GetData(typeof(GridTableState))!;
|
||||
tableState.AddLine(ref processor.Line);
|
||||
if (processor.CurrentChar == '+')
|
||||
{
|
||||
@@ -113,7 +113,7 @@ namespace Markdig.Extensions.Tables
|
||||
|
||||
private BlockState HandleNewRow(BlockProcessor processor, GridTableState tableState, Table gridTable)
|
||||
{
|
||||
var columns = tableState.ColumnSlices;
|
||||
var columns = tableState.ColumnSlices!;
|
||||
SetRowSpanState(columns, processor.Line, out bool isHeaderRow, out bool hasRowSpan);
|
||||
SetColumnSpanState(columns, processor.Line);
|
||||
TerminateCurrentRow(processor, tableState, gridTable, false);
|
||||
@@ -161,44 +161,43 @@ namespace Markdig.Extensions.Tables
|
||||
|
||||
private static bool IsRowSeperator(StringSlice slice)
|
||||
{
|
||||
while (slice.Length > 0)
|
||||
char c = slice.CurrentChar;
|
||||
do
|
||||
{
|
||||
if (slice.CurrentChar != '-' && slice.CurrentChar != '=' && slice.CurrentChar != ':')
|
||||
if (c != '-' && c != '=' && c != ':')
|
||||
{
|
||||
return false;
|
||||
return c == '\0';
|
||||
}
|
||||
slice.NextChar();
|
||||
c = slice.NextChar();
|
||||
}
|
||||
return true;
|
||||
while (true);
|
||||
}
|
||||
|
||||
private static void TerminateCurrentRow(BlockProcessor processor, GridTableState tableState, Table gridTable, bool isLastRow)
|
||||
{
|
||||
var columns = tableState.ColumnSlices;
|
||||
TableRow currentRow = null;
|
||||
for (int i = 0; i < columns.Count; i++)
|
||||
TableRow? currentRow = null;
|
||||
for (int i = 0; i < columns!.Count; i++)
|
||||
{
|
||||
var columnSlice = columns[i];
|
||||
if (columnSlice.CurrentCell != null)
|
||||
{
|
||||
if (currentRow == null)
|
||||
{
|
||||
currentRow = new TableRow();
|
||||
}
|
||||
currentRow ??= new TableRow();
|
||||
|
||||
// If this cell does not already belong to a row
|
||||
if (columnSlice.CurrentCell.Parent == null)
|
||||
if (columnSlice.CurrentCell.Parent is null)
|
||||
{
|
||||
currentRow.Add(columnSlice.CurrentCell);
|
||||
}
|
||||
// If the cell is not going to span through to the next row
|
||||
if (columnSlice.CurrentCell.AllowClose)
|
||||
{
|
||||
columnSlice.BlockProcessor.Close(columnSlice.CurrentCell);
|
||||
columnSlice.BlockProcessor!.Close(columnSlice.CurrentCell);
|
||||
}
|
||||
}
|
||||
|
||||
// Renew the block parser processor (or reset it for the last row)
|
||||
if (columnSlice.BlockProcessor != null && (columnSlice.CurrentCell == null || columnSlice.CurrentCell.AllowClose))
|
||||
if (columnSlice.BlockProcessor != null && (columnSlice.CurrentCell is null || columnSlice.CurrentCell.AllowClose))
|
||||
{
|
||||
columnSlice.BlockProcessor.ReleaseChild();
|
||||
columnSlice.BlockProcessor = isLastRow ? null : processor.CreateChild();
|
||||
@@ -214,7 +213,7 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
}
|
||||
|
||||
if (currentRow != null && currentRow.Count > 0)
|
||||
if (currentRow is { Count: > 0 })
|
||||
{
|
||||
gridTable.Add(currentRow);
|
||||
}
|
||||
@@ -223,7 +222,7 @@ namespace Markdig.Extensions.Tables
|
||||
private BlockState HandleContents(BlockProcessor processor, GridTableState tableState, Table gridTable)
|
||||
{
|
||||
var isRowLine = processor.CurrentChar == '+';
|
||||
var columns = tableState.ColumnSlices;
|
||||
var columns = tableState.ColumnSlices!;
|
||||
var line = processor.Line;
|
||||
SetColumnSpanState(columns, line);
|
||||
if (!isRowLine && !CanContinueRow(columns))
|
||||
@@ -267,7 +266,7 @@ namespace Markdig.Extensions.Tables
|
||||
|
||||
if (!isRowLine || !IsRowSeperator(sliceForCell))
|
||||
{
|
||||
if (columnSlice.CurrentCell == null)
|
||||
if (columnSlice.CurrentCell is null)
|
||||
{
|
||||
columnSlice.CurrentCell = new TableCell(this)
|
||||
{
|
||||
@@ -275,7 +274,7 @@ namespace Markdig.Extensions.Tables
|
||||
ColumnIndex = i
|
||||
};
|
||||
|
||||
if (columnSlice.BlockProcessor == null)
|
||||
if (columnSlice.BlockProcessor is null)
|
||||
{
|
||||
columnSlice.BlockProcessor = processor.CreateChild();
|
||||
}
|
||||
@@ -284,7 +283,7 @@ namespace Markdig.Extensions.Tables
|
||||
columnSlice.BlockProcessor.Open(columnSlice.CurrentCell);
|
||||
}
|
||||
// Process the content of the cell
|
||||
columnSlice.BlockProcessor.LineIndex = processor.LineIndex;
|
||||
columnSlice.BlockProcessor!.LineIndex = processor.LineIndex;
|
||||
columnSlice.BlockProcessor.ProcessLine(sliceForCell);
|
||||
}
|
||||
|
||||
@@ -335,7 +334,7 @@ namespace Markdig.Extensions.Tables
|
||||
{
|
||||
var parser = processor.Parsers.FindExact<ParagraphBlockParser>();
|
||||
// Discard the grid table
|
||||
var parent = gridTable.Parent;
|
||||
var parent = gridTable.Parent!;
|
||||
processor.Discard(gridTable);
|
||||
var paragraphBlock = new ParagraphBlock(parser)
|
||||
{
|
||||
@@ -351,7 +350,7 @@ namespace Markdig.Extensions.Tables
|
||||
var gridTable = block as Table;
|
||||
if (gridTable != null)
|
||||
{
|
||||
var tableState = (GridTableState)block.GetData(typeof(GridTableState));
|
||||
var tableState = (GridTableState)block.GetData(typeof(GridTableState))!;
|
||||
TerminateCurrentRow(processor, tableState, gridTable, true);
|
||||
if (!gridTable.IsValid())
|
||||
{
|
||||
|
||||
@@ -13,63 +13,65 @@ namespace Markdig.Extensions.Tables
|
||||
/// </summary>
|
||||
internal sealed class GridTableState
|
||||
{
|
||||
public int Start { get; set; }
|
||||
public GridTableState(int start, bool expectRow)
|
||||
{
|
||||
Start = start;
|
||||
ExpectRow = expectRow;
|
||||
}
|
||||
|
||||
public int Start { get; }
|
||||
|
||||
public StringLineGroup Lines;
|
||||
|
||||
public List<ColumnSlice> ColumnSlices { get; private set; }
|
||||
public List<ColumnSlice>? ColumnSlices { get; private set; }
|
||||
|
||||
public bool ExpectRow { get; set; }
|
||||
public bool ExpectRow { get; }
|
||||
|
||||
public int StartRowGroup { get; set; }
|
||||
|
||||
public void AddLine(ref StringSlice line)
|
||||
{
|
||||
if (Lines.Lines == null)
|
||||
if (Lines.Lines is null)
|
||||
{
|
||||
Lines = new StringLineGroup(4);
|
||||
}
|
||||
|
||||
Lines.Add(line);
|
||||
}
|
||||
|
||||
public void AddColumn(int start, int end, TableColumnAlign? align)
|
||||
{
|
||||
if (ColumnSlices == null)
|
||||
{
|
||||
ColumnSlices = new List<ColumnSlice>();
|
||||
}
|
||||
|
||||
ColumnSlices.Add(new ColumnSlice()
|
||||
{
|
||||
Start = start,
|
||||
End = end,
|
||||
Align = align,
|
||||
});
|
||||
ColumnSlices ??= new List<ColumnSlice>();
|
||||
|
||||
ColumnSlices.Add(new ColumnSlice(start, end, align));
|
||||
}
|
||||
|
||||
public class ColumnSlice
|
||||
public sealed class ColumnSlice
|
||||
{
|
||||
public ColumnSlice()
|
||||
public ColumnSlice(int start, int end, TableColumnAlign? align)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
Align = align;
|
||||
CurrentColumnSpan = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index position of this column (after the |)
|
||||
/// </summary>
|
||||
public int Start { get; set; }
|
||||
public int Start { get; }
|
||||
|
||||
public int End { get; set; }
|
||||
public int End { get; }
|
||||
|
||||
public TableColumnAlign? Align { get; set; }
|
||||
public TableColumnAlign? Align { get; }
|
||||
|
||||
public int CurrentColumnSpan { get; set; }
|
||||
|
||||
public int PreviousColumnSpan { get; set; }
|
||||
|
||||
public BlockProcessor BlockProcessor { get; set; }
|
||||
public BlockProcessor? BlockProcessor { get; set; }
|
||||
|
||||
public TableCell CurrentCell { get; set; }
|
||||
public TableCell? CurrentCell { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace Markdig.Extensions.Tables
|
||||
protected override void Write(HtmlRenderer renderer, Table table)
|
||||
{
|
||||
renderer.EnsureLine();
|
||||
renderer.Write("<table").WriteAttributes(table).WriteLine(">");
|
||||
renderer.Write("<table").WriteAttributes(table).WriteLine('>');
|
||||
|
||||
bool hasBody = false;
|
||||
bool hasAlreadyHeader = false;
|
||||
@@ -69,7 +69,7 @@ namespace Markdig.Extensions.Tables
|
||||
renderer.WriteLine("<tbody>");
|
||||
hasBody = true;
|
||||
}
|
||||
renderer.Write("<tr").WriteAttributes(row).WriteLine(">");
|
||||
renderer.Write("<tr").WriteAttributes(row).WriteLine('>');
|
||||
for (int i = 0; i < row.Count; i++)
|
||||
{
|
||||
var cellObj = row[i];
|
||||
@@ -109,7 +109,7 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
}
|
||||
renderer.WriteAttributes(cell);
|
||||
renderer.Write(">");
|
||||
renderer.Write('>');
|
||||
|
||||
var previousImplicitParagraph = renderer.ImplicitParagraph;
|
||||
if (cell.Count == 1)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Markdig.Extensions.Tables
|
||||
{
|
||||
// Only if we have already a paragraph
|
||||
var paragraph = processor.CurrentBlock as ParagraphBlock;
|
||||
if (processor.IsCodeIndent || paragraph == null)
|
||||
if (processor.IsCodeIndent || paragraph is null)
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ namespace Markdig.Extensions.Tables
|
||||
if (countPipe > 0)
|
||||
{
|
||||
// Mark the paragraph as open (important, otherwise we would have an infinite loop)
|
||||
paragraph.AppendLine(ref processor.Line, processor.Column, processor.LineIndex, processor.Line.Start);
|
||||
paragraph.AppendLine(ref processor.Line, processor.Column, processor.LineIndex, processor.Line.Start, processor.TrackTrivia);
|
||||
paragraph.IsOpen = true;
|
||||
return BlockState.BreakDiscard;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Markdig.Extensions.Tables
|
||||
/// Initializes a new instance of the <see cref="PipeTableExtension"/> class.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
public PipeTableExtension(PipeTableOptions options = null)
|
||||
public PipeTableExtension(PipeTableOptions? options = null)
|
||||
{
|
||||
Options = options ?? new PipeTableOptions();
|
||||
}
|
||||
@@ -38,7 +38,7 @@ namespace Markdig.Extensions.Tables
|
||||
var lineBreakParser = pipeline.InlineParsers.FindExact<LineBreakInlineParser>();
|
||||
if (!pipeline.InlineParsers.Contains<PipeTableParser>())
|
||||
{
|
||||
pipeline.InlineParsers.InsertBefore<EmphasisInlineParser>(new PipeTableParser(lineBreakParser, Options));
|
||||
pipeline.InlineParsers.InsertBefore<EmphasisInlineParser>(new PipeTableParser(lineBreakParser!, Options));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,10 @@ namespace Markdig.Extensions.Tables
|
||||
/// </summary>
|
||||
/// <param name="lineBreakParser">The linebreak parser to use</param>
|
||||
/// <param name="options">The options.</param>
|
||||
public PipeTableParser(LineBreakInlineParser lineBreakParser, PipeTableOptions options = null)
|
||||
public PipeTableParser(LineBreakInlineParser lineBreakParser, PipeTableOptions? options = null)
|
||||
{
|
||||
this.lineBreakParser = lineBreakParser ?? throw new ArgumentNullException(nameof(lineBreakParser));
|
||||
OpeningCharacters = new[] { '|', '\n' };
|
||||
OpeningCharacters = new[] { '|', '\n', '\r' };
|
||||
Options = options ?? new PipeTableOptions();
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Markdig.Extensions.Tables
|
||||
var position = processor.GetSourcePosition(slice.Start, out int globalLineIndex, out int column);
|
||||
var localLineIndex = globalLineIndex - processor.LineIndex;
|
||||
|
||||
if (tableState == null)
|
||||
if (tableState is null)
|
||||
{
|
||||
|
||||
// A table could be preceded by an empty line or a line containing an inline
|
||||
@@ -67,12 +67,12 @@ namespace Markdig.Extensions.Tables
|
||||
// start for a table. Typically, with this, we can have an attributes {...}
|
||||
// starting on the first line of a pipe table, even if the first line
|
||||
// doesn't have a pipe
|
||||
if (processor.Inline != null && (localLineIndex > 0 || c == '\n'))
|
||||
if (processor.Inline != null && (localLineIndex > 0 || c == '\n' || c == '\r'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (processor.Inline == null)
|
||||
if (processor.Inline is null)
|
||||
{
|
||||
isFirstLineEmpty = true;
|
||||
}
|
||||
@@ -81,7 +81,7 @@ namespace Markdig.Extensions.Tables
|
||||
processor.ParserStates[Index] = tableState;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
if (c == '\n' || c == '\r')
|
||||
{
|
||||
if (!isFirstLineEmpty && !tableState.LineHasPipe)
|
||||
{
|
||||
@@ -92,8 +92,8 @@ namespace Markdig.Extensions.Tables
|
||||
tableState.LineIndex++;
|
||||
if (!isFirstLineEmpty)
|
||||
{
|
||||
tableState.ColumnAndLineDelimiters.Add(processor.Inline);
|
||||
tableState.EndOfLines.Add(processor.Inline);
|
||||
tableState.ColumnAndLineDelimiters.Add(processor.Inline!);
|
||||
tableState.EndOfLines.Add(processor.Inline!);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -112,16 +112,15 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
tableState.LineHasPipe = true;
|
||||
tableState.LineIndex = localLineIndex;
|
||||
slice.NextChar(); // Skip the `|` character
|
||||
slice.SkipChar(); // Skip the `|` character
|
||||
|
||||
tableState.ColumnAndLineDelimiters.Add(processor.Inline);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool PostProcess(InlineProcessor state, Inline root, Inline lastChild, int postInlineProcessorIndex, bool isFinalProcessing)
|
||||
public bool PostProcess(InlineProcessor state, Inline? root, Inline? lastChild, int postInlineProcessorIndex, bool isFinalProcessing)
|
||||
{
|
||||
var container = root as ContainerInline;
|
||||
var tableState = state.ParserStates[Index] as TableState;
|
||||
@@ -129,23 +128,20 @@ namespace Markdig.Extensions.Tables
|
||||
// If the delimiters are being processed by an image link, we need to transform them back to literals
|
||||
if (!isFinalProcessing)
|
||||
{
|
||||
if (container == null || tableState == null)
|
||||
if (container is null || tableState is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var child = container.LastChild;
|
||||
List<PipeTableDelimiterInline> delimitersToRemove = null;
|
||||
List<PipeTableDelimiterInline>? delimitersToRemove = null;
|
||||
|
||||
while (child != null)
|
||||
{
|
||||
var pipeDelimiter = child as PipeTableDelimiterInline;
|
||||
if (pipeDelimiter != null)
|
||||
if (child is PipeTableDelimiterInline pipeDelimiter)
|
||||
{
|
||||
if (delimitersToRemove == null)
|
||||
{
|
||||
delimitersToRemove = new List<PipeTableDelimiterInline>();
|
||||
}
|
||||
delimitersToRemove ??= new List<PipeTableDelimiterInline>();
|
||||
|
||||
delimitersToRemove.Add(pipeDelimiter);
|
||||
}
|
||||
|
||||
@@ -196,10 +192,10 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
|
||||
// Remove previous state
|
||||
state.ParserStates[Index] = null;
|
||||
state.ParserStates[Index] = null!;
|
||||
|
||||
// Continue
|
||||
if (tableState == null || container == null || tableState.IsInvalidTable || !tableState.LineHasPipe ) //|| tableState.LineIndex != state.LocalLineIndex)
|
||||
if (tableState is null || container is null || tableState.IsInvalidTable || !tableState.LineHasPipe ) //|| tableState.LineIndex != state.LocalLineIndex)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -209,7 +205,7 @@ namespace Markdig.Extensions.Tables
|
||||
// TODO: we could optimize this by merging FindHeaderRow and the cell loop
|
||||
var aligns = FindHeaderRow(delimiters);
|
||||
|
||||
if (Options.RequireHeaderSeparator && aligns == null)
|
||||
if (Options.RequireHeaderSeparator && aligns is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -217,7 +213,7 @@ namespace Markdig.Extensions.Tables
|
||||
var table = new Table();
|
||||
|
||||
// If the current paragraph block has any attributes attached, we can copy them
|
||||
var attributes = state.Block.TryGetAttributes();
|
||||
var attributes = state.Block!.TryGetAttributes();
|
||||
if (attributes != null)
|
||||
{
|
||||
attributes.CopyTo(table.GetAttributes());
|
||||
@@ -258,9 +254,9 @@ namespace Markdig.Extensions.Tables
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (lastElement is ContainerInline)
|
||||
if (lastElement is ContainerInline lastElementContainer)
|
||||
{
|
||||
var nextElement = ((ContainerInline) lastElement).LastChild;
|
||||
var nextElement = lastElementContainer.LastChild;
|
||||
if (nextElement != null)
|
||||
{
|
||||
lastElement = nextElement;
|
||||
@@ -287,26 +283,24 @@ namespace Markdig.Extensions.Tables
|
||||
|
||||
// Cell loop
|
||||
// Reconstruct the table from the delimiters
|
||||
TableRow row = null;
|
||||
TableRow firstRow = null;
|
||||
TableRow? row = null;
|
||||
TableRow? firstRow = null;
|
||||
for (int i = 0; i < delimiters.Count; i++)
|
||||
{
|
||||
var delimiter = delimiters[i];
|
||||
var pipeSeparator = delimiter as PipeTableDelimiterInline;
|
||||
var isLine = delimiter is LineBreakInline;
|
||||
|
||||
if (row == null)
|
||||
if (row is null)
|
||||
{
|
||||
row = new TableRow();
|
||||
if (firstRow == null)
|
||||
{
|
||||
firstRow = row;
|
||||
}
|
||||
|
||||
firstRow ??= row;
|
||||
|
||||
// If the first delimiter is a pipe and doesn't have any parent or previous sibling, for cases like:
|
||||
// 0) | a | b | \n
|
||||
// 1) | a | b \n
|
||||
if (pipeSeparator != null && (delimiter.PreviousSibling == null || delimiter.PreviousSibling is LineBreakInline))
|
||||
if (pipeSeparator != null && (delimiter.PreviousSibling is null || delimiter.PreviousSibling is LineBreakInline))
|
||||
{
|
||||
delimiter.Remove();
|
||||
continue;
|
||||
@@ -318,23 +312,23 @@ namespace Markdig.Extensions.Tables
|
||||
// x
|
||||
// 1) | a | b \n
|
||||
// 2) a | b \n
|
||||
Inline endOfCell = null;
|
||||
Inline beginOfCell = null;
|
||||
Inline? endOfCell = null;
|
||||
Inline? beginOfCell = null;
|
||||
var cellContentIt = delimiter;
|
||||
while (true)
|
||||
{
|
||||
cellContentIt = cellContentIt.PreviousSibling ?? cellContentIt.Parent;
|
||||
|
||||
if (cellContentIt == null || cellContentIt is LineBreakInline)
|
||||
if (cellContentIt is null || cellContentIt is LineBreakInline)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// The cell begins at the first effective child after a | or the top ContainerInline (which is not necessary to bring into the tree + it contains an invalid span calculation)
|
||||
if (cellContentIt is PipeTableDelimiterInline || (cellContentIt.GetType() == typeof(ContainerInline) && cellContentIt.Parent == null ))
|
||||
if (cellContentIt is PipeTableDelimiterInline || (cellContentIt.GetType() == typeof(ContainerInline) && cellContentIt.Parent is null ))
|
||||
{
|
||||
beginOfCell = ((ContainerInline)cellContentIt).FirstChild;
|
||||
if (endOfCell == null)
|
||||
if (endOfCell is null)
|
||||
{
|
||||
endOfCell = beginOfCell;
|
||||
}
|
||||
@@ -342,20 +336,19 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
|
||||
beginOfCell = cellContentIt;
|
||||
if (endOfCell == null)
|
||||
if (endOfCell is null)
|
||||
{
|
||||
endOfCell = beginOfCell;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If the current deilimiter is a pipe `|` OR
|
||||
// the beginOfCell/endOfCell are not null and
|
||||
// either they are :
|
||||
// - different
|
||||
// - they contain a single element, but it is not a line break (\n) or an empty/whitespace Literal.
|
||||
// Then we can add a cell to the current row
|
||||
if (!isLine || (beginOfCell != null && endOfCell != null && ( beginOfCell != endOfCell || !(beginOfCell is LineBreakInline || (beginOfCell is LiteralInline && ((LiteralInline)beginOfCell).Content.IsEmptyOrWhitespace())))))
|
||||
if (!isLine || (beginOfCell != null && endOfCell != null && ( beginOfCell != endOfCell || !(beginOfCell is LineBreakInline || (beginOfCell is LiteralInline beingOfCellLiteral && beingOfCellLiteral.Content.IsEmptyOrWhitespace())))))
|
||||
{
|
||||
if (!isLine)
|
||||
{
|
||||
@@ -420,11 +413,11 @@ namespace Markdig.Extensions.Tables
|
||||
Debug.Assert(row != null);
|
||||
if (table.Span.IsEmpty)
|
||||
{
|
||||
table.Span = row.Span;
|
||||
table.Span = row!.Span;
|
||||
table.Line = row.Line;
|
||||
table.Column = row.Column;
|
||||
}
|
||||
table.Add(row);
|
||||
table.Add(row!);
|
||||
row = null;
|
||||
}
|
||||
}
|
||||
@@ -470,11 +463,11 @@ namespace Markdig.Extensions.Tables
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool ParseHeaderString(Inline inline, out TableColumnAlign? align)
|
||||
private static bool ParseHeaderString(Inline? inline, out TableColumnAlign? align)
|
||||
{
|
||||
align = 0;
|
||||
var literal = inline as LiteralInline;
|
||||
if (literal == null)
|
||||
if (literal is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -493,10 +486,10 @@ namespace Markdig.Extensions.Tables
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<TableColumnDefinition> FindHeaderRow(List<Inline> delimiters)
|
||||
private List<TableColumnDefinition>? FindHeaderRow(List<Inline> delimiters)
|
||||
{
|
||||
bool isValidRow = false;
|
||||
List<TableColumnDefinition> aligns = null;
|
||||
List<TableColumnDefinition>? aligns = null;
|
||||
for (int i = 0; i < delimiters.Count; i++)
|
||||
{
|
||||
if (!IsLine(delimiters[i]))
|
||||
@@ -518,20 +511,21 @@ namespace Markdig.Extensions.Tables
|
||||
|
||||
// Check the left side of a `|` delimiter
|
||||
TableColumnAlign? align = null;
|
||||
if (delimiter.PreviousSibling != null && !ParseHeaderString(delimiter.PreviousSibling, out align))
|
||||
if (delimiter.PreviousSibling != null &&
|
||||
!(delimiter.PreviousSibling is LiteralInline li && li.Content.IsEmptyOrWhitespace()) && // ignore parsed whitespace
|
||||
!ParseHeaderString(delimiter.PreviousSibling, out align))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Create aligns until we may have a header row
|
||||
if (aligns == null)
|
||||
{
|
||||
aligns = new List<TableColumnDefinition>();
|
||||
}
|
||||
|
||||
aligns ??= new List<TableColumnDefinition>();
|
||||
|
||||
aligns.Add(new TableColumnDefinition() { Alignment = align });
|
||||
|
||||
// If this is the last delimiter, we need to check the right side of the `|` delimiter
|
||||
if (nextDelimiter == null)
|
||||
if (nextDelimiter is null)
|
||||
{
|
||||
var nextSibling = columnDelimiter != null
|
||||
? columnDelimiter.FirstChild
|
||||
@@ -572,20 +566,20 @@ namespace Markdig.Extensions.Tables
|
||||
return inline is LineBreakInline;
|
||||
}
|
||||
|
||||
private static bool IsStartOfLineColumnDelimiter(Inline inline)
|
||||
private static bool IsStartOfLineColumnDelimiter(Inline? inline)
|
||||
{
|
||||
if (inline == null)
|
||||
if (inline is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var previous = inline.PreviousSibling;
|
||||
if (previous == null)
|
||||
if (previous is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
var literal = previous as LiteralInline;
|
||||
if (literal != null)
|
||||
|
||||
if (previous is LiteralInline literal)
|
||||
{
|
||||
if (!literal.Content.IsEmptyOrWhitespace())
|
||||
{
|
||||
@@ -593,65 +587,57 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
previous = previous.PreviousSibling;
|
||||
}
|
||||
return previous == null || IsLine(previous);
|
||||
return previous is null || IsLine(previous);
|
||||
}
|
||||
|
||||
private static void TrimStart(Inline inline)
|
||||
private static void TrimStart(Inline? inline)
|
||||
{
|
||||
while (inline is ContainerInline && !(inline is DelimiterInline))
|
||||
{
|
||||
inline = ((ContainerInline)inline).FirstChild;
|
||||
}
|
||||
var literal = inline as LiteralInline;
|
||||
if (literal != null)
|
||||
|
||||
if (inline is LiteralInline literal)
|
||||
{
|
||||
literal.Content.TrimStart();
|
||||
}
|
||||
}
|
||||
|
||||
private static void TrimEnd(Inline inline)
|
||||
private static void TrimEnd(Inline? inline)
|
||||
{
|
||||
var literal = inline as LiteralInline;
|
||||
if (literal != null)
|
||||
if (inline is LiteralInline literal)
|
||||
{
|
||||
literal.Content.TrimEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsNullOrSpace(Inline inline)
|
||||
private static bool IsNullOrSpace(Inline? inline)
|
||||
{
|
||||
if (inline == null)
|
||||
if (inline is null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
var literal = inline as LiteralInline;
|
||||
if (literal != null)
|
||||
|
||||
if (inline is LiteralInline literal)
|
||||
{
|
||||
return literal.Content.IsEmptyOrWhitespace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private class TableState
|
||||
private sealed class TableState
|
||||
{
|
||||
public TableState()
|
||||
{
|
||||
ColumnAndLineDelimiters = new List<Inline>();
|
||||
Cells = new List<TableCell>();
|
||||
EndOfLines = new List<Inline>();
|
||||
}
|
||||
|
||||
public bool IsInvalidTable { get; set; }
|
||||
|
||||
public bool LineHasPipe { get; set; }
|
||||
|
||||
public int LineIndex { get; set; }
|
||||
|
||||
public List<Inline> ColumnAndLineDelimiters { get; }
|
||||
public List<Inline> ColumnAndLineDelimiters { get; } = new();
|
||||
|
||||
public List<TableCell> Cells { get; }
|
||||
public List<TableCell> Cells { get; } = new();
|
||||
|
||||
public List<Inline> EndOfLines { get; }
|
||||
public List<Inline> EndOfLines { get; } = new();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace Markdig.Extensions.Tables
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Table"/> class.
|
||||
/// </summary>
|
||||
public Table() : this(null)
|
||||
public Table() : base(null)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -25,15 +25,14 @@ namespace Markdig.Extensions.Tables
|
||||
/// Initializes a new instance of the <see cref="Table"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parser">The parser used to create this block.</param>
|
||||
public Table(BlockParser parser) : base(parser)
|
||||
public Table(BlockParser? parser) : base(parser)
|
||||
{
|
||||
ColumnDefinitions = new List<TableColumnDefinition>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column alignments. May be null.
|
||||
/// </summary>
|
||||
public List<TableColumnDefinition> ColumnDefinitions { get; }
|
||||
public List<TableColumnDefinition> ColumnDefinitions { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the table structure is valid.
|
||||
@@ -83,8 +82,7 @@ namespace Markdig.Extensions.Tables
|
||||
var maxColumn = 0;
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
{
|
||||
var row = this[i] as TableRow;
|
||||
if (row != null && row.Count > maxColumn)
|
||||
if (this[i] is TableRow row && row.Count > maxColumn)
|
||||
{
|
||||
maxColumn = row.Count;
|
||||
}
|
||||
@@ -92,8 +90,7 @@ namespace Markdig.Extensions.Tables
|
||||
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
{
|
||||
var row = this[i] as TableRow;
|
||||
if (row != null)
|
||||
if (this[i] is TableRow row)
|
||||
{
|
||||
for (int j = row.Count; j < maxColumn; j++)
|
||||
{
|
||||
@@ -124,8 +121,7 @@ namespace Markdig.Extensions.Tables
|
||||
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
{
|
||||
var row = this[i] as TableRow;
|
||||
if (row != null)
|
||||
if (this[i] is TableRow row)
|
||||
{
|
||||
for (int j = row.Count; j < maxColumn; j++)
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Markdig.Extensions.Tables
|
||||
/// Initializes a new instance of the <see cref="TableCell"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parser">The parser used to create this block.</param>
|
||||
public TableCell(BlockParser parser) : base(parser)
|
||||
public TableCell(BlockParser? parser) : base(parser)
|
||||
{
|
||||
AllowClose = true;
|
||||
ColumnSpan = 1;
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Markdig.Extensions.Tables
|
||||
if (c == ':')
|
||||
{
|
||||
hasLeft = true;
|
||||
slice.NextChar();
|
||||
slice.SkipChar();
|
||||
}
|
||||
|
||||
slice.TrimStart();
|
||||
@@ -91,7 +91,7 @@ namespace Markdig.Extensions.Tables
|
||||
if (c == ':')
|
||||
{
|
||||
hasRight = true;
|
||||
slice.NextChar();
|
||||
slice.SkipChar();
|
||||
}
|
||||
slice.TrimStart();
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Markdig.Extensions.TaskLists
|
||||
// [ ]
|
||||
// or [x] or [X]
|
||||
|
||||
if (!(processor.Block.Parent is ListItemBlock listItemBlock))
|
||||
if (!(processor.Block!.Parent is ListItemBlock listItemBlock))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ namespace Markdig.Extensions.TaskLists
|
||||
return false;
|
||||
}
|
||||
// Skip last ]
|
||||
slice.NextChar();
|
||||
slice.SkipChar();
|
||||
|
||||
// Create the TaskList
|
||||
var taskItem = new TaskList()
|
||||
@@ -75,7 +75,7 @@ namespace Markdig.Extensions.TaskLists
|
||||
listItemBlock.GetAttributes().AddClass(ListItemClass);
|
||||
}
|
||||
|
||||
var listBlock = (ListBlock) listItemBlock.Parent;
|
||||
var listBlock = (ListBlock) listItemBlock.Parent!;
|
||||
if (!string.IsNullOrEmpty(ListClass))
|
||||
{
|
||||
listBlock.GetAttributes().AddClass(ListClass);
|
||||
|
||||
@@ -221,9 +221,9 @@ namespace Markdig.Helpers
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsNewLine(this char c)
|
||||
public static bool IsNewLineOrLineFeed(this char c)
|
||||
{
|
||||
return c == '\n';
|
||||
return c == '\n' || c == '\r';
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -355,22 +355,6 @@ namespace Markdig.Helpers
|
||||
internal static bool IsInInclusiveRange(int value, uint min, uint max)
|
||||
=> ((uint)value - min) <= (max - min);
|
||||
|
||||
public static IEnumerable<int> ToUtf32(StringSlice text)
|
||||
{
|
||||
for (int i = text.Start; i <= text.End; i++)
|
||||
{
|
||||
if (IsHighSurrogate(text[i]) && i < text.End && IsLowSurrogate(text[i + 1]))
|
||||
{
|
||||
Debug.Assert(char.IsSurrogatePair(text[i], text[i + 1]));
|
||||
yield return char.ConvertToUtf32(text[i], text[i + 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return text[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsRightToLeft(int c)
|
||||
{
|
||||
// Generated from Table D.1 of RFC3454
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,10 +14,10 @@ namespace Markdig.Helpers
|
||||
/// Allows to associate characters to a data structures and query efficiently for them.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class CharacterMap<T> where T : class
|
||||
public sealed class CharacterMap<T> where T : class
|
||||
{
|
||||
private readonly T[] asciiMap;
|
||||
private readonly Dictionary<uint, T> nonAsciiMap;
|
||||
private readonly Dictionary<uint, T>? nonAsciiMap;
|
||||
private readonly BoolVector128 isOpeningCharacter;
|
||||
|
||||
/// <summary>
|
||||
@@ -27,7 +27,7 @@ namespace Markdig.Helpers
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public CharacterMap(IEnumerable<KeyValuePair<char, T>> maps)
|
||||
{
|
||||
if (maps == null) ThrowHelper.ArgumentNullException(nameof(maps));
|
||||
if (maps is null) ThrowHelper.ArgumentNullException(nameof(maps));
|
||||
var charSet = new HashSet<char>();
|
||||
int maxChar = 0;
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Markdig.Helpers
|
||||
asciiMap[openingChar] ??= state.Value;
|
||||
isOpeningCharacter.Set(openingChar);
|
||||
}
|
||||
else if (!nonAsciiMap.ContainsKey(openingChar))
|
||||
else if (!nonAsciiMap!.ContainsKey(openingChar))
|
||||
{
|
||||
nonAsciiMap[openingChar] = state.Value;
|
||||
}
|
||||
@@ -76,7 +76,7 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
/// <param name="openingChar">The opening character.</param>
|
||||
/// <returns>A list of parsers valid for the specified opening character or null if no parsers registered.</returns>
|
||||
public T this[uint openingChar]
|
||||
public T? this[uint openingChar]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
@@ -88,7 +88,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
else
|
||||
{
|
||||
T map = null;
|
||||
T? map = null;
|
||||
nonAsciiMap?.TryGetValue(openingChar, out map);
|
||||
return map;
|
||||
}
|
||||
@@ -172,7 +172,7 @@ namespace Markdig.Helpers
|
||||
for (int i = start; i <= end; i++)
|
||||
{
|
||||
char c = pText[i];
|
||||
if (c < 128 ? isOpeningCharacter[c] : nonAsciiMap.ContainsKey(c))
|
||||
if (c < 128 ? isOpeningCharacter[c] : nonAsciiMap!.ContainsKey(c))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// Copyright (c) Miha Zupan. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@@ -277,7 +280,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_unicodeRootMap == null)
|
||||
if (_unicodeRootMap is null)
|
||||
{
|
||||
_unicodeRootMap = new Dictionary<char, int>();
|
||||
}
|
||||
@@ -312,7 +315,7 @@ namespace Markdig.Helpers
|
||||
/// <param name="input">Matches to initialize the <see cref="CompactPrefixTree{TValue}"/> with. For best lookup performance, this collection should be sorted.</param>
|
||||
public CompactPrefixTree(ICollection<KeyValuePair<string, TValue>> input)
|
||||
{
|
||||
if (input == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
|
||||
if (input is null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
|
||||
|
||||
Init(input.Count, input.Count * 2, input.Count * 2);
|
||||
|
||||
@@ -426,7 +429,7 @@ namespace Markdig.Helpers
|
||||
private bool TryInsert(in KeyValuePair<string, TValue> pair, InsertionBehavior behavior)
|
||||
{
|
||||
string key = pair.Key;
|
||||
if (key == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
|
||||
if (key is null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
|
||||
if (key.Length == 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.key, ExceptionReason.String_Empty);
|
||||
Debug.Assert(!string.IsNullOrEmpty(key));
|
||||
|
||||
@@ -516,7 +519,7 @@ namespace Markdig.Helpers
|
||||
Debug.Assert(key.Length != previousKey.Length);
|
||||
if (previousKey.Length < key.Length) // If the input was sorted, this should be hit
|
||||
{
|
||||
Debug.Assert(key.StartsWith(previousKey));
|
||||
Debug.Assert(key.StartsWith(previousKey, StringComparison.Ordinal));
|
||||
node.ChildChar = key[i];
|
||||
node.MatchIndex = previousMatchIndex;
|
||||
EnsureTreeCapacity(TreeSize + 1);
|
||||
@@ -530,7 +533,7 @@ namespace Markdig.Helpers
|
||||
else // if key.Length < previousKey.Length
|
||||
{
|
||||
Debug.Assert(key.Length < previousKey.Length);
|
||||
Debug.Assert(previousKey.StartsWith(key));
|
||||
Debug.Assert(previousKey.StartsWith(key, StringComparison.Ordinal));
|
||||
node.ChildChar = previousKey[i];
|
||||
node.MatchIndex = Count;
|
||||
EnsureTreeCapacity(TreeSize + 1);
|
||||
@@ -580,7 +583,7 @@ namespace Markdig.Helpers
|
||||
else
|
||||
{
|
||||
// This node has a child char, therefore we either don't have a match attached or that match is simply a prefix of the current key
|
||||
Debug.Assert(node.MatchIndex == -1 || key.StartsWith(_matches[node.MatchIndex].Key));
|
||||
Debug.Assert(node.MatchIndex == -1 || key.StartsWith(_matches[node.MatchIndex].Key, StringComparison.Ordinal));
|
||||
|
||||
// Set this pair as the current node's first element in the Children list
|
||||
node.Children = _childrenIndex;
|
||||
@@ -638,7 +641,7 @@ namespace Markdig.Helpers
|
||||
// It's not a duplicate but shares key.Length characters, therefore it's longer
|
||||
// This will never occur if the input was sorted
|
||||
Debug.Assert(previousMatch.Key.Length > key.Length);
|
||||
Debug.Assert(previousMatch.Key.StartsWith(key));
|
||||
Debug.Assert(previousMatch.Key.StartsWith(key, StringComparison.Ordinal));
|
||||
Debug.Assert(node.ChildChar == 0 && node.Children == -1);
|
||||
|
||||
// It is a leaf node
|
||||
|
||||
@@ -23,14 +23,14 @@ namespace Markdig.Helpers
|
||||
public T[] Rent()
|
||||
{
|
||||
T[][] buffers = _buffers;
|
||||
T[] buffer = null;
|
||||
T[] buffer = null!;
|
||||
if (Interlocked.CompareExchange(ref _lock, 1, 0) == 0)
|
||||
{
|
||||
int index = _index;
|
||||
if ((uint)index < (uint)buffers.Length)
|
||||
{
|
||||
buffer = buffers[index];
|
||||
buffers[index] = null;
|
||||
buffers[index] = null!;
|
||||
_index = index + 1;
|
||||
}
|
||||
Interlocked.Decrement(ref _lock);
|
||||
@@ -64,17 +64,16 @@ namespace Markdig.Helpers
|
||||
_bucket32 = new Bucket(size32);
|
||||
}
|
||||
|
||||
private Bucket SelectBucket(int length)
|
||||
private Bucket? SelectBucket(int length)
|
||||
{
|
||||
switch (length)
|
||||
return length switch
|
||||
{
|
||||
case 4: return _bucket4;
|
||||
case 8: return _bucket8;
|
||||
case 16: return _bucket16;
|
||||
case 32: return _bucket32;
|
||||
|
||||
default: return null;
|
||||
}
|
||||
4 => _bucket4,
|
||||
8 => _bucket8,
|
||||
16 => _bucket16,
|
||||
32 => _bucket32,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
public T[] Rent(int length)
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity without <c>&</c> and <c>;</c> symbols, for example, <c>copy</c>.</param>
|
||||
/// <returns>The unicode character set or <c>null</c> if the entity was not recognized.</returns>
|
||||
public static string DecodeEntity(ReadOnlySpan<char> entity)
|
||||
public static string? DecodeEntity(ReadOnlySpan<char> entity)
|
||||
{
|
||||
if (EntityMap.TryMatchExact(entity, out KeyValuePair<string, string> result))
|
||||
return result.Value;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
@@ -30,28 +31,30 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
}
|
||||
public static string EscapeUrlCharacter(char c)
|
||||
|
||||
public static string? EscapeUrlCharacter(char c)
|
||||
{
|
||||
return c < 128 ? EscapeUrlsForAscii[c] : null;
|
||||
}
|
||||
|
||||
public static bool TryParseHtmlTag(StringSlice text, out string htmlTag)
|
||||
{
|
||||
return TryParseHtmlTag(ref text, out htmlTag);
|
||||
}
|
||||
|
||||
public static bool TryParseHtmlTag(ref StringSlice text, out string htmlTag)
|
||||
public static bool TryParseHtmlTag(ref StringSlice text, [NotNullWhen(true)] out string? htmlTag)
|
||||
{
|
||||
var builder = StringBuilderCache.Local();
|
||||
var result = TryParseHtmlTag(ref text, builder);
|
||||
htmlTag = builder.ToString();
|
||||
builder.Length = 0;
|
||||
return result;
|
||||
if (TryParseHtmlTag(ref text, builder))
|
||||
{
|
||||
htmlTag = builder.GetStringAndReset();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
htmlTag = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryParseHtmlTag(ref StringSlice text, StringBuilder builder)
|
||||
{
|
||||
if (builder == null) ThrowHelper.ArgumentNullException(nameof(builder));
|
||||
if (builder is null) ThrowHelper.ArgumentNullException(nameof(builder));
|
||||
var c = text.CurrentChar;
|
||||
if (c != '<')
|
||||
{
|
||||
@@ -127,7 +130,7 @@ namespace Markdig.Helpers
|
||||
case '\0':
|
||||
return false;
|
||||
case '>':
|
||||
text.NextChar();
|
||||
text.SkipChar();
|
||||
builder.Append(c);
|
||||
return true;
|
||||
case '/':
|
||||
@@ -137,7 +140,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
return false;
|
||||
}
|
||||
text.NextChar();
|
||||
text.SkipChar();
|
||||
builder.Append('>');
|
||||
return true;
|
||||
case '=':
|
||||
@@ -269,7 +272,7 @@ namespace Markdig.Helpers
|
||||
|
||||
if (c == '>')
|
||||
{
|
||||
text.NextChar();
|
||||
text.SkipChar();
|
||||
builder.Append('>');
|
||||
return true;
|
||||
}
|
||||
@@ -278,16 +281,12 @@ namespace Markdig.Helpers
|
||||
|
||||
private static bool TryParseHtmlTagCData(ref StringSlice text, StringBuilder builder)
|
||||
{
|
||||
builder.Append('[');
|
||||
var c = text.NextChar();
|
||||
if (c == 'C' &&
|
||||
text.NextChar() == 'D' &&
|
||||
text.NextChar() == 'A' &&
|
||||
text.NextChar() == 'T' &&
|
||||
text.NextChar() == 'A' &&
|
||||
(c = text.NextChar()) == '[')
|
||||
if (text.Match("[CDATA["))
|
||||
{
|
||||
builder.Append("CDATA[");
|
||||
builder.Append("[CDATA[");
|
||||
text.Start += 6;
|
||||
|
||||
char c = '\0';
|
||||
while (true)
|
||||
{
|
||||
var pc = c;
|
||||
@@ -297,23 +296,15 @@ namespace Markdig.Helpers
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c == ']' && pc == ']')
|
||||
{
|
||||
builder.Append(']');
|
||||
c = text.NextChar();
|
||||
if (c == '>')
|
||||
{
|
||||
builder.Append('>');
|
||||
text.NextChar();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c == '\0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
builder.Append(c);
|
||||
|
||||
if (c == ']' && pc == ']' && text.PeekChar() == '>')
|
||||
{
|
||||
text.SkipChar();
|
||||
text.SkipChar();
|
||||
builder.Append('>');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -337,7 +328,7 @@ namespace Markdig.Helpers
|
||||
c = text.NextChar();
|
||||
if (c == '>')
|
||||
{
|
||||
text.NextChar();
|
||||
text.SkipChar();
|
||||
builder.Append('>');
|
||||
return true;
|
||||
}
|
||||
@@ -392,7 +383,7 @@ namespace Markdig.Helpers
|
||||
if (c == '>')
|
||||
{
|
||||
builder.Append('>');
|
||||
text.NextChar();
|
||||
text.SkipChar();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -417,7 +408,7 @@ namespace Markdig.Helpers
|
||||
if (c == '>' && prevChar == '?')
|
||||
{
|
||||
builder.Append('>');
|
||||
text.NextChar();
|
||||
text.SkipChar();
|
||||
return true;
|
||||
}
|
||||
prevChar = c;
|
||||
@@ -431,7 +422,7 @@ namespace Markdig.Helpers
|
||||
/// <param name="text">The string data that will be changed by unescaping any punctuation or symbol characters.</param>
|
||||
/// <param name="removeBackSlash">if set to <c>true</c> [remove back slash].</param>
|
||||
/// <returns></returns>
|
||||
public static string Unescape(string text, bool removeBackSlash = true)
|
||||
public static string Unescape(string? text, bool removeBackSlash = true)
|
||||
{
|
||||
// Credits: code from CommonMark.NET
|
||||
// Copyright (c) 2014, Kārlis Gaņģis All rights reserved.
|
||||
@@ -446,9 +437,9 @@ namespace Markdig.Helpers
|
||||
int lastPos = 0;
|
||||
char c;
|
||||
char[] search = removeBackSlash ? SearchBackAndAmp : SearchAmp;
|
||||
StringBuilder sb = null;
|
||||
StringBuilder? sb = null;
|
||||
|
||||
while ((searchPos = text.IndexOfAny(search, searchPos)) != -1)
|
||||
while ((searchPos = text!.IndexOfAny(search, searchPos)) != -1)
|
||||
{
|
||||
sb ??= StringBuilderCache.Local();
|
||||
c = text[searchPos];
|
||||
@@ -497,7 +488,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
if (sb == null || lastPos == 0)
|
||||
if (sb is null || lastPos == 0)
|
||||
return text;
|
||||
|
||||
sb.Append(text, lastPos, text.Length - lastPos);
|
||||
@@ -530,29 +521,24 @@ namespace Markdig.Helpers
|
||||
if (c == '#')
|
||||
{
|
||||
c = slice.PeekChar();
|
||||
if (c == 'x' || c == 'X')
|
||||
if ((c | 0x20) == 'x')
|
||||
{
|
||||
c = slice.NextChar(); // skip #
|
||||
// expect 1-6 hex digits starting from pos+3
|
||||
while (c != '\0')
|
||||
{
|
||||
c = slice.NextChar();
|
||||
if (c >= '0' && c <= '9')
|
||||
|
||||
if (c.IsDigit())
|
||||
{
|
||||
if (++counter == 7) return 0;
|
||||
numericEntity = numericEntity*16 + (c - '0');
|
||||
numericEntity = numericEntity * 16 + (c - '0');
|
||||
continue;
|
||||
}
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
else if ((uint)((c - 'A') & ~0x20) <= ('F' - 'A'))
|
||||
{
|
||||
if (++counter == 7) return 0;
|
||||
numericEntity = numericEntity*16 + (c - 'A' + 10);
|
||||
continue;
|
||||
}
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
{
|
||||
if (++counter == 7) return 0;
|
||||
numericEntity = numericEntity*16 + (c - 'a' + 10);
|
||||
numericEntity = numericEntity * 16 + ((c | 0x20) - 'a' + 10);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -569,10 +555,10 @@ namespace Markdig.Helpers
|
||||
{
|
||||
c = slice.NextChar();
|
||||
|
||||
if (c >= '0' && c <= '9')
|
||||
if (c.IsDigit())
|
||||
{
|
||||
if (++counter == 8) return 0;
|
||||
numericEntity = numericEntity*10 + (c - '0');
|
||||
numericEntity = numericEntity * 10 + (c - '0');
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -586,7 +572,7 @@ namespace Markdig.Helpers
|
||||
else
|
||||
{
|
||||
// expect a letter and 1-31 letters or digits
|
||||
if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')))
|
||||
if (!c.IsAlpha())
|
||||
return 0;
|
||||
|
||||
namedEntityStart = slice.Start;
|
||||
@@ -595,7 +581,8 @@ namespace Markdig.Helpers
|
||||
while (c != '\0')
|
||||
{
|
||||
c = slice.NextChar();
|
||||
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'))
|
||||
|
||||
if (c.IsAlphaNumeric())
|
||||
{
|
||||
if (++counter == 32)
|
||||
return 0;
|
||||
|
||||
@@ -31,12 +31,23 @@ namespace Markdig.Helpers
|
||||
/// <returns>The next character. `\0` is end of the iteration.</returns>
|
||||
char NextChar();
|
||||
|
||||
/// <summary>
|
||||
/// Goes to the next character, incrementing the <see cref="Start" /> position.
|
||||
/// </summary>
|
||||
void SkipChar();
|
||||
|
||||
/// <summary>
|
||||
/// Peeks at the next character, without incrementing the <see cref="Start"/> position.
|
||||
/// </summary>
|
||||
/// <returns>The next character. `\0` is end of the iteration.</returns>
|
||||
char PeekChar();
|
||||
|
||||
/// <summary>
|
||||
/// Peeks at the next character, without incrementing the <see cref="Start"/> position.
|
||||
/// </summary>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns>The next character. `\0` is end of the iteration.</returns>
|
||||
char PeekChar(int offset = 1);
|
||||
char PeekChar(int offset);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is empty.
|
||||
|
||||
@@ -45,13 +45,25 @@ namespace Markdig.Helpers
|
||||
for (int i = sourcePosition; i < text.Length; i++)
|
||||
{
|
||||
char c = text[i];
|
||||
if (c == '\r' || c == '\n')
|
||||
if (c == '\r')
|
||||
{
|
||||
var slice = new StringSlice(text, sourcePosition, i - 1);
|
||||
|
||||
int length = 1;
|
||||
var newLine = NewLine.CarriageReturn;
|
||||
if (c == '\r' && (uint)(i + 1) < (uint)text.Length && text[i + 1] == '\n')
|
||||
{
|
||||
i++;
|
||||
length = 2;
|
||||
newLine = NewLine.CarriageReturnLineFeed;
|
||||
}
|
||||
|
||||
var slice = new StringSlice(text, sourcePosition, i - length, newLine);
|
||||
SourcePosition = i + 1;
|
||||
return slice;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
var slice = new StringSlice(text, sourcePosition, i - 1, NewLine.LineFeed);
|
||||
SourcePosition = i + 1;
|
||||
return slice;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
@@ -13,7 +14,7 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
public static class LinkHelper
|
||||
{
|
||||
public static bool TryParseAutolink(StringSlice text, out string link, out bool isEmail)
|
||||
public static bool TryParseAutolink(StringSlice text, [NotNullWhen(true)] out string? link, out bool isEmail)
|
||||
{
|
||||
return TryParseAutolink(ref text, out link, out isEmail);
|
||||
}
|
||||
@@ -117,7 +118,7 @@ namespace Markdig.Helpers
|
||||
return c == '_' || c == '-' || c == '.';
|
||||
}
|
||||
|
||||
public static bool TryParseAutolink(ref StringSlice text, out string link, out bool isEmail)
|
||||
public static bool TryParseAutolink(ref StringSlice text, [NotNullWhen(true)] out string? link, out bool isEmail)
|
||||
{
|
||||
link = null;
|
||||
isEmail = false;
|
||||
@@ -246,7 +247,7 @@ namespace Markdig.Helpers
|
||||
break;
|
||||
}
|
||||
|
||||
text.NextChar();
|
||||
text.SkipChar();
|
||||
link = builder.ToString();
|
||||
builder.Length = 0;
|
||||
return true;
|
||||
@@ -294,7 +295,7 @@ namespace Markdig.Helpers
|
||||
|
||||
if (c == '>')
|
||||
{
|
||||
text.NextChar();
|
||||
text.SkipChar();
|
||||
link = builder.ToString();
|
||||
builder.Length = 0;
|
||||
return true;
|
||||
@@ -321,22 +322,22 @@ namespace Markdig.Helpers
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryParseInlineLink(StringSlice text, out string link, out string title)
|
||||
public static bool TryParseInlineLink(StringSlice text, out string? link, out string? title)
|
||||
{
|
||||
return TryParseInlineLink(ref text, out link, out title, out _, out _);
|
||||
}
|
||||
|
||||
public static bool TryParseInlineLink(StringSlice text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan)
|
||||
public static bool TryParseInlineLink(StringSlice text, out string? link, out string? title, out SourceSpan linkSpan, out SourceSpan titleSpan)
|
||||
{
|
||||
return TryParseInlineLink(ref text, out link, out title, out linkSpan, out titleSpan);
|
||||
}
|
||||
|
||||
public static bool TryParseInlineLink(ref StringSlice text, out string link, out string title)
|
||||
public static bool TryParseInlineLink(ref StringSlice text, out string? link, out string? title)
|
||||
{
|
||||
return TryParseInlineLink(ref text, out link, out title, out SourceSpan linkSpan, out SourceSpan titleSpan);
|
||||
}
|
||||
|
||||
public static bool TryParseInlineLink(ref StringSlice text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan)
|
||||
public static bool TryParseInlineLink(ref StringSlice text, out string? link, out string? title, out SourceSpan linkSpan, out SourceSpan titleSpan)
|
||||
{
|
||||
// 1. An inline link consists of a link text followed immediately by a left parenthesis (,
|
||||
// 2. optional whitespace, TODO: specs: is it whitespace or multiple whitespaces?
|
||||
@@ -355,11 +356,11 @@ namespace Markdig.Helpers
|
||||
// 1. An inline link consists of a link text followed immediately by a left parenthesis (,
|
||||
if (c == '(')
|
||||
{
|
||||
text.NextChar();
|
||||
text.TrimStart();
|
||||
text.SkipChar();
|
||||
text.TrimStart(); // this breaks whitespace before an uri
|
||||
|
||||
var pos = text.Start;
|
||||
if (TryParseUrl(ref text, out link))
|
||||
if (TryParseUrl(ref text, out link, out _))
|
||||
{
|
||||
linkSpan.Start = pos;
|
||||
linkSpan.End = text.Start - 1;
|
||||
@@ -384,7 +385,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
isValid = true;
|
||||
}
|
||||
else if (TryParseTitle(ref text, out title))
|
||||
else if (TryParseTitle(ref text, out title, out char enclosingCharacter))
|
||||
{
|
||||
titleSpan.Start = pos;
|
||||
titleSpan.End = text.Start - 1;
|
||||
@@ -407,28 +408,136 @@ namespace Markdig.Helpers
|
||||
if (isValid)
|
||||
{
|
||||
// Skip ')'
|
||||
text.NextChar();
|
||||
text.SkipChar();
|
||||
title ??= string.Empty;
|
||||
}
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public static bool TryParseTitle<T>(T text, out string title) where T : ICharIterator
|
||||
public static bool TryParseInlineLinkTrivia(
|
||||
ref StringSlice text,
|
||||
[NotNullWhen(true)] out string? link,
|
||||
out SourceSpan unescapedLink,
|
||||
out string? title,
|
||||
out SourceSpan unescapedTitle,
|
||||
out char titleEnclosingCharacter,
|
||||
out SourceSpan linkSpan,
|
||||
out SourceSpan titleSpan,
|
||||
out SourceSpan triviaBeforeLink,
|
||||
out SourceSpan triviaAfterLink,
|
||||
out SourceSpan triviaAfterTitle,
|
||||
out bool urlHasPointyBrackets)
|
||||
{
|
||||
return TryParseTitle(ref text, out title);
|
||||
// 1. An inline link consists of a link text followed immediately by a left parenthesis (,
|
||||
// 2. optional whitespace, TODO: specs: is it whitespace or multiple whitespaces?
|
||||
// 3. an optional link destination,
|
||||
// 4. an optional link title separated from the link destination by whitespace,
|
||||
// 5. optional whitespace, TODO: specs: is it whitespace or multiple whitespaces?
|
||||
// 6. and a right parenthesis )
|
||||
bool isValid = false;
|
||||
var c = text.CurrentChar;
|
||||
link = null;
|
||||
unescapedLink = SourceSpan.Empty;
|
||||
title = null;
|
||||
unescapedTitle = SourceSpan.Empty;
|
||||
|
||||
linkSpan = SourceSpan.Empty;
|
||||
titleSpan = SourceSpan.Empty;
|
||||
triviaBeforeLink = SourceSpan.Empty;
|
||||
triviaAfterLink = SourceSpan.Empty;
|
||||
triviaAfterTitle = SourceSpan.Empty;
|
||||
urlHasPointyBrackets = false;
|
||||
titleEnclosingCharacter = '\0';
|
||||
|
||||
// 1. An inline link consists of a link text followed immediately by a left parenthesis (,
|
||||
if (c == '(')
|
||||
{
|
||||
text.SkipChar();
|
||||
var sourcePosition = text.Start;
|
||||
text.TrimStart();
|
||||
triviaBeforeLink = new SourceSpan(sourcePosition, text.Start - 1);
|
||||
var pos = text.Start;
|
||||
if (TryParseUrlTrivia(ref text, out link, out urlHasPointyBrackets))
|
||||
{
|
||||
linkSpan.Start = pos;
|
||||
linkSpan.End = text.Start - 1;
|
||||
unescapedLink.Start = pos + (urlHasPointyBrackets ? 1 : 0);
|
||||
unescapedLink.End = text.Start - 1 - (urlHasPointyBrackets ? 1 : 0);
|
||||
if (linkSpan.End < linkSpan.Start)
|
||||
{
|
||||
linkSpan = SourceSpan.Empty;
|
||||
}
|
||||
|
||||
int triviaStart = text.Start;
|
||||
text.TrimStart(out int spaceCount);
|
||||
|
||||
triviaAfterLink = new SourceSpan(triviaStart, text.Start - 1);
|
||||
var hasWhiteSpaces = spaceCount > 0;
|
||||
|
||||
c = text.CurrentChar;
|
||||
if (c == ')')
|
||||
{
|
||||
isValid = true;
|
||||
}
|
||||
else if (hasWhiteSpaces)
|
||||
{
|
||||
c = text.CurrentChar;
|
||||
pos = text.Start;
|
||||
if (c == ')')
|
||||
{
|
||||
isValid = true;
|
||||
}
|
||||
else if (TryParseTitleTrivia(ref text, out title, out titleEnclosingCharacter))
|
||||
{
|
||||
titleSpan.Start = pos;
|
||||
titleSpan.End = text.Start - 1;
|
||||
unescapedTitle.Start = pos + 1; // skip opening character
|
||||
unescapedTitle.End = text.Start - 1 - 1; // skip closing character
|
||||
if (titleSpan.End < titleSpan.Start)
|
||||
{
|
||||
titleSpan = SourceSpan.Empty;
|
||||
}
|
||||
var startTrivia = text.Start;
|
||||
text.TrimStart();
|
||||
triviaAfterTitle = new SourceSpan(startTrivia, text.Start - 1);
|
||||
c = text.CurrentChar;
|
||||
|
||||
if (c == ')')
|
||||
{
|
||||
isValid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
// Skip ')'
|
||||
text.SkipChar();
|
||||
title ??= string.Empty;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public static bool TryParseTitle<T>(ref T text, out string title) where T : ICharIterator
|
||||
public static bool TryParseTitle<T>(T text, out string? title) where T : ICharIterator
|
||||
{
|
||||
return TryParseTitle(ref text, out title, out _);
|
||||
}
|
||||
|
||||
public static bool TryParseTitle<T>(ref T text, out string? title, out char enclosingCharacter) where T : ICharIterator
|
||||
{
|
||||
bool isValid = false;
|
||||
var buffer = StringBuilderCache.Local();
|
||||
enclosingCharacter = '\0';
|
||||
|
||||
// a sequence of zero or more characters between straight double-quote characters ("), including a " character only if it is backslash-escaped, or
|
||||
// a sequence of zero or more characters between straight single-quote characters ('), including a ' character only if it is backslash-escaped, or
|
||||
var c = text.CurrentChar;
|
||||
if (c == '\'' || c == '"' || c == '(')
|
||||
{
|
||||
enclosingCharacter = c;
|
||||
var closingQuote = c == '(' ? ')' : c;
|
||||
bool hasEscape = false;
|
||||
// -1: undefined
|
||||
@@ -439,7 +548,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
c = text.NextChar();
|
||||
|
||||
if (c == '\n')
|
||||
if (c == '\r' || c == '\n')
|
||||
{
|
||||
if (hasOnlyWhiteSpacesSinceLastLine >= 0)
|
||||
{
|
||||
@@ -449,6 +558,12 @@ namespace Markdig.Helpers
|
||||
}
|
||||
hasOnlyWhiteSpacesSinceLastLine = -1;
|
||||
}
|
||||
buffer.Append(c);
|
||||
if (c == '\r' && text.PeekChar() == '\n')
|
||||
{
|
||||
buffer.Append('\n');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\0')
|
||||
@@ -466,7 +581,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
|
||||
// Skip last quote
|
||||
text.NextChar();
|
||||
text.SkipChar();
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
@@ -491,7 +606,7 @@ namespace Markdig.Helpers
|
||||
hasOnlyWhiteSpacesSinceLastLine = 1;
|
||||
}
|
||||
}
|
||||
else if (c != '\n')
|
||||
else if (c != '\n' && c != '\r' && (c != '\r' && text.PeekChar() != '\n'))
|
||||
{
|
||||
hasOnlyWhiteSpacesSinceLastLine = 0;
|
||||
}
|
||||
@@ -505,15 +620,110 @@ namespace Markdig.Helpers
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public static bool TryParseUrl<T>(T text, out string link) where T : ICharIterator
|
||||
{
|
||||
return TryParseUrl(ref text, out link);
|
||||
}
|
||||
|
||||
public static bool TryParseUrl<T>(ref T text, out string link, bool isAutoLink = false) where T : ICharIterator
|
||||
public static bool TryParseTitleTrivia<T>(ref T text, out string? title, out char enclosingCharacter) where T : ICharIterator
|
||||
{
|
||||
bool isValid = false;
|
||||
var buffer = StringBuilderCache.Local();
|
||||
enclosingCharacter = '\0';
|
||||
|
||||
// a sequence of zero or more characters between straight double-quote characters ("), including a " character only if it is backslash-escaped, or
|
||||
// a sequence of zero or more characters between straight single-quote characters ('), including a ' character only if it is backslash-escaped, or
|
||||
var c = text.CurrentChar;
|
||||
if (c == '\'' || c == '"' || c == '(')
|
||||
{
|
||||
enclosingCharacter = c;
|
||||
var closingQuote = c == '(' ? ')' : c;
|
||||
bool hasEscape = false;
|
||||
// -1: undefined
|
||||
// 0: has only spaces
|
||||
// 1: has other characters
|
||||
int hasOnlyWhiteSpacesSinceLastLine = -1;
|
||||
while (true)
|
||||
{
|
||||
c = text.NextChar();
|
||||
|
||||
if (c == '\r' || c == '\n')
|
||||
{
|
||||
if (hasOnlyWhiteSpacesSinceLastLine >= 0)
|
||||
{
|
||||
if (hasOnlyWhiteSpacesSinceLastLine == 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
hasOnlyWhiteSpacesSinceLastLine = -1;
|
||||
}
|
||||
buffer.Append(c);
|
||||
if (c == '\r' && text.PeekChar() == '\n')
|
||||
{
|
||||
buffer.Append('\n');
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == closingQuote)
|
||||
{
|
||||
if (hasEscape)
|
||||
{
|
||||
buffer.Append(closingQuote);
|
||||
hasEscape = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip last quote
|
||||
text.SkipChar();
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasEscape && !c.IsAsciiPunctuation())
|
||||
{
|
||||
buffer.Append('\\');
|
||||
}
|
||||
|
||||
if (c == '\\')
|
||||
{
|
||||
hasEscape = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
hasEscape = false;
|
||||
|
||||
if (c.IsSpaceOrTab())
|
||||
{
|
||||
if (hasOnlyWhiteSpacesSinceLastLine < 0)
|
||||
{
|
||||
hasOnlyWhiteSpacesSinceLastLine = 1;
|
||||
}
|
||||
}
|
||||
else if (c != '\n' && c != '\r' && (c != '\r' && text.PeekChar() != '\n'))
|
||||
{
|
||||
hasOnlyWhiteSpacesSinceLastLine = 0;
|
||||
}
|
||||
|
||||
buffer.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
title = isValid ? buffer.ToString() : null;
|
||||
buffer.Length = 0;
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public static bool TryParseUrl<T>(T text, [NotNullWhen(true)] out string? link) where T : ICharIterator
|
||||
{
|
||||
return TryParseUrl(ref text, out link, out _);
|
||||
}
|
||||
|
||||
public static bool TryParseUrl<T>(ref T text, [NotNullWhen(true)] out string? link, out bool hasPointyBrackets, bool isAutoLink = false) where T : ICharIterator
|
||||
{
|
||||
bool isValid = false;
|
||||
hasPointyBrackets = false;
|
||||
var buffer = StringBuilderCache.Local();
|
||||
|
||||
var c = text.CurrentChar;
|
||||
|
||||
@@ -527,7 +737,8 @@ namespace Markdig.Helpers
|
||||
c = text.NextChar();
|
||||
if (!hasEscape && c == '>')
|
||||
{
|
||||
text.NextChar();
|
||||
text.SkipChar();
|
||||
hasPointyBrackets = true;
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
@@ -548,7 +759,7 @@ namespace Markdig.Helpers
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c.IsNewLine())
|
||||
if (c.IsNewLineOrLineFeed())
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -648,6 +859,152 @@ namespace Markdig.Helpers
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public static bool TryParseUrlTrivia<T>(ref T text, out string? link, out bool hasPointyBrackets, bool isAutoLink = false) where T : ICharIterator
|
||||
{
|
||||
bool isValid = false;
|
||||
hasPointyBrackets = false;
|
||||
var buffer = StringBuilderCache.Local();
|
||||
var unescaped = new StringBuilder();
|
||||
|
||||
var c = text.CurrentChar;
|
||||
|
||||
// a sequence of zero or more characters between an opening < and a closing >
|
||||
// that contains no line breaks, or unescaped < or > characters, or
|
||||
if (c == '<')
|
||||
{
|
||||
bool hasEscape = false;
|
||||
do
|
||||
{
|
||||
c = text.NextChar();
|
||||
if (!hasEscape && c == '>')
|
||||
{
|
||||
text.SkipChar();
|
||||
hasPointyBrackets = true;
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasEscape && c == '<')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasEscape && !c.IsAsciiPunctuation())
|
||||
{
|
||||
buffer.Append('\\');
|
||||
unescaped.Append('\\');
|
||||
}
|
||||
|
||||
if (c == '\\')
|
||||
{
|
||||
hasEscape = true;
|
||||
unescaped.Append('\\');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c.IsNewLineOrLineFeed())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
hasEscape = false;
|
||||
|
||||
buffer.Append(c);
|
||||
unescaped.Append(c);
|
||||
|
||||
} while (c != '\0');
|
||||
}
|
||||
else
|
||||
{
|
||||
// a nonempty sequence of characters that does not start with <, does not include ASCII space or control characters,
|
||||
// and includes parentheses only if (a) they are backslash-escaped or (b) they are part of a
|
||||
// balanced pair of unescaped parentheses that is not itself inside a balanced pair of unescaped
|
||||
// parentheses.
|
||||
bool hasEscape = false;
|
||||
int openedParent = 0;
|
||||
while (true)
|
||||
{
|
||||
// Match opening and closing parenthesis
|
||||
if (c == '(')
|
||||
{
|
||||
if (!hasEscape)
|
||||
{
|
||||
openedParent++;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == ')')
|
||||
{
|
||||
if (!hasEscape)
|
||||
{
|
||||
openedParent--;
|
||||
if (openedParent < 0)
|
||||
{
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAutoLink)
|
||||
{
|
||||
if (hasEscape && !c.IsAsciiPunctuation())
|
||||
{
|
||||
buffer.Append('\\');
|
||||
}
|
||||
|
||||
// If we have an escape
|
||||
if (c == '\\')
|
||||
{
|
||||
hasEscape = true;
|
||||
c = text.NextChar();
|
||||
unescaped.Append('\\');
|
||||
continue;
|
||||
}
|
||||
|
||||
hasEscape = false;
|
||||
}
|
||||
|
||||
if (IsEndOfUri(c, isAutoLink))
|
||||
{
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isAutoLink)
|
||||
{
|
||||
if (c == '&')
|
||||
{
|
||||
if (HtmlHelper.ScanEntity(text, out _, out _, out _) > 0)
|
||||
{
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (IsTrailingUrlStopCharacter(c) && IsEndOfUri(text.PeekChar(), true))
|
||||
{
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.Append(c);
|
||||
unescaped.Append(c);
|
||||
|
||||
c = text.NextChar();
|
||||
}
|
||||
|
||||
if (openedParent > 0)
|
||||
{
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
link = isValid ? buffer.ToString() : null;
|
||||
buffer.Length = 0;
|
||||
return isValid;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsTrailingUrlStopCharacter(char c)
|
||||
{
|
||||
@@ -712,20 +1069,13 @@ namespace Markdig.Helpers
|
||||
segmentCount - lastUnderscoreSegment >= 2; // No underscores are present in the last two segments of the domain
|
||||
}
|
||||
|
||||
public static bool TryParseLinkReferenceDefinition<T>(T text, out string label, out string url,
|
||||
out string title) where T : ICharIterator
|
||||
{
|
||||
return TryParseLinkReferenceDefinition(ref text, out label, out url, out title);
|
||||
}
|
||||
|
||||
public static bool TryParseLinkReferenceDefinition<T>(ref T text, out string label, out string url, out string title)
|
||||
where T : ICharIterator
|
||||
{
|
||||
return TryParseLinkReferenceDefinition(ref text, out label, out url, out title, out SourceSpan labelSpan, out SourceSpan urlSpan,
|
||||
out SourceSpan titleSpan);
|
||||
}
|
||||
|
||||
public static bool TryParseLinkReferenceDefinition<T>(ref T text, out string label, out string url, out string title, out SourceSpan labelSpan, out SourceSpan urlSpan, out SourceSpan titleSpan) where T : ICharIterator
|
||||
public static bool TryParseLinkReferenceDefinition<T>(ref T text,
|
||||
out string? label,
|
||||
out string? url,
|
||||
out string? title,
|
||||
out SourceSpan labelSpan,
|
||||
out SourceSpan urlSpan,
|
||||
out SourceSpan titleSpan) where T : ICharIterator
|
||||
{
|
||||
url = null;
|
||||
title = null;
|
||||
@@ -743,14 +1093,14 @@ namespace Markdig.Helpers
|
||||
label = null;
|
||||
return false;
|
||||
}
|
||||
text.NextChar(); // Skip ':'
|
||||
text.SkipChar(); // Skip ':'
|
||||
|
||||
// Skip any whitespace before the url
|
||||
text.TrimStart();
|
||||
|
||||
urlSpan.Start = text.Start;
|
||||
bool isAngleBracketsUrl = text.CurrentChar == '<';
|
||||
if (!TryParseUrl(ref text, out url) || (!isAngleBracketsUrl && string.IsNullOrEmpty(url)))
|
||||
if (!TryParseUrl(ref text, out url, out _) || (!isAngleBracketsUrl && string.IsNullOrEmpty(url)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -762,7 +1112,7 @@ namespace Markdig.Helpers
|
||||
if (c == '\'' || c == '"' || c == '(')
|
||||
{
|
||||
titleSpan.Start = text.Start;
|
||||
if (TryParseTitle(ref text, out title))
|
||||
if (TryParseTitle(ref text, out title, out _))
|
||||
{
|
||||
titleSpan.End = text.Start - 1;
|
||||
// If we have a title, it requires a whitespace after the url
|
||||
@@ -778,7 +1128,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
else
|
||||
{
|
||||
if (text.CurrentChar == '\0' || newLineCount > 0)
|
||||
if (text.IsEmpty || newLineCount > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -811,27 +1161,188 @@ namespace Markdig.Helpers
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryParseLabel<T>(T lines, out string label) where T : ICharIterator
|
||||
public static bool TryParseLinkReferenceDefinitionTrivia<T>(
|
||||
ref T text,
|
||||
out SourceSpan triviaBeforeLabel,
|
||||
out string? label,
|
||||
out SourceSpan labelWithTrivia,
|
||||
out SourceSpan triviaBeforeUrl, // can contain newline
|
||||
out string? url,
|
||||
out SourceSpan unescapedUrl,
|
||||
out bool urlHasPointyBrackets,
|
||||
out SourceSpan triviaBeforeTitle, // can contain newline
|
||||
out string? title, // can contain non-consecutive newlines
|
||||
out SourceSpan unescapedTitle,
|
||||
out char titleEnclosingCharacter,
|
||||
out NewLine newLine,
|
||||
out SourceSpan triviaAfterTitle,
|
||||
out SourceSpan labelSpan,
|
||||
out SourceSpan urlSpan,
|
||||
out SourceSpan titleSpan) where T : ICharIterator
|
||||
{
|
||||
labelWithTrivia = SourceSpan.Empty;
|
||||
triviaBeforeUrl = SourceSpan.Empty;
|
||||
url = null;
|
||||
unescapedUrl = SourceSpan.Empty;
|
||||
triviaBeforeTitle = SourceSpan.Empty;
|
||||
title = null;
|
||||
unescapedTitle = SourceSpan.Empty;
|
||||
newLine = NewLine.None;
|
||||
|
||||
urlSpan = SourceSpan.Empty;
|
||||
titleSpan = SourceSpan.Empty;
|
||||
|
||||
text.TrimStart();
|
||||
triviaBeforeLabel = new SourceSpan(0, text.Start - 1);
|
||||
triviaAfterTitle = SourceSpan.Empty;
|
||||
urlHasPointyBrackets = false;
|
||||
titleEnclosingCharacter = '\0';
|
||||
|
||||
labelWithTrivia.Start = text.Start + 1; // skip opening [
|
||||
if (!TryParseLabelTrivia(ref text, out label, out labelSpan))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
labelWithTrivia.End = text.Start - 2; // skip closing ] and subsequent :
|
||||
|
||||
if (text.CurrentChar != ':')
|
||||
{
|
||||
label = null;
|
||||
return false;
|
||||
}
|
||||
text.SkipChar(); // Skip ':'
|
||||
var triviaBeforeUrlStart = text.Start;
|
||||
|
||||
// Skip any whitespace before the url
|
||||
text.TrimStart();
|
||||
triviaBeforeUrl = new SourceSpan(triviaBeforeUrlStart, text.Start - 1);
|
||||
|
||||
urlSpan.Start = text.Start;
|
||||
bool isAngleBracketsUrl = text.CurrentChar == '<';
|
||||
unescapedUrl.Start = text.Start + (isAngleBracketsUrl ? 1 : 0);
|
||||
if (!TryParseUrlTrivia(ref text, out url, out urlHasPointyBrackets) || (!isAngleBracketsUrl && string.IsNullOrEmpty(url)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
urlSpan.End = text.Start - 1;
|
||||
unescapedUrl.End = text.Start - 1 - (isAngleBracketsUrl ? 1 : 0);
|
||||
int triviaBeforeTitleStart = text.Start;
|
||||
|
||||
var saved = text;
|
||||
var hasWhiteSpaces = CharIteratorHelper.TrimStartAndCountNewLines(ref text, out int newLineCount, out newLine);
|
||||
|
||||
// Remove the newline from the trivia (as it may have multiple lines)
|
||||
var triviaBeforeTitleEnd = text.Start - 1;
|
||||
triviaBeforeTitle = new SourceSpan(triviaBeforeTitleStart, triviaBeforeTitleEnd);
|
||||
var c = text.CurrentChar;
|
||||
if (c == '\'' || c == '"' || c == '(')
|
||||
{
|
||||
titleSpan.Start = text.Start;
|
||||
unescapedTitle.Start = text.Start + 1; // + 1; // skip opening enclosing character
|
||||
if (TryParseTitleTrivia(ref text, out title, out titleEnclosingCharacter))
|
||||
{
|
||||
titleSpan.End = text.Start - 1;
|
||||
unescapedTitle.End = text.Start - 1 - 1; // skip closing enclosing character
|
||||
// If we have a title, it requires a whitespace after the url
|
||||
if (!hasWhiteSpaces)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Discard the newline if we have a title
|
||||
newLine = NewLine.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (text.IsEmpty || newLineCount > 0)
|
||||
{
|
||||
// If we have an end of line, we need to remove it from the trivia
|
||||
triviaBeforeTitle.End -= newLine.Length();
|
||||
triviaAfterTitle = new SourceSpan(text.Start, text.Start - 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the current line has only trailing spaces
|
||||
c = text.CurrentChar;
|
||||
int triviaAfterTitleStart = text.Start;
|
||||
while (c.IsSpaceOrTab())
|
||||
{
|
||||
c = text.NextChar();
|
||||
}
|
||||
|
||||
if (c != '\0' && c != '\n' && c != '\r' && (c != '\r' && text.PeekChar() != '\n'))
|
||||
{
|
||||
// If we were able to parse the url but the title doesn't end with space,
|
||||
// we are still returning a valid definition
|
||||
if (newLineCount > 0 && title != null)
|
||||
{
|
||||
text = saved;
|
||||
title = null;
|
||||
newLine = NewLine.None;
|
||||
unescapedTitle = SourceSpan.Empty;
|
||||
triviaAfterTitle = SourceSpan.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
label = null;
|
||||
url = null;
|
||||
unescapedUrl = SourceSpan.Empty;
|
||||
title = null;
|
||||
unescapedTitle = SourceSpan.Empty;
|
||||
return false;
|
||||
}
|
||||
triviaAfterTitle = new SourceSpan(triviaAfterTitleStart, text.Start - 1);
|
||||
if (c != '\0')
|
||||
{
|
||||
if (c == '\n')
|
||||
{
|
||||
newLine = NewLine.LineFeed;
|
||||
}
|
||||
else if (c == '\r' && text.PeekChar() == '\n')
|
||||
{
|
||||
newLine = NewLine.CarriageReturnLineFeed;
|
||||
}
|
||||
else if (c == '\r')
|
||||
{
|
||||
newLine = NewLine.CarriageReturn;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryParseLabel<T>(T lines, [NotNullWhen(true)] out string? label) where T : ICharIterator
|
||||
{
|
||||
return TryParseLabel(ref lines, false, out label, out SourceSpan labelSpan);
|
||||
}
|
||||
|
||||
public static bool TryParseLabel<T>(T lines, out string label, out SourceSpan labelSpan) where T : ICharIterator
|
||||
public static bool TryParseLabel<T>(T lines, [NotNullWhen(true)] out string? label, out SourceSpan labelSpan) where T : ICharIterator
|
||||
{
|
||||
return TryParseLabel(ref lines, false, out label, out labelSpan);
|
||||
}
|
||||
|
||||
public static bool TryParseLabel<T>(ref T lines, out string label) where T : ICharIterator
|
||||
public static bool TryParseLabel<T>(ref T lines, [NotNullWhen(true)] out string? label) where T : ICharIterator
|
||||
{
|
||||
return TryParseLabel(ref lines, false, out label, out SourceSpan labelSpan);
|
||||
}
|
||||
|
||||
public static bool TryParseLabel<T>(ref T lines, out string label, out SourceSpan labelSpan) where T : ICharIterator
|
||||
public static bool TryParseLabel<T>(ref T lines, [NotNullWhen(true)] out string? label, out SourceSpan labelSpan) where T : ICharIterator
|
||||
{
|
||||
return TryParseLabel(ref lines, false, out label, out labelSpan);
|
||||
}
|
||||
|
||||
public static bool TryParseLabel<T>(ref T lines, bool allowEmpty, out string label, out SourceSpan labelSpan) where T : ICharIterator
|
||||
public static bool TryParseLabelTrivia<T>(ref T lines, [NotNullWhen(true)] out string? label, out SourceSpan labelSpan) where T : ICharIterator
|
||||
{
|
||||
return TryParseLabelTrivia(ref lines, false, out label, out labelSpan);
|
||||
}
|
||||
|
||||
public static bool TryParseLabel<T>(ref T lines, bool allowEmpty, [NotNullWhen(true)] out string? label, out SourceSpan labelSpan) where T : ICharIterator
|
||||
{
|
||||
label = null;
|
||||
char c = lines.CurrentChar;
|
||||
@@ -873,7 +1384,7 @@ namespace Markdig.Helpers
|
||||
|
||||
if (c == ']')
|
||||
{
|
||||
lines.NextChar(); // Skip ]
|
||||
lines.SkipChar(); // Skip ]
|
||||
if (allowEmpty || hasNonWhiteSpace)
|
||||
{
|
||||
// Remove trailing spaces
|
||||
@@ -945,5 +1456,125 @@ namespace Markdig.Helpers
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public static bool TryParseLabelTrivia<T>(ref T lines, bool allowEmpty, out string? label, out SourceSpan labelSpan) where T : ICharIterator
|
||||
{
|
||||
label = null;
|
||||
char c = lines.CurrentChar;
|
||||
labelSpan = SourceSpan.Empty;
|
||||
if (c != '[')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var buffer = StringBuilderCache.Local();
|
||||
|
||||
var startLabel = -1;
|
||||
var endLabel = -1;
|
||||
|
||||
bool hasEscape = false;
|
||||
bool previousWhitespace = true;
|
||||
bool hasNonWhiteSpace = false;
|
||||
bool isValid = false;
|
||||
while (true)
|
||||
{
|
||||
c = lines.NextChar();
|
||||
if (c == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (hasEscape)
|
||||
{
|
||||
if (c != '[' && c != ']' && c != '\\')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c == '[')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == ']')
|
||||
{
|
||||
lines.SkipChar(); // Skip ]
|
||||
if (allowEmpty || hasNonWhiteSpace)
|
||||
{
|
||||
// Remove trailing spaces
|
||||
for (int i = buffer.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (!buffer[i].IsWhitespace())
|
||||
{
|
||||
break;
|
||||
}
|
||||
buffer.Length = i;
|
||||
endLabel--;
|
||||
}
|
||||
|
||||
// Only valid if buffer is less than 1000 characters
|
||||
if (buffer.Length <= 999)
|
||||
{
|
||||
labelSpan.Start = startLabel;
|
||||
labelSpan.End = endLabel;
|
||||
if (labelSpan.Start > labelSpan.End)
|
||||
{
|
||||
labelSpan = SourceSpan.Empty;
|
||||
}
|
||||
|
||||
label = buffer.ToString();
|
||||
isValid = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var isWhitespace = c.IsWhitespace();
|
||||
|
||||
|
||||
if (!hasEscape && c == '\\')
|
||||
{
|
||||
if (startLabel < 0)
|
||||
{
|
||||
startLabel = lines.Start;
|
||||
}
|
||||
hasEscape = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasEscape = false;
|
||||
|
||||
if (!previousWhitespace || !isWhitespace)
|
||||
{
|
||||
if (startLabel < 0)
|
||||
{
|
||||
startLabel = lines.Start;
|
||||
}
|
||||
endLabel = lines.Start;
|
||||
if (isWhitespace)
|
||||
{
|
||||
// Replace any whitespace by a single ' '
|
||||
buffer.Append(' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.Append(c);
|
||||
}
|
||||
if (!isWhitespace)
|
||||
{
|
||||
hasNonWhiteSpace = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
previousWhitespace = isWhitespace;
|
||||
}
|
||||
|
||||
buffer.Length = 0;
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
35
src/Markdig/Helpers/Newline.cs
Normal file
35
src/Markdig/Helpers/Newline.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a character or set of characters that represent a separation
|
||||
/// between two lines of text
|
||||
/// </summary>
|
||||
public enum NewLine : byte
|
||||
{
|
||||
// Values have the length encoded in last 2 bits
|
||||
None = 0,
|
||||
CarriageReturn = 4 | 1,
|
||||
LineFeed = 8 | 1,
|
||||
CarriageReturnLineFeed = 16 | 2
|
||||
}
|
||||
|
||||
public static class NewLineExtensions
|
||||
{
|
||||
public static string AsString(this NewLine newLine) => newLine switch
|
||||
{
|
||||
NewLine.CarriageReturnLineFeed => "\r\n",
|
||||
NewLine.LineFeed => "\n",
|
||||
NewLine.CarriageReturn => "\r",
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
public static int Length(this NewLine newLine) => (int)newLine & 3;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user