Compare commits

...

232 Commits

Author SHA1 Message Date
Alexandre Mutel
64875c3dd9 Bump to 0.25.0 2021-06-10 09:02:03 +02:00
Alexandre Mutel
4c92fe5a3b Fix regression when parsing link reference definitions (#543) 2021-06-10 08:58:53 +02:00
Alexandre Mutel
27f625f15b Merge pull request #548 from Mysteryduck001/master
JiraLinkInlineParser.cs - Make digits in JiraKey's posible
2021-05-08 12:01:32 +02:00
Mysteryduck001
aca085703e JiraLinks.Generated.cs - add test for JiraKey's starting with a digit (not allowed) 2021-05-03 17:28:11 +02:00
Mysteryduck001
8aa0948b20 JiraLinkInlineParser.cs - prevent Jira key's to start with a digit 2021-05-03 17:22:13 +02:00
Mysteryduck001
8ce6f4d9ad JiraLinks.md - Do not allow a digit to be the first Char of the Key 2021-05-03 17:18:36 +02:00
Mysteryduck001
3a47a5115a JiraLinks.generated.cs - updated tests for JiraLink 2021-05-03 17:14:25 +02:00
Mysteryduck001
a8737e8481 JiraLinks.md - add examples with JiraKeys using Digits 2021-05-03 17:03:31 +02:00
Mysteryduck001
f56b8e6ba7 JiraLinkInlineParser.cs - Make digits in JiraKey's posible 2021-05-03 16:32:08 +02:00
Alexandre Mutel
0a0040450f Merge pull request #544 from generateui/fix_535
ignore whitespace at end of pipe table row header
2021-04-14 16:38:40 +02:00
Alexandre Mutel
fb12be5ab0 Merge pull request #540 from MihaZupan/coverage-link
Update coverage badge link
2021-04-13 07:35:27 +02:00
Ruud Poutsma
2277596e2e ignore whitespace at end of pipe table row header 2021-04-10 17:59:32 +02:00
MihaZupan
a10f6f6b8c Update coverage badge link 2021-04-07 00:18:57 +02:00
Alexandre Mutel
203cfd6508 Bump to 0.24.0 2021-03-20 08:47:20 +01:00
Alexandre Mutel
5fefcbb5b3 Merge pull request #530 from carbon/cq
Improve Code Quality
2021-03-15 20:40:54 +01:00
Alexandre Mutel
7ad7b55c48 Merge pull request #529 from MihaZupan/cache-parsers
Cache the MarkdownParser & Processors
2021-03-15 06:34:36 +01:00
Alexandre Mutel
e9ea103e32 Merge pull request #532 from MihaZupan/more-allocs
Avoid char[] & string allocations in HtmlBlockParser
2021-03-15 06:33:02 +01:00
Alexandre Mutel
c680910828 Merge pull request #531 from MihaZupan/random-perf-3
A handful of codegen improvements
2021-03-15 06:32:26 +01:00
Jason Nelson
4526d886c8 Use WriteLine char overload 2021-03-14 09:24:09 -07:00
Jason Nelson
397de86e2f Remove obvious parameter label 2021-03-14 09:15:41 -07:00
MihaZupan
f985750b82 Don't use the Roundtrip InfoParser by default 2021-03-14 11:21:34 +01:00
MihaZupan
00c175a79c Remove char[], string allocations in HtmlBlockParser 2021-03-14 11:21:14 +01:00
MihaZupan
6204095261 Remove static ctor from CharNormalizer 2021-03-14 04:13:27 +01:00
MihaZupan
63fddf4511 Add quick range check in CharNormalizer 2021-03-14 04:10:58 +01:00
MihaZupan
c964659085 Optimize Globalization StartsWithRtlCharacter check 2021-03-14 03:56:36 +01:00
MihaZupan
eabfe74e92 Avoid struct copy in Roundtrip CodeBlockRenderer 2021-03-14 03:17:59 +01:00
MihaZupan
bd1dcd952c Improve HtmlHelper 2021-03-14 03:06:47 +01:00
MihaZupan
54e2514778 Avoid struct copies in StringLineGroup 2021-03-14 02:31:08 +01:00
MihaZupan
4b7a4d21de Optimize NewLine.Length() 2021-03-14 02:07:45 +01:00
MihaZupan
6b1399ba23 Use IsEmpty instead of Length == 0 or CurrentChar == '\0' 2021-03-14 02:00:37 +01:00
MihaZupan
07467d6c30 Add ICharIterator SkipChar and PeekChar
PeekChar() already existed on StringSlice, but not on the interface, so it wasn't always getting used
2021-03-14 01:50:56 +01:00
MihaZupan
2f9588498c Add missing null check 2021-03-13 20:44:55 +01:00
MihaZupan
80ed85e2a8 Cache the MarkdownParser & Processors 2021-03-13 20:41:35 +01:00
Jason Nelson
b2306db388 Make CodeInline.Content nonnull 2021-03-12 11:31:11 -08:00
Jason Nelson
8db238797b Enable nullable on AutolinkInline 2021-03-12 11:29:53 -08:00
Jason Nelson
780e16a9c9 Simplify list initialization 2021-03-12 11:28:05 -08:00
Jason Nelson
8fdc0d59d7 Format TableState 2021-03-12 11:21:15 -08:00
Jason Nelson
f1cd0cb1b8 Enable nullable on CharacterMap 2021-03-12 11:20:57 -08:00
Jason Nelson
a724783e3f CodeBlockLines is always defined. Remove lazy initializer 2021-03-12 11:20:42 -08:00
Jason Nelson
1c862a1e07 Remove unused using statement 2021-03-12 11:10:17 -08:00
Jason Nelson
35d3160ad2 Eliminate a few allocations to hold indents 2021-03-12 11:09:53 -08:00
Jason Nelson
e0a2f9e52d Add notes on why we continue to support net452 2021-03-12 11:05:54 -08:00
Jason Nelson
c9ba236dbc Write chars, instead of strings, where possible 2021-03-12 11:01:06 -08:00
Jason Nelson
0c9b5dddc9 Use is (not) pattern to match constant strings 2021-03-12 10:50:18 -08:00
Jason Nelson
bd2bb98631 Compare strings using StringComparison.Ordinal 2021-03-12 10:49:26 -08:00
Alexandre Mutel
da0ba34165 Merge pull request #527 from MihaZupan/last-nullable
A few nullable changes
2021-03-12 06:08:54 +01:00
MihaZupan
51c5bec315 A few nullable changes 2021-03-11 20:56:55 +01:00
Alexandre Mutel
13bdab4570 Merge pull request #526 from carbon/nullability2
Complete nullable annotations
2021-03-11 20:20:50 +01:00
Jason Nelson
6b9433c7d8 Remove temporary #nullable enable directives 2021-03-11 10:41:14 -08:00
Jason Nelson
eedbb494fc Revert accidental readonly removal 2021-03-11 10:27:02 -08:00
Jason Nelson
9d36a74312 Replace == null with is null 2021-03-10 22:45:49 -08:00
Jason Nelson
4009c89321 Enable nullable at project level and add remaining nullable annotations 2021-03-10 22:44:42 -08:00
Alexandre Mutel
6b433d9352 Merge pull request #525 from carbon/nullability2
Enable nullability, round 3 of x
2021-03-11 07:05:12 +01:00
Jason Nelson
f6e6001d94 Remove unnecessary suppression 2021-03-10 18:18:41 -08:00
Jason Nelson
1474b7b29a Fix default NewLine 2021-03-10 15:28:31 -08:00
Jason Nelson
61b29b6d41 Fix incorrect logic 2021-03-10 15:20:43 -08:00
Jason Nelson
6684c8257c Enable nullable, round 3 2021-03-10 15:12:30 -08:00
Alexandre Mutel
247cd92926 Merge pull request #524 from carbon/nullability2
Enable nullable annotations, round 2 of x
2021-03-10 22:55:24 +01:00
Jason Nelson
404a94f284 Fix nullability in MarkdownParser 2021-03-10 10:01:05 -08:00
Jason Nelson
1cc8a40473 Use pattern matching to test for non-empty collections 2021-03-10 09:39:12 -08:00
Jason Nelson
dc4968d5ab Reapply nullable annotations after merge 2021-03-10 09:32:02 -08:00
Jason Nelson
b2b36038ff Merge remote-tracking branch 'upstream/master' into nullability2 2021-03-10 09:27:12 -08:00
Alexandre Mutel
4a57035aec Merge pull request #521 from MihaZupan/random-perf-2
Random perf improvements
2021-03-10 07:27:10 +01:00
Jason Nelson
1752178631 Make attachedDatas nullable 2021-03-09 17:07:02 -08:00
Jason Nelson
39c05f34d1 Enable nullable annotations, round 2 of x 2021-03-09 16:55:13 -08:00
Alexandre Mutel
640196a18f Merge pull request #520 from MihaZupan/cleanup
Some minor cleanup
2021-03-09 07:55:19 +01:00
MihaZupan
4f9119fc96 Merge master 2021-03-09 07:54:20 +01:00
MihaZupan
5eb600afc3 Expose TrackTrivia on PipelineBuilder as get-only 2021-03-09 07:35:49 +01:00
Alexandre Mutel
64ebff4012 Merge pull request #519 from grishat/document-to-html
ADD: Markdown.ToHtml from MarkdownDocument
2021-03-09 07:13:49 +01:00
Alexandre Mutel
9e5d30cd4c Merge pull request #522 from carbon/master
Enable nullability
2021-03-09 07:13:18 +01:00
Alexandre Mutel
4324caaaea Update src/Markdig/Markdown.cs 2021-03-09 07:08:15 +01:00
Jason Nelson
46c2d49243 Begin nullability enablement 2021-03-08 11:43:02 -08:00
MihaZupan
80b1cf6020 Rename NoBlockFoundBlockRenderer.cs => EmptyBlockRenderer.cs 2021-03-08 12:19:43 +01:00
MihaZupan
264f7f2132 Update Roundtrip.md 2021-03-08 12:18:12 +01:00
MihaZupan
80ec8da7d3 Add missing license headers 2021-03-08 11:54:43 +01:00
MihaZupan
1e2399669d Fix most build warnings 2021-03-08 11:37:29 +01:00
MihaZupan
ab53969f06 Revert "Fix some build warnings"
This reverts commit 325fb7158e.
2021-03-08 11:16:31 +01:00
MihaZupan
168217b4e0 Expose TrackTrivia on Pipeline as get-only 2021-03-08 11:12:55 +01:00
MihaZupan
a3ce1903c1 Cache renderers for custom writers 2021-03-07 21:28:39 +01:00
MihaZupan
db1021a979 Avoid minor allocations in ProcessInlines loop 2021-03-07 20:08:04 +01:00
MihaZupan
cbd00a45af Remove test dependency on source file's line endings
This test uses '@' strings so if source files are checked out with different line endings, it will fail
2021-03-07 19:53:57 +01:00
MihaZupan
fef4719e41 Don't change Writer's NewLine 2021-03-07 19:29:17 +01:00
MihaZupan
ae25a8f12c Reduce the size of MarkdownObject by 1 pointer size
Since this is the base type of every node on the AST, it amounts to ~3% allocated bytes reduction
2021-03-07 19:19:03 +01:00
MihaZupan
bb5403c795 Hide TrackTrivia from PipelineBuilder
EnableTrackTrivia is exposed to enable trivia
2021-03-07 19:13:34 +01:00
MihaZupan
325fb7158e Fix some build warnings 2021-03-07 18:59:23 +01:00
MihaZupan
67416e4b45 Hide TrackTrivia from Pipeline 2021-03-07 18:52:41 +01:00
Alexandre Mutel
8b48accb7e Merge pull request #481 from generateui/cst
Roundtrip implementation
2021-03-07 16:18:35 +01:00
Ruud Poutsma
bb42ee42ca code review feedback 2021-03-07 16:14:48 +01:00
Rishat Gildanov
d17660fe5d ADD: Markdown.ToHtml from MarkdownDocument (already parsed Markdown text) 2021-03-07 17:39:43 +05:00
Ruud Poutsma
19e409ceca correctly calculate newlines in StringLineGroup PeekChar 2021-03-04 23:28:37 +01:00
Ruud Poutsma
59df6d1a2e primarily define TrackTrivia on MarkdownPipeline 2021-03-04 22:57:57 +01:00
Ruud Poutsma
763ed32212 NewLine renaming remnants 2021-03-04 22:42:48 +01:00
Ruud Poutsma
8aa522c4bf Merge branch 'cst' of https://github.com/generateui/markdig into cst 2021-03-04 22:36:00 +01:00
Ruud Poutsma
9031be96f8 rename Newline to NewLine 2021-03-04 22:35:48 +01:00
Ruud Poutsma
4bc2e847d5 PR feedback 2021-03-04 22:04:26 +01:00
Alexandre Mutel
80d4e6f344 Fix StringSlice overlaps
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
2021-03-03 05:32:28 +01:00
Ruud Poutsma
b6e38a0a96 fix ContainerBlocksBlockQuotes_Example210 2021-03-01 23:26:55 +01:00
Ruud Poutsma
1d79ad8436 rerun specFileGen 2021-03-01 23:11:31 +01:00
Ruud Poutsma
383b197490 document roundtrip parsing feature 2021-03-01 23:07:40 +01:00
Ruud Poutsma
7aeee0681f rename Whitespace* member names to Trvia* member names 2021-03-01 21:12:51 +01:00
Ruud Poutsma
e017adae84 remove TODO: RTPs 2021-02-27 23:38:48 +01:00
Ruud Poutsma
7035aed74a Refactor Newline struct into enum 2021-02-27 23:32:09 +01:00
Ruud Poutsma
21b6a3869a implement NoBlocksFoundBlock, fix null character tests 2021-02-27 22:00:43 +01:00
Ruud Poutsma
aa92e59b2c merge from master 2021-02-27 21:25:44 +01:00
Alexandre Mutel
5f87e56651 Merge pull request #518 from grishat/master
FIX: Markdown.ToHtml doesnt get MarkdownParserContext parameter
2021-02-26 08:12:34 +01:00
Rishat Gildanov
9934c0033a FIX: Markdown.ToHtml doesnt get MarkdownParserContext parameter 2021-02-26 10:55:59 +05:00
Alexandre Mutel
5d1da2deac Merge pull request #511 from AndreyChechel/issue-508
Expose `TextRendererBase.Reset` method to inheritors
2021-02-17 20:57:48 +01:00
Andrey Chechel
4e424c43fb Expose TextRendererBase.Reset method to inheritors 2021-02-09 17:27:02 +03:00
Alexandre Mutel
3030b72f78 Bump to 0.23.0 2021-01-16 08:35:47 +01:00
Alexandre Mutel
d1784676b2 Rename AutolineInlineParser to AutolinkInlineParser 2021-01-16 08:34:45 +01:00
Alexandre Mutel
47e49f0a4f Add test documentation for root namespace 2021-01-16 08:28:20 +01:00
Alexandre Mutel
0bfc456509 Merge pull request #502 from yufeih/code-inline-plain-text
Fix CodeInline to plain text incorrect escape
2021-01-14 20:33:17 +01:00
Alexandre Mutel
8e14997bc1 Merge pull request #500 from MihaZupan/depth-limit
Add depth limits to avoid pathological-case parsing times/StackOverflows
2021-01-14 20:32:00 +01:00
Yufei Huang
a8ec3c1a48 Fix CodeInline to plain text incorrect escape 2021-01-12 16:11:31 +08:00
MihaZupan
f6f617a746 Add depth limits 2021-01-07 23:16:25 +01:00
Alexandre Mutel
3d65b9f6b6 Upgrade webapp settings 2020-12-22 14:36:26 +01:00
Alexandre Mutel
5f7d93be52 Use net5.0 2020-12-22 12:30:10 +01:00
Alexandre Mutel
3ec787687c Force not to use a pre-release in global.json 2020-12-22 11:16:38 +01:00
Ruud Poutsma
9fbe5eb21d merge from lunet-io/master 2020-12-12 14:26:02 +01:00
Alexandre Mutel
e523dfd7f4 Update the logo for the NuGet package 2020-12-02 08:04:32 +01:00
Alexandre Mutel
412500158c Merge pull request #492 from ilich/master
Added a reference to WebStoating.Markdig.Prism
2020-12-02 07:56:38 +01:00
Alexandre Mutel
fc942b4d01 Update logo 2020-12-02 07:55:18 +01:00
Ilya Verbitskiy
d1d64842ca Added a reference to WebStoating.Markdig.Prism 2020-11-10 16:40:19 +08:00
Alexandre Mutel
930eee383f Use dotnet 3.1 for SpecFileGen 2020-11-08 11:06:16 +01:00
Alexandre Mutel
87c45db46b Use latest dotnet 3.1.x SDK 2020-11-08 10:52:17 +01:00
Ruud Poutsma
fb4abdfae3 fix broken multi-empty-line after UnorderedList and make naming of before/after consistent 2020-11-07 23:49:25 +01:00
Ruud Poutsma
7ab34d5cef fix paragraph with more then 2 newlines after 2020-11-07 18:58:37 +01:00
Ruud Poutsma
b576b08332 document MarkdownParser assigning empty lines 2020-11-07 12:46:22 +01:00
Ruud Poutsma
9c43b802bd update Roundtrip.md todo list 2020-11-07 12:38:33 +01:00
Ruud Poutsma
7dda864e4a document and cleanup 2020-11-07 12:36:07 +01:00
Ruud Poutsma
b3953dbe23 use StringSlice instead of string in InlineLink and LinkReferenceDefinition 2020-11-07 11:47:12 +01:00
Ruud Poutsma
453e8239d2 use StringSlice in IFencedBlock 2020-11-06 17:33:27 +01:00
Ruud Poutsma
8e8b95c3bb fix broken newline parsing for LineBreakInline and PipeTableParser 2020-11-06 13:27:46 +01:00
Ruud Poutsma
35b64052d6 fix whitespaces for QuoteBlock 2020-11-01 22:48:04 +01:00
Ruud Poutsma
ef495da097 fix whitespace after atx heading char 2020-11-01 20:03:13 +01:00
Ruud Poutsma
a6b9c9a41e don't trip up SpecFileGen test by placing docs in *Spec folder in .Test project 2020-11-01 17:52:26 +01:00
Ruud Poutsma
5bb2d92180 fix quoteblock > orderedlist > quoteblock rendering 2020-11-01 17:38:12 +01:00
Ruud Poutsma
929ec36c76 fix FencedCodeBlock parsing 2020-11-01 13:48:08 +01:00
Ruud Poutsma
8e17d9c94e fix newlines surrounding IndentedCodeBlock 2020-11-01 13:04:44 +01:00
Ruud Poutsma
a11cd8c28c merge from main 2020-10-31 15:48:00 +01:00
Ruud Poutsma
68d2a20d20 fix broken PeekChar() of StringLineGroup.Iterator 2020-10-31 15:38:00 +01:00
Ruud Poutsma
e3531413e1 fix broken StringSlice tests 2020-10-31 15:07:10 +01:00
Ruud Poutsma
ef534224f8 fix heading SoureceSpan bugs 2020-10-31 14:34:25 +01:00
Ruud Poutsma
2f3e1451b8 ensure to write indents when indents are added to the text renderer 2020-10-27 20:56:57 +01:00
Ruud Poutsma
a34e257d2c fix ParagraphBlockParser parsing into paragraphs instead of setext headings 2020-10-26 22:59:29 +01:00
Ruud Poutsma
9d83631cfe fix horribly broken OrderedList 2020-10-26 22:37:44 +01:00
Ruud Poutsma
61b3ffde91 better respect TrackTrivia flag 2020-10-25 21:11:37 +01:00
Ruud Poutsma
bebdf0179e respect TrackTrivia flag much more widely, restore intentionally broken behaviors 2020-10-25 20:26:56 +01:00
Ruud Poutsma
ab5e8ae9e2 apply review feedback 2020-10-25 16:12:06 +01:00
Ruud Poutsma
c8da430134 cleanup RoundtripRenderer 2020-10-25 15:48:19 +01:00
Ruud Poutsma
0e8c312fda extract RoundtripRenderer and restore NormalizeRenderer 2020-10-25 15:43:27 +01:00
Ruud Poutsma
4814e9cea5 introduce trackTrivia feature flag 2020-10-25 15:21:13 +01:00
Ruud Poutsma
068a6e7af5 check IsNewline calls 2020-10-25 14:54:52 +01:00
Ruud Poutsma
e4f2892a23 do "TODO: RTP:" todos 2020-10-25 14:52:23 +01:00
Ruud Poutsma
c2cfb05d8d fix whitespace between heading and headingchar for atx headings 2020-10-25 14:31:25 +01:00
Ruud Poutsma
813647ca10 place newline on LinkReferenceDefinition instead of leaf whitespace properties 2020-10-25 14:16:53 +01:00
Ruud Poutsma
83357dc929 fix whitespace in nested QuoteBlock 2020-10-25 13:33:06 +01:00
Ruud Poutsma
420aa79a48 fix LinkReferenceDefinition bugs 2020-10-25 13:15:46 +01:00
Ruud Poutsma
24be820827 omit LinkReferenceDefinitionGroup and insert LinkReferenceDefinitions at proper indexes of document/parent 2020-10-25 11:32:25 +01:00
Ruud Poutsma
694a764f96 fix escaped within local label of InlineLink 2020-10-24 16:18:33 +02:00
Ruud Poutsma
bb1da73da2 fix whitespace in HtmlBlock within ListItemBlock 2020-10-24 15:53:34 +02:00
Ruud Poutsma
e6b591c035 fix escapes in FencedCodeBlock info and arguments 2020-10-24 15:32:14 +02:00
Ruud Poutsma
5a19cdfebb implement ordered list item bullet rendering 2020-10-24 15:27:37 +02:00
Ruud Poutsma
34e439f494 fix whitespace rendering of FencedCodeBlock as child of ListItemBlock 2020-10-24 15:27:10 +02:00
Ruud Poutsma
b0602a7bb0 fix HtmlBlock whitespace regression 2020-10-24 14:51:09 +02:00
Ruud Poutsma
d7558f0442 fix FencedCodeBlock beforewhitespace regression 2020-10-24 14:41:35 +02:00
Ruud Poutsma
eac0ab3cca fix broken InlineLink rendering 2020-10-24 14:37:49 +02:00
Ruud Poutsma
147cd48a8d fix escapes in InlineLink regression 2020-10-24 14:29:39 +02:00
Ruud Poutsma
1b44256fc0 improve whitespace handling in FencedCodeBlock 2020-10-24 14:13:38 +02:00
Ruud Poutsma
db2856daf7 fix label rendering on LinkInline 2020-10-24 13:53:29 +02:00
Ruud Poutsma
9beb0c1613 fix whitespace on empty lines 2020-10-24 00:24:00 +02:00
Ruud Poutsma
ef343158b7 fix escapes in LinkReferenceDefinition 2020-10-24 00:11:35 +02:00
Ruud Poutsma
de5526877d fix broken AtxHeading whitespace 2020-10-23 23:31:31 +02:00
Ruud Poutsma
b00d75607b fix tabs in IndentedCodeBlock 2020-10-23 23:21:11 +02:00
Ruud Poutsma
8c37a1bef5 fix HtmlBlock whitespace before 2020-10-23 23:00:38 +02:00
Ruud Poutsma
7a9405ec9e fix ThematicBreakParser 2020-10-23 22:55:55 +02:00
Ruud Poutsma
6506e4594c fix escaped characters in LinkInline 2020-10-23 22:47:39 +02:00
Ruud Poutsma
7b20299d2b fix empty ListItem 2020-10-23 19:34:16 +02:00
Ruud Poutsma
55eaadce67 fix optional space after Atx heading leaders 2020-10-23 19:29:12 +02:00
Ruud Poutsma
87269d88b9 fix newlines after LinkReferenceDefinition 2020-10-23 18:52:48 +02:00
Ruud Poutsma
fb8162f5d7 fix whitespace surrounding closing fences of FencedCodeBlock 2020-10-23 18:28:28 +02:00
Ruud Poutsma
a04aa8a4de fix empty QuoteBlock lines 2020-10-23 17:50:07 +02:00
Ruud Poutsma
352443d9cd fix unclosed FencedCodeBlock in QuoteBlock 2020-10-23 11:32:15 +02:00
Ruud Poutsma
cd798b8d95 fix newlines after links 2020-10-18 22:40:17 +02:00
Ruud Poutsma
c5b260f708 fix rendering of empty lines with whitespace 2020-10-18 16:09:24 +02:00
Ruud Poutsma
2583463ea2 fix broken links 2020-10-18 15:05:41 +02:00
Ruud Poutsma
777ac71bd1 fix HardLineBreak, CodeInline tests 2020-10-18 14:11:47 +02:00
Ruud Poutsma
810ae49cc7 fix SetextHeading tests 2020-10-18 13:08:40 +02:00
Ruud Poutsma
225e308438 fix AtxHeading tests 2020-10-18 12:40:57 +02:00
Ruud Poutsma
05dd543d16 fix HtmlBlock tests 2020-10-18 12:17:33 +02:00
Ruud Poutsma
c711201b34 implement codegen for roundtrip parsing 2020-10-17 23:49:44 +02:00
Ruud Poutsma
47b3ac5d99 fix Autlink tests 2020-10-17 23:19:44 +02:00
Ruud Poutsma
49aa856f52 implement LinkInline 2020-10-17 23:09:53 +02:00
Ruud Poutsma
5e15575929 implement LinkReferenceDefinition 2020-10-17 15:48:36 +02:00
Ruud Poutsma
c9fc608598 update todo 2020-10-11 14:07:32 +02:00
Ruud Poutsma
6ba3c3d683 implement newline 2020-10-11 14:00:09 +02:00
Ruud Poutsma
d15edb79fa fix broken whitespace starting on newlines within a paragraph 2020-10-10 17:25:22 +02:00
Ruud Poutsma
fa3b67342d add some tests, update todolist 2020-10-10 17:10:18 +02:00
Ruud Poutsma
033f156b2b add many tests, create todolist 2020-10-10 16:37:17 +02:00
Ruud Poutsma
f3db5e882e fix FencedCodeBlock 2020-10-09 19:47:16 +02:00
Ruud Poutsma
c9365f3551 correctly render whitespace for IndentedCodeBlock 2020-10-09 18:30:03 +02:00
Ruud Poutsma
9ecb5b9950 test nested indented codeblock in quoteblock 2020-10-09 17:20:58 +02:00
Ruud Poutsma
95fb53cbc9 fix IndentedCodeBlock newlines 2020-10-09 16:44:16 +02:00
Ruud Poutsma
9d82088e03 fix quoteblock 2020-10-09 14:14:41 +02:00
Ruud Poutsma
340b75b557 add some tests, fix nested blockquote in listitem 2020-10-09 13:24:44 +02:00
Ruud Poutsma
0b79389b14 fix CodeInline parsing & rendering 2020-10-04 21:49:54 +02:00
Ruud Poutsma
111d3d3362 fix multiline quote blocks, split tests into file per node type 2020-10-04 21:20:32 +02:00
Ruud Poutsma
ac6ceb23e8 restore whitespace parsing in BlockProcessor 2020-10-03 23:13:04 +02:00
Ruud Poutsma
d31fa47cd1 add some testcases 2020-10-03 18:55:02 +02:00
Ruud Poutsma
d7f6a94f12 cleanup, add some testcases 2020-10-03 18:54:45 +02:00
Ruud Poutsma
dbdd752b73 Keep line info when parsing quoteblocks and render accordingly 2020-10-03 16:11:01 +02:00
Ruud Poutsma
4c2b46e0fc fix listblock-paragraph 2020-09-27 13:27:14 +02:00
Ruud Poutsma
68d12d0212 fix newlines and list blocks 2020-09-27 13:16:53 +02:00
Ruud Poutsma
fa1c117011 experiemnt with list rendering 2020-09-26 14:24:58 +02:00
Ruud Poutsma
6b1c5bc816 fix thematic break 2020-09-26 14:07:57 +02:00
Ruud Poutsma
e2cafc6b3d fix newline after listblock 2020-09-26 13:46:33 +02:00
Ruud Poutsma
aff7604b4b fix completely broken unordered list 2020-09-26 13:34:37 +02:00
Ruud Poutsma
68530aa4e0 improve linebreak handling 2020-09-26 12:59:48 +02:00
Ruud Poutsma
bfc1152b8a default newline after Html block, fix newlines before list block 2020-09-25 22:35:49 +02:00
Ruud Poutsma
37af8f8ecb implement newline with HtmlBlock 2020-09-25 22:03:34 +02:00
Ruud Poutsma
6792bffb5e better thematic break handling 2020-09-25 21:39:53 +02:00
Ruud Poutsma
976855a4c3 implement TheamticBreak renderer 2020-09-25 21:23:28 +02:00
Ruud Poutsma
acf2ba9502 allow CodeInline with multiple delimiter characters 2020-09-25 19:23:11 +02:00
Ruud Poutsma
cadbc67825 fix newline between blocks 2020-09-25 18:55:54 +02:00
Ruud Poutsma
0d86a93200 fix block nodes 2020-09-25 17:58:58 +02:00
Ruud Poutsma
f78b5c83cd handle whitespace before and after paragraphs correctly 2020-09-25 17:07:10 +02:00
Ruud Poutsma
0234d60d74 fix broken whitespace calculation 2020-08-08 18:25:08 +02:00
Ruud Poutsma
cd18087e29 implement cst for header 2020-08-08 18:18:55 +02:00
Ruud Poutsma
30f670bf5f fix NRE 2020-08-08 17:24:16 +02:00
Ruud Poutsma
c73785372b implement cst for Paragraph 2020-08-08 17:22:53 +02:00
Ruud Poutsma
147698daab revert LinkInlineRenderer 2020-08-08 14:54:57 +02:00
Ruud Poutsma
2b07e9a5b9 add naive cst implementation 2020-08-08 14:41:02 +02:00
238 changed files with 34024 additions and 3193 deletions

View File

@@ -20,14 +20,16 @@ jobs:
- name: Checkout
uses: actions/checkout@v1
- name: Install .NET Core
- name: Install .NET 5.0
uses: actions/setup-dotnet@v1
with:
dotnet-version: '5.0.x'
- name: Build (Release)
run: dotnet build src -c Release
- name: SpecFileGen
run: dotnet src/SpecFileGen/bin/Release/netcoreapp2.1/SpecFileGen.dll
run: dotnet src/SpecFileGen/bin/Release/net5.0/SpecFileGen.dll
- name: Test (Release)
run: dotnet test src -c Release
@@ -36,14 +38,14 @@ jobs:
run: dotnet test src -c Debug
- name: Coverlet
run: dotnet test src -c Release -f netcoreapp3.1 /p:Include=\"[${{env.PROJECT_NAME}}]*\" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
run: dotnet test src -c Release -f net5.0 /p:Include=\"[${{env.PROJECT_NAME}}]*\" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
- name: Coveralls Upload
uses: coverallsapp/github-action@v1.0.1
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: src/${{env.PROJECT_NAME}}.Tests/coverage.netcoreapp3.1.info
path-to-lcov: src/${{env.PROJECT_NAME}}.Tests/coverage.info
- name: Pack
run: dotnet pack src -c Release

View File

@@ -1,11 +1,27 @@
# Changelog
## 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
## 0.22.1 (2 Dec 2020)
- Update logo for NuGet package
## 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -14,11 +14,11 @@
viewBox="0 0 192 192"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="markdig.svg"
inkscape:export-filename="C:\Code\lunet-io\markdig\markdig.png"
inkscape:export-xdpi="93.400002"
inkscape:export-ydpi="93.400002">
inkscape:export-filename="C:\code\lunet\markdig\img\markdig.png"
inkscape:export-xdpi="256"
inkscape:export-ydpi="256">
<defs
id="defs4" />
<sodipodi:namedview
@@ -28,16 +28,16 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4.4374889"
inkscape:cx="174.14769"
inkscape:cy="93.189838"
inkscape:zoom="6.275557"
inkscape:cx="81.620292"
inkscape:cy="119.68434"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="2560"
inkscape:window-height="1377"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-width="3840"
inkscape:window-height="2066"
inkscape:window-x="-11"
inkscape:window-y="-11"
inkscape:window-maximized="1"
units="px" />
<metadata
@@ -48,7 +48,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@@ -59,13 +59,8 @@
style="display:inline"
transform="translate(0,-860.36216)">
<path
style="fill:#000000"
d="M 75.009766 60.486328 L 34.648438 122.74414 C 33.793918 123.59769 37.647081 134.96384 37.052734 136.09766 L 4.0234375 177.69727 C 2.4142291 180.39677 3.2900484 182.21846 4.8730469 183.84766 C 5.9214414 184.93636 6.6591287 186.06887 8.3828125 185.67188 C 9.0750612 185.50987 10.104893 185.27338 10.875 184.76758 L 52.806641 151.81445 C 53.912466 151.23195 64.44071 154.77813 65.289062 153.92383 L 126.45312 111.46875 L 75.009766 60.486328 z M 89.632812 84.769531 L 103.77539 98.912109 L 79.027344 123.66016 L 86.238281 130.87109 L 48.621094 139.92383 L 57.435547 102.30859 L 64.884766 109.51758 L 89.632812 84.769531 z "
transform="translate(0,860.36216)"
id="path4140" />
<path
style="fill:#000000;fill-opacity:1"
d="m 111.18463,862.06984 c -1.98231,0 -3.96282,0.78454 -5.54759,2.38445 L 88.200894,881.94537 75.123368,868.82653 c -3.169466,-3.18017 -7.92567,-3.18017 -11.095213,0 -3.169466,3.18017 -3.169466,7.95108 0,11.13109 l 13.077526,13.11885 -11.095212,10.73223 82.031621,81.49227 11.09525,-11.13109 13.87084,13.51554 c 1.57915,1.59105 3.56724,2.38445 5.54759,2.38445 1.98231,0 3.96285,-0.78453 5.54762,-2.38445 3.16947,-3.18017 3.16947,-7.95111 0,-11.13109 l -13.87083,-13.11884 17.43611,-17.09442 c 1.17983,-1.59105 1.98231,-3.5788 1.98231,-5.96329 0,-1.98351 -0.79863,-3.97554 -2.37816,-5.56446 l -70.54053,-70.35856 c -1.57914,-1.59105 -3.56724,-2.38446 -5.54758,-2.38446 z m 15.86949,20.75826 9.50139,9.5291 -4.04453,23.11372 23.04691,-4.05619 9.50138,9.5291 -36.8437,36.95052 -9.50135,-9.5291 21.13082,-21.19197 -23.04672,4.05619 4.04453,-23.11396 -21.131009,21.19198 -9.501383,-9.5291 36.843662,-36.95048 z"
style="fill:#006680;fill-opacity:1;stroke-width:1.09204066"
d="m 34.5307,873.24547 c -1.506145,1.5557 -2.414834,3.72567 -2.403331,6.22497 l 0.04173,27.41047 -19.903814,-0.0324 c -4.824408,-0.009 -8.4381391,3.7242 -8.4300642,8.70738 0.00817,4.98312 3.6330369,8.72727 8.4573232,8.73554 l 19.903821,0.0324 -0.275791,17.12991 124.244306,-0.42328 -0.0273,-17.44297 20.80798,-0.27883 c 2.4087,0.009 4.52205,-0.92824 6.02671,-2.48239 1.50614,-1.55569 2.41486,-3.72569 2.40336,-6.225 -0.009,-4.98311 -3.63306,-8.7273 -8.45733,-8.73554 l -20.50656,0.59015 0.25961,-27.09914 c -0.31244,-2.17456 -1.213,-4.36429 -3.02472,-6.23561 -1.50705,-1.55665 -3.62738,-2.49321 -6.03475,-2.50058 l -107.054134,0.14282 c -2.408688,-0.009 -4.522059,0.92823 -6.026707,2.48237 z m 27.829505,3.83665 14.459233,0.0217 14.488635,21.31347 14.429017,-21.27019 14.45922,0.0217 0.0811,57.91281 -14.4592,-0.0217 -0.0464,-33.21443 -14.428869,21.27005 -14.488819,-21.31366 0.04632,33.21458 -14.459226,-0.0218 -0.08116,-57.91275 z"
id="path4142"
inkscape:connector-curvature="0" />
<rect
@@ -95,7 +90,7 @@
<flowRoot
xml:space="preserve"
id="flowRoot4797"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="font-style:normal;font-weight:normal;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="matrix(0.1746417,0,0,0.1459084,499.69318,366.39614)"><flowRegion
id="flowRegion4799"><rect
id="rect4801"
@@ -103,7 +98,8 @@
height="618.71844"
x="959.01355"
y="-976.15039" /></flowRegion><flowPara
id="flowPara4803" /></flowRoot> <g
id="flowPara4803"
style="font-size:40px;line-height:1.25"> </flowPara></flowRoot> <g
id="g4833"
transform="matrix(0.09510056,0,0,0.09061765,496.09965,368.83934)">
<rect
@@ -114,18 +110,20 @@
y="0"
style="fill:#ffffff" />
</g>
<path
inkscape:connector-curvature="0"
id="path4886"
d="m 111.18463,862.06984 c -1.98231,0 -3.96282,0.78454 -5.54759,2.38445 L 88.200894,881.94537 75.123368,868.82653 c -3.169466,-3.18017 -7.92567,-3.18017 -11.095213,0 -3.169466,3.18017 -3.169466,7.95108 0,11.13109 l 13.077526,13.11885 -11.095212,10.73223 82.031621,81.49227 11.09525,-11.13109 13.87084,13.51554 c 1.57915,1.59105 3.56724,2.38445 5.54759,2.38445 1.98231,0 3.96285,-0.78453 5.54762,-2.38445 3.16947,-3.18017 3.16947,-7.95111 0,-11.13109 l -13.87083,-13.11884 17.43611,-17.09442 c 1.17983,-1.59105 1.98231,-3.5788 1.98231,-5.96329 0,-1.98351 -0.79863,-3.97554 -2.37816,-5.56446 l -70.54053,-70.35856 c -1.57914,-1.59105 -3.56724,-2.38446 -5.54758,-2.38446 z m 15.86949,20.75826 9.50139,9.5291 -4.04453,23.11372 23.04691,-4.05619 9.50138,9.5291 -36.8437,36.95052 -9.50135,-9.5291 21.13082,-21.19197 -23.04672,4.05619 4.04453,-23.11396 -21.131009,21.19198 -9.501383,-9.5291 36.843662,-36.95048 z"
style="fill:#000000;fill-opacity:1" />
<g
transform="translate(234.63786,787.55486)"
id="g4170" />
<path
id="path4225"
transform="translate(0,860.36216)"
d="M 75.009766 60.486328 L 34.648438 122.74414 C 33.793918 123.59769 37.647081 134.96384 37.052734 136.09766 L 4.0234375 177.69727 C 2.4142291 180.39677 3.2900484 182.21846 4.8730469 183.84766 C 5.9214414 184.93636 6.6591287 186.06887 8.3828125 185.67188 C 9.0750612 185.50987 10.104893 185.27338 10.875 184.76758 L 52.806641 151.81445 C 53.912466 151.23195 64.44071 154.77813 65.289062 153.92383 L 126.45312 111.46875 L 75.009766 60.486328 z M 89.632812 84.769531 L 103.77539 98.912109 L 79.027344 123.66016 L 86.238281 130.87109 L 48.621094 139.92383 L 57.435547 102.30859 L 64.884766 109.51758 L 89.632812 84.769531 z "
style="fill:#000000" />
sodipodi:nodetypes="cccccccccccccc"
inkscape:connector-curvature="0"
id="path826"
d="m 45.058494,943.54749 20.013843,88.87081 c -8.87e-4,1.4793 13.909884,7.9857 28.77803,7.9868 14.868153,0 27.732473,-6.0454 27.727033,-7.5199 l 17.1004,-89.73688 z m 35.561125,8.36596 h 25.853221 l 10e-6,42.86491 h 13.18189 l -26.108514,40.41734 -26.324515,-40.20925 13.397908,-0.20809 z"
style="fill:#ff6600;stroke-width:1.2582258" />
<path
style="fill:#c83737;stroke-width:1.2582258"
d="m 45.058494,943.54749 20.013843,88.87081 c -8.87e-4,1.4793 13.909884,7.9857 28.77803,7.9868 14.868153,0 27.732473,-6.0454 27.727033,-7.5199 l 17.1004,-89.73688 z m 35.561125,8.36596 h 25.853221 l 10e-6,42.86491 h 13.18189 l -26.108514,40.41734 -26.324515,-40.20925 13.397908,-0.20809 z"
id="path828"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,4 +1,4 @@
# Markdig [![Build Status](https://github.com/lunet-io/markdig/workflows/ci/badge.svg?branch=master)](https://github.com/lunet-io/markdig/actions) [![Coverage Status](https://coveralls.io/repos/github/lunet-io/markdig/badge.svg?branch=master)](https://coveralls.io/github/lunet-io/markdig?branch=master) [![NuGet](https://img.shields.io/nuget/v/Markdig.svg)](https://www.nuget.org/packages/Markdig/) [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRGHXBTP442JL)
# Markdig [![Build Status](https://github.com/lunet-io/markdig/workflows/ci/badge.svg?branch=master)](https://github.com/lunet-io/markdig/actions) [![Coverage Status](https://coveralls.io/repos/github/xoofx/markdig/badge.svg?branch=master)](https://coveralls.io/github/xoofx/markdig?branch=master) [![NuGet](https://img.shields.io/nuget/v/Markdig.svg)](https://www.nuget.org/packages/Markdig/) [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](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))
@@ -59,6 +60,7 @@ If you are looking for support for an old .NET Framework 3.5 or 4.0, you can dow
- [**WPF/XAML Markdown Renderer**: `markdig.wpf`](https://github.com/Kryptos-FR/markdig.wpf)
- [**WPF/XAML Markdown Renderer**: `Neo.Markdig.Xaml`](https://github.com/neolithos/NeoMarkdigXaml)
- [**Syntax highlighting**: `Markdig.SyntaxHighlighting`](https://github.com/RichardSlater/Markdig.SyntaxHighlighting)
- [**Syntax highlighting using Prism.js**: `WebStoating.Markdig.Prism`](https://github.com/ilich/Markdig.Prism)
- [**Embedded C# scripting**: `Markdig.Extensions.ScriptCs`](https://github.com/macaba/Markdig.Extensions.ScriptCs)
## Documentation

View File

@@ -36,12 +36,12 @@
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.10.6" />
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.10.6" />
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.12.1" />
<PackageReference Include="CommonMark.NET" Version="0.15.1" />
<PackageReference Include="MarkdownSharp" Version="1.13.0.0" />
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="0.8.31-beta" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="1.0.41.0" />
<PackageReference Include="MarkdownSharp" Version="2.0.5" />
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="2.0.161401" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.62" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Markdig\Markdig.csproj" />

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1</TargetFrameworks>
<TargetFramework>net5.0</TargetFramework>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
<StartupObject>Markdig.Tests.Program</StartupObject>
@@ -12,7 +12,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
</ItemGroup>

View File

@@ -62,6 +62,29 @@ namespace Markdig.Tests
}
}
[Theory]
[TestCase('[', 9 * 1024, true, false)]
[TestCase('[', 11 * 1024, true, true)]
[TestCase('[', 100, false, false)]
[TestCase('[', 150, false, true)]
[TestCase('>', 100, true, false)]
[TestCase('>', 150, true, true)]
public void GuardsAgainstHighlyNestedNodes(char c, int count, bool parseOnly, bool shouldThrow)
{
var markdown = new string(c, count);
TestDelegate test = parseOnly ? () => Markdown.Parse(markdown) : () => Markdown.ToHtml(markdown);
if (shouldThrow)
{
Exception e = Assert.Throws<ArgumentException>(test);
Assert.True(e.Message.Contains("depth limit"));
}
else
{
test();
}
}
[Test]
public void IsIssue356Corrected()
{

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View 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);
}
}
}

View 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);
}
}
}

View File

@@ -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("&gt;")]
[TestCase("&lt;")]
[TestCase("&nbsp;")]
[TestCase("&heartsuit;")]
[TestCase("&#42;")]
[TestCase("&#0;")]
[TestCase("&#1234;")]
[TestCase("&#xcab;")]
[TestCase(" &gt;")]
[TestCase(" &lt;")]
[TestCase(" &nbsp;")]
[TestCase(" &heartsuit;")]
[TestCase(" &#42;")]
[TestCase(" &#0;")]
[TestCase(" &#1234;")]
[TestCase(" &#xcab;")]
[TestCase("&gt; ")]
[TestCase("&lt; ")]
[TestCase("&nbsp; ")]
[TestCase("&heartsuit; ")]
[TestCase("&#42; ")]
[TestCase("&#0; ")]
[TestCase("&#1234; ")]
[TestCase("&#xcab; ")]
[TestCase(" &gt; ")]
[TestCase(" &lt; ")]
[TestCase(" &nbsp; ")]
[TestCase(" &heartsuit; ")]
[TestCase(" &#42; ")]
[TestCase(" &#0; ")]
[TestCase(" &#1234; ")]
[TestCase(" &#xcab; ")]
public void Test(string value)
{
RoundTrip(value);
}
}
}

View 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);
}
}
}

View File

@@ -0,0 +1,25 @@
using NUnit.Framework;
using static Markdig.Tests.TestRoundtrip;
namespace Markdig.Tests.RoundtripSpecs.Inlines
{
[TestFixture]
public class TestImageInline
{
[TestCase("![](a)")]
[TestCase(" ![](a)")]
[TestCase("![](a) ")]
[TestCase(" ![](a) ")]
[TestCase(" ![description](http://example.com)")]
public void Test(string value)
{
RoundTrip(value);
}
[TestCase("paragraph ![description](http://example.com)")]
public void TestParagraph(string value)
{
RoundTrip(value);
}
}
}

View File

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

View 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);
}
}
}

View File

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

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View File

@@ -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");
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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);
}
}
}

View File

@@ -21,6 +21,7 @@ namespace Markdig.Tests
[TestCase(/* markdownText: */ "- foo\n- bar\n- baz", /* expected: */ "foo\nbar\nbaz\n")]
[TestCase(/* markdownText: */ "- foo<baz", /* expected: */ "foo<baz\n")]
[TestCase(/* markdownText: */ "- foo&lt;baz", /* expected: */ "foo<baz\n")]
[TestCase(/* markdownText: */ "## foo `bar::baz >`", /* expected: */ "foo bar::baz >\n")]
public void TestPlainEnsureNewLine(string markdownText, string expected)
{
var actual = Markdown.ToPlainText(markdownText);

View File

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

View File

@@ -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")]

View 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);
}
}
}

View File

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

View File

@@ -1,42 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
<AssemblyName>Markdig.WebApp</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Markdig.WebApp</PackageId>
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<ApplicationInsightsResourceId>/subscriptions/b6745039-70e7-4641-994b-5457cb220e2a/resourcegroups/Default-ApplicationInsights-EastUS/providers/microsoft.insights/components/Markdig.WebApp</ApplicationInsightsResourceId>
<ApplicationInsightsAnnotationResourceId>/subscriptions/b6745039-70e7-4641-994b-5457cb220e2a/resourcegroups/Default-ApplicationInsights-EastUS/providers/microsoft.insights/components/Markdig.WebApp</ApplicationInsightsAnnotationResourceId>
<IsPackable>false</IsPackable>
<UserSecretsId>683145f2-53a7-4938-bd96-35e60ac55d95</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<None Update="Views">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.16.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Markdig\Markdig.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.12.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="2.2.1" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.1" />
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Connected Services" />
</ItemGroup>
</Project>

View File

@@ -1,6 +1,7 @@
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
namespace Markdig.WebApp
{
@@ -8,14 +9,14 @@ namespace Markdig.WebApp
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}

View File

@@ -1,111 +0,0 @@
# Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
# Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
[cmdletbinding(SupportsShouldProcess=$true)]
param($publishProperties=@{}, $packOutput, $pubProfilePath, $nugetUrl)
# to learn more about this file visit https://go.microsoft.com/fwlink/?LinkId=524327
$publishModuleVersion = '1.1.0'
function Get-PublishModulePath{
[cmdletbinding()]
param()
process{
$keysToCheck = @('hklm:\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\{0}',
'hklm:\SOFTWARE\Microsoft\VisualStudio\{0}',
'hklm:\SOFTWARE\Wow6432Node\Microsoft\VWDExpress\{0}',
'hklm:\SOFTWARE\Microsoft\VWDExpress\{0}'
)
$versions = @('14.0', '15.0')
[string]$publishModulePath=$null
:outer foreach($keyToCheck in $keysToCheck){
foreach($version in $versions){
if(Test-Path ($keyToCheck -f $version) ){
$vsInstallPath = (Get-itemproperty ($keyToCheck -f $version) -Name InstallDir -ErrorAction SilentlyContinue | select -ExpandProperty InstallDir -ErrorAction SilentlyContinue)
if($vsInstallPath){
$installedPublishModulePath = "{0}Extensions\Microsoft\Web Tools\Publish\Scripts\{1}\" -f $vsInstallPath, $publishModuleVersion
if(!(Test-Path $installedPublishModulePath)){
$vsInstallPath = $vsInstallPath + 'VWDExpress'
$installedPublishModulePath = "{0}Extensions\Microsoft\Web Tools\Publish\Scripts\{1}\" -f $vsInstallPath, $publishModuleVersion
}
if(Test-Path $installedPublishModulePath){
$publishModulePath = $installedPublishModulePath
break outer;
}
}
}
}
}
$publishModulePath
}
}
$publishModulePath = Get-PublishModulePath
$defaultPublishSettings = New-Object psobject -Property @{
LocalInstallDir = $publishModulePath
}
function Enable-PackageDownloader{
[cmdletbinding()]
param(
$toolsDir = "$env:LOCALAPPDATA\Microsoft\Web Tools\Publish\package-downloader-$publishModuleVersion\",
$pkgDownloaderDownloadUrl = 'https://go.microsoft.com/fwlink/?LinkId=524325') # package-downloader.psm1
process{
if(get-module package-downloader){
remove-module package-downloader | Out-Null
}
if(!(get-module package-downloader)){
if(!(Test-Path $toolsDir)){ New-Item -Path $toolsDir -ItemType Directory -WhatIf:$false }
$expectedPath = (Join-Path ($toolsDir) 'package-downloader.psm1')
if(!(Test-Path $expectedPath)){
'Downloading [{0}] to [{1}]' -f $pkgDownloaderDownloadUrl,$expectedPath | Write-Verbose
(New-Object System.Net.WebClient).DownloadFile($pkgDownloaderDownloadUrl, $expectedPath)
}
if(!$expectedPath){throw ('Unable to download package-downloader.psm1')}
'importing module [{0}]' -f $expectedPath | Write-Output
Import-Module $expectedPath -DisableNameChecking -Force
}
}
}
function Enable-PublishModule{
[cmdletbinding()]
param()
process{
if(get-module publish-module){
remove-module publish-module | Out-Null
}
if(!(get-module publish-module)){
$localpublishmodulepath = Join-Path $defaultPublishSettings.LocalInstallDir 'publish-module.psm1'
if(Test-Path $localpublishmodulepath){
'importing module [publish-module="{0}"] from local install dir' -f $localpublishmodulepath | Write-Verbose
Import-Module $localpublishmodulepath -DisableNameChecking -Force
$true
}
}
}
}
try{
if (!(Enable-PublishModule)){
Enable-PackageDownloader
Enable-NuGetModule -name 'publish-module' -version $publishModuleVersion -nugetUrl $nugetUrl
}
'Calling Publish-AspNet' | Write-Verbose
# call Publish-AspNet to perform the publish operation
Publish-AspNet -publishProperties $publishProperties -packOutput $packOutput -pubProfilePath $pubProfilePath
}
catch{
"An error occurred during publish.`n{0}" -f $_.Exception.Message | Write-Error
}

View File

@@ -1,28 +1,9 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:65396/",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Markdig.WebApp": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "http://localhost:5000/api/to_html?text=yes",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
"launchUrl": "api/to_html?text=test"
}
}
}

View File

@@ -30,19 +30,30 @@ namespace Markdig.WebApp
// This method gets called by the runtime. Use this method to add services to the container
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//loggerFactory.AddConsole(Configuration.GetSection("Logging"));
//loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

View File

@@ -8,6 +8,7 @@
}
},
"ApplicationInsights": {
"InstrumentationKey": "5d12f113-76b2-41fe-a35a-db454b104bf9"
"InstrumentationKey": "5d12f113-76b2-41fe-a35a-db454b104bf9",
"ConnectionString": "InstrumentationKey=c00ad051-d881-4dc3-b8c1-8718f1b1c0c6;IngestionEndpoint=https://westus2-0.in.applicationinsights.azure.com/"
}
}

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
-->
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
</system.webServer>
</configuration>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,7 +17,7 @@ namespace Markdig.Extensions.AutoLinks
{
public readonly AutoLinkOptions Options;
public AutoLinkExtension(AutoLinkOptions options)
public AutoLinkExtension(AutoLinkOptions? options)
{
Options = options ?? new AutoLinkOptions();
}

View File

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

View File

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

View File

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

View File

@@ -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>");
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -102,7 +102,7 @@ namespace Markdig.Extensions.EmphasisExtras
}
}
private string GetTag(EmphasisInline emphasisInline)
private string? GetTag(EmphasisInline emphasisInline)
{
var c = emphasisInline.DelimiterChar;
switch (c)

View File

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

View File

@@ -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>");
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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++)
{

View File

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

Some files were not shown because too many files have changed in this diff Show More