Compare commits

..

637 Commits

Author SHA1 Message Date
Martijn Laarman
d47fbc757f Optimize PipeTable parsing: O(n²) → O(n) for 3.7x–85x speedup, enables 10K+ row tables (#922)
* Optimize PipeTable parsing: O(n²) → O(n) for large tables

Pipe tables were creating deeply nested tree structures where each pipe
delimiter contained all subsequent content as children, causing O(n²)
traversal complexity for n cells. This change restructures the parser to
use a flat sibling-based structure, treating tables as matrices rather
than nested trees.

Key changes:
- Set IsClosed=true on PipeTableDelimiterInline to prevent nesting
- Add PromoteNestedPipesToRootLevel() to flatten pipes nested in emphasis
- Update cell boundary detection to use sibling traversal
- Move EmphasisInlineParser before PipeTableParser in processing order
- Fix EmphasisInlineParser to continue past IsClosed delimiters
- Add ContainsParentOrSiblingOfType<T>() helper for flat structure detection

Performance improvements (measured on typical markdown content):

| Rows | Before    | After   | Speedup |
|------|-----------|---------|---------|
| 100  | 542 μs    | 150 μs  | 3.6x    |
| 500  | 23,018 μs | 763 μs  | 30x     |
| 1000 | 89,418 μs | 1,596 μs| 56x     |
| 1500 | 201,593 μs| 2,740 μs| 74x     |
| 5000 | CRASH     | 10,588 μs| ∞      |
| 10000| CRASH     | 18,551 μs| ∞      |

Tables with 5000+ rows previously crashed due to stack overflow from
recursive depth. They now parse successfully with linear time complexity.

* remove baseline results file

* Do not use System.Index and fix nullabillity checks for older platforms
2026-01-30 22:05:18 +01:00
prozolic
3602433b84 Replace null checks with IsEmpty property for ReadOnlySpan<char> (#916)
This change suppresses CA2265 warnings.
2026-01-30 22:01:50 +01:00
prozolic
1bac4afc9b Use Dictionary.TryAdd instead of ContainsKey and indexer by reducing lookups. (#917)
* Use Dictionary.TryAdd instead of ContainsKey and indexer by reducing lookups.

* Update src/Markdig/Parsers/ParserList.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-30 22:01:27 +01:00
Tatsunori Uchino
a89056d961 Recognize supplementary characters (#913)
* Recognize supplementary characters

* Internatize Rune

* Fix failing tests

* Fix extra comment error

* Remove extra local variable c

* Reorganize classes around Rune

* Prepare both Rune and char variants / make Rune variant public for .NET

* Make APIs in StringSlice.cs public only in modern .NET

* Throw exception if cannot obtain first Rune

* Add comments

* Add comment on PeekRuneExtra

* Use `Rune.TryCreate`

* Remove backtrack

* Fix parameter name in XML comment

* Don't throw when error in `Rune.DecodeFromUtf16`

* Fix RuneAt

* Add tests of Rune-related methods of `StringSlice`

* Make comment more tolerant of changes

* Tweak comment

* Fix comment

* Add `readonly`

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>

* Move namespace of polyfilled Rune out of System.Text

* Apply suggestions from code review

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>

* Fix regression by review suggestion

* Prepare constant for .NET Standard test

* Don't call `IsPunctuationException` if unnecessary

* PR feedback

---------

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
2026-01-12 11:08:03 +01:00
Miha Zupan
cd7b9ca0ef Test netstandard (#915)
* Add GH Action to test netstandard 2.0 and 2.1

* Account for TFM changes in tests project
2025-11-17 18:46:26 +01:00
Alexandre Mutel
fb698598e4 Use central package management 2025-11-17 08:19:42 +01:00
mos379
12590e5fbe feat(link-helper): improve ASCII normalization handling (#911)
* feat(link-helper): improve ASCII normalization handling

Enhanced the `Urilize` method to better handle ASCII normalization and special characters. Added support for decomposing characters when `allowOnlyAscii` is true and skipping diacritical marks. Introduced handling for special German, Scandinavian, and Icelandic characters via new helper methods: `IsSpecialScandinavianOrGermanChar` and `NormalizeScandinavianOrGermanChar`.

Reorganized `using` directives for better clarity. Updated the processing loop in `Urilize` to handle normalized spans and ASCII equivalents more effectively. These changes improve link generation compatibility across various languages.

* Add tests for Scandinavian and German character normalization

Added tests for NormalizeScandinavianOrGermanChar method to validate character normalization for various special characters in both ASCII and non-ASCII contexts.

* test(link-helper): update ASCII transliteration tests

Updated test cases in `TestUrilizeOnlyAscii_Simple` to reflect
changes in `LinkHelper.Urilize` behavior. Non-ASCII characters
like `æ` and `ø` are now transliterated to their ASCII
equivalents (`ae` and `oe`) instead of being removed.
2025-11-10 22:01:35 +01:00
Miha Zupan
8c01cf0549 Add another test for pipe tables (#907) 2025-10-21 08:37:43 +02:00
Miha Zupan
bcbd8e47ac Lazily allocate ProcessInlinesBegin/End delegates on Blocks (#906) 2025-10-21 08:37:02 +02:00
Miha Zupan
d6e88f16f7 Fix pipe table parsing with a leading paragraph (#905)
* Fix pipe table parsing with a leading paragraph

* Use the alternative approach
2025-10-20 21:43:25 +02:00
Miha Zupan
03bdf60086 Add a basic fuzzing project (#903)
* Add basic fuzzing project

* Mark the project as non-packable
2025-10-17 08:09:28 +02:00
Miha Zupan
5c78932f55 Fix edge cases in EmphasisInlineParser (#902) 2025-10-17 08:07:15 +02:00
Miha Zupan
191e33ab32 Fix build warnings (#899) 2025-10-16 17:25:47 +02:00
Miha Zupan
800235ba7a Fix IndexOutOfRangeException in CodeInlineParser (#900) 2025-10-16 17:25:30 +02:00
Miha Zupan
d5f8a809a0 Move sln to slnx (#901) 2025-10-16 17:24:33 +02:00
Asttear
781d9b5365 Remove leading newline in block attributes (#896)
* Remove leading newline in block attributes

fix #895

* Add handling logic for `\r\n`
2025-10-05 11:21:12 +02:00
Phillip Haydon
543570224e Fix issue where an inline code block that spans multiple lines doesn't parse correctly (#893)
* fixes issue where an inline code block that spans multiple lines doesn't get treated as code

* Update src/Markdig.Tests/TestPipeTable.cs

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>

* Apply suggestion from @MihaZupan

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>

* Update src/Markdig.Tests/TestPipeTable.cs

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>

* fix broken test

* removed unreachable code and added more tests

* Update src/Markdig.Tests/TestPipeTable.cs

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>

* Update src/Markdig.Tests/TestPipeTable.cs

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>

* removed uncessary inline code check

* Update src/Markdig/Parsers/Inlines/CodeInlineParser.cs

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>

---------

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
Co-authored-by: Alexandre Mutel <alexandre_mutel@live.com>
2025-10-03 09:34:24 +02:00
Daniel Klecha
4dc0be88b4 add options for link inline (#894)
* add options for link inline

* create LinkOptions and associate it with all four parsers

* set EnableHtmlParsing to true by default
2025-10-03 09:22:51 +02:00
Phillip Haydon
0e9e80e1cd Fix for table depth error when cell contains backticks (#891)
* failing test

* fixed bug with table containing back tick which causes depth error
2025-09-21 16:26:02 +02:00
Alexandre Mutel
1b04599c44 Merge pull request #888 from prozolic/pullreq
Fixes issue #845
2025-09-11 07:55:51 +02:00
prozolic
5e6fb2d1c5 Add test for issue #845 list item blank line 2025-09-08 22:36:09 +09:00
prozolic
14406bc60d Fixes issue #845 2025-09-06 21:10:51 +09:00
Alexandre Mutel
2aa6780a30 Merge pull request #883 from messani/master
Add source position tracking for grid tables
2025-08-28 09:04:44 +02:00
Alexandre Mutel
c43646586c Merge pull request #885 from dannyp32/supportTableWithoutExtraLine
Add support for a table without an extra new line before it
2025-08-28 09:02:29 +02:00
Daniel Pino
d548b82bcd Add support for a table without an extra new line before it 2025-08-09 08:50:49 +00:00
Tibor Peluch
aab5543cb5 Code cleanup 2025-07-14 20:17:50 +02:00
Tibor Peluch
2e1d741aaf Cleaned up code, added tests for source position 2025-07-14 10:23:15 +02:00
Tibor Peluch
80c50e31e2 Attempt to fix tracking of tree node positions (line, column) inside GridTable 2025-07-11 13:25:03 +02:00
Alexandre Mutel
7ff8db9016 Merge pull request #877 from Mertsch/Mertsch-patch-1
Update readme.md
2025-06-19 08:41:54 +02:00
Alexandre Mutel
c69fb9ae73 Merge pull request #879 from stylefish/issue878
Fixes #878: RoundtripRenderer: render indent and 0 blocks for ordered lists
2025-06-19 08:41:10 +02:00
stylefish
5a3c206076 Fixes #878: render indent and 0 blocks 2025-06-16 11:26:23 +02:00
Mertsch
b92890094c Update readme.md 2025-06-12 14:26:00 +02:00
Alexandre Mutel
682c727288 Merge pull request #876 from Akarinnnnn/fix-872
Fix #872 by reserve null title string.
2025-06-05 07:57:29 +02:00
Fa鸽
ec2eef25b2 Remove HtmlHelper.UnescapeNullable 2025-06-04 19:23:18 +08:00
Fa鸽
6261660d37 Explain why not to normalize link title into empty strings 2025-05-31 22:26:33 +08:00
Fa鸽
6d1fa96389 Changed link parsing tests for #872 2025-05-31 16:33:29 +08:00
Fa鸽
47c4e9b1e2 Fix #872 by reserve null title string. 2025-05-31 16:01:42 +08:00
Alexandre Mutel
3535701d70 Merge pull request #869 from prozolic/pullreq
Fix bug in `Markdown.ToPlainText` with code blocks
2025-04-27 18:52:57 +02:00
prozolic
c41b389053 Fix CodeBlockRenderer.Write 2025-04-27 16:49:05 +09:00
Alexandre Mutel
09a4b81a6e Update tests 2025-04-15 11:35:54 +02:00
Alexandre Mutel
7b14e2e091 Merge pull request #867 from MihaZupan/commonmark-0.31.2
Update to CommonMark 0.31.2
2025-04-15 10:59:22 +02:00
Alexandre Mutel
1e17dcdd08 Merge pull request #866 from MihaZupan/alert-perf
Improve Alert parsing perf
2025-04-15 10:58:40 +02:00
Alexandre Mutel
40e5ab1514 Merge pull request #863 from Amberg/master
Infer pipe table column widths from separator row
2025-04-15 10:57:47 +02:00
Alexandre Mutel
2953b026fc Merge pull request #865 from RamType0/patch-1
Fix `MathInline` is called "math block"
2025-04-15 10:56:27 +02:00
Miha Zupan
42ab98968d Update readme 2025-04-15 04:32:52 +02:00
Miha Zupan
b15cf582a5 Add 'search' HTML tag support 2025-04-15 04:31:13 +02:00
Miha Zupan
61e9be290b Allow empty HTML comments, double hyphens in text 2025-04-15 04:02:22 +02:00
Miha Zupan
a9ce0eb438 Update definition of punctuation to include symbols 2025-04-15 03:09:59 +02:00
Miha Zupan
023d93c091 Update CommonMark spec to 0.31.2 2025-04-14 23:32:22 +02:00
Miha Zupan
bbefce3b1f Sealed + ref struct 2025-04-14 22:11:53 +02:00
Miha Zupan
0d6343b421 Make AlertBlock parsing a bit cheaper 2025-04-14 22:02:21 +02:00
Ram.Type-0
f4effc25c0 Fix MathInline is called "math block" 2025-04-15 00:57:16 +09:00
Alexandre Mutel
7a83a1fd3d Merge pull request #864 from MihaZupan/net9-perf4
A couple perf improvements
2025-04-14 11:10:48 +02:00
Miha Zupan
8269ff1af5 Improve AutoLinkParser overhead for false-positive opening chars 2025-04-13 17:45:52 +02:00
Miha Zupan
0e6d0f4cb2 Fix style 2025-04-13 17:23:40 +02:00
Miha Zupan
8484420b72 Remove some branches from IsWhiteSpace and IsWhiteSpaceOrZero 2025-04-13 17:23:27 +02:00
Miha Zupan
c82a36884d Use the field keyword in a few places 2025-04-13 17:22:51 +02:00
Miha Zupan
da3d7f4f3a Improve some descriptions 2025-04-13 17:22:24 +02:00
Miha Zupan
eceb70c16a Avoid delegate allocations in AutoIdentifierExtension 2025-04-13 17:22:04 +02:00
Miha Zupan
7a9c192d7d Speed up FencedCodeBlock rendering 2025-04-13 17:21:43 +02:00
Miha Zupan
8cfa0cf0ae Improve more character tests with SearchValues 2025-04-13 16:59:55 +02:00
Miha Zupan
a82c3bd705 Improve some character tests 2025-04-13 16:59:29 +02:00
Miha Zupan
ecfda373b9 Avoid warnings in Markdig.WebApp 2025-04-13 16:11:30 +02:00
Miha Zupan
d8f69218db Commit FrozenDictionary polyfill 2025-04-13 16:11:02 +02:00
Miha Zupan
adfcf42529 Use FrozenDictionary in a couple places 2025-04-13 16:09:37 +02:00
Miha Zupan
dab1ca5483 Avoid unnecessary null check when reading trivia info 2025-04-13 16:09:24 +02:00
Manuel Amstutz
55f770cc07 feat: infer pipe table column widths from separator row
Adds support for calculating column widths in pipe tables based on the number of dashes in the header separator row.
Enabled via the InferColumnWidthsFromSeparator option in PipeTableOptions.
2025-04-09 20:55:54 +02:00
Alexandre Mutel
8b84542527 Merge pull request #861 from Meir017/patch-1
chore: update repository's github path
2025-03-20 16:38:19 +01:00
Meir Blachman
086440bcd3 update repository's github path 2025-03-20 16:59:50 +02:00
Alexandre Mutel
97470bd61f Merge pull request #859 from JamesNK/jamesnk/autolinks-domain-no-period
Add AutoLinkOptions.AllowDomainWithoutPeriod
2025-03-18 10:00:13 +01:00
James Newton-King
90c73b7754 Update src/Markdig/Helpers/LinkHelper.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
2025-03-18 14:54:20 +08:00
James Newton-King
ee403ce28f Port tests 2025-03-17 08:26:51 +08:00
James Newton-King
8b403918b9 Update XML doc 2025-03-17 07:47:40 +08:00
James Newton-King
39b07d6bc5 Add AutoLinkOptions.AllowDomainWithoutPeriod 2025-03-17 07:46:23 +08:00
Alexandre Mutel
fb3fe8b261 Merge pull request #838 from Melodi17/master
Implemented better indent control in TextRendererBase
2025-02-28 09:23:27 +01:00
Alexandre Mutel
abb19ecf37 Merge pull request #851 from Akarinnnnn/encoding-ployfill
Replace encoding polyfill with NET5+ one.
2025-02-28 09:22:48 +01:00
Fa鸽
9dac60df73 Replace encoding polyfill with NET5+ one.
netstandard2.1 is a special TFM that .NET5+ doesn't mark themselves compitable, even if they mostly are.
2025-02-24 10:49:59 +08:00
Melodi
148278417f Added error throwing when stack is empty and PopIndent() is called 2025-01-14 14:25:20 +10:00
Alexandre Mutel
5b32391348 Update dependencies NuGet 2025-01-10 08:56:38 +01:00
Alexandre Mutel
5528023158 Merge pull request #844 from snnz/fix-gridtables
Prevent GridTableParser from looking beyond the end of a line.
2025-01-09 18:15:11 +01:00
Alexandre Mutel
f93b9d79d9 Merge branch 'master' into fix-gridtables 2025-01-06 08:43:45 +01:00
Alexandre Mutel
d53fd0e870 Merge pull request #843 from snnz/fix-deflists
Fixes exception in DefinitionListParser.GetCurrentDefinitionList()
2025-01-06 08:42:36 +01:00
Alexandre Mutel
c488aca96c Merge branch 'master' into fix-deflists 2025-01-05 21:12:33 +01:00
Alexandre Mutel
9b3f442765 Merge pull request #842 from snnz/fix-alerts
Check that the alert candidate is not already in an alert block or nested within other elements.
2025-01-05 21:11:11 +01:00
Sergey Nozhenko
7b6d659bbd A test has been added. 2025-01-03 07:03:28 +03:00
Sergey Nozhenko
bc8ba4fecb A test has been added. 2025-01-03 07:02:38 +03:00
Sergey Nozhenko
d87bb7292d A test has been added. 2025-01-03 07:01:29 +03:00
Sergey Nozhenko
118d28f886 Prevent GridTableParser from looking beyond the end of a line. 2025-01-03 04:29:24 +03:00
Sergey Nozhenko
3e0c72f043 Fixes exception in DefinitionListParser.GetCurrentDefinitionList() 2025-01-03 03:30:49 +03:00
Sergey Nozhenko
f2590e7b80 Check that the alert candidate is not already in an alert block or nested within other elements. 2025-01-03 01:27:11 +03:00
Melodi
88c5b5cb41 Added method for clearing indents in TextRendererBase as well as added case handling to PopIndent() 2024-12-31 22:57:02 +10:00
Alexandre Mutel
d1233ffe66 Merge pull request #837 from snnz/fix-links
Fix errors in LinkHelper and LinkInlineParser.
2024-12-27 09:49:04 +01:00
Sergey Nozhenko
ab8e85b06e Remove additional condition, since a carriage return constitute a line ending regardless of whether it is followed by a line feed or not. 2024-12-21 06:56:23 +03:00
snnz
90bc15c016 Update src/Markdig.Tests/TestPlayParser.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
2024-12-21 06:14:16 +03:00
snnz
7f604bef30 Update src/Markdig/Parsers/Inlines/LinkInlineParser.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
2024-12-21 06:14:07 +03:00
snnz
54783b8f65 Update src/Markdig/Parsers/Inlines/LinkInlineParser.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
2024-12-21 06:13:56 +03:00
snnz
ad0770a594 Update src/Markdig/Parsers/Inlines/LinkInlineParser.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
2024-12-21 06:13:22 +03:00
snnz
90365bfeee Update src/Markdig/Parsers/Inlines/LinkInlineParser.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
2024-12-21 06:13:09 +03:00
Sergey Nozhenko
c35f7fff17 Fixed errors in LinkHelper and LinkInlineParser. 2024-12-21 03:29:31 +03:00
Alexandre Mutel
fdaef77474 Update ci badge 2024-12-19 05:48:36 +01:00
Alexandre Mutel
733c028311 Merge pull request #836 from snnz/fix-abbreviation
Fix an error in the AbbreviationParser.
2024-12-19 05:46:48 +01:00
Sergey Nozhenko
bc41b0c2a3 Existing test has been extended. 2024-12-19 00:44:29 +03:00
Alexandre Mutel
a8de2087d8 Merge pull request #835 from snnz/fix-pipetable-span
Include opening and closing pipes in the table span
2024-12-18 20:36:31 +01:00
Sergey Nozhenko
2cff6c5194 It's necessary to keep a copy of the original literal.Span.End, because otherwise it is just lost in some cases. 2024-12-18 13:09:00 +03:00
Sergey Nozhenko
5e4a917dbd Fixes an error in the AbbreviationParser. 2024-12-18 12:38:13 +03:00
Sergey Nozhenko
aff8a6823a A test has been added. 2024-12-18 10:21:29 +03:00
Alexandre Mutel
b8a3c270cc Merge pull request #834 from snnz/fix-gridtables
Fix an incorrect offset in GridTableParser.
2024-12-18 06:47:17 +01:00
Sergey Nozhenko
68659f4037 Include opening and closing pipes in the table span 2024-12-18 07:43:22 +03:00
Sergey Nozhenko
e92a8097d0 Fixes an incorrect offset in GridTableParser. 2024-12-18 02:36:56 +03:00
Alexandre Mutel
57fad6fc1a Merge pull request #828 from MihaZupan/net90
Add .NET 9 target, drop .NET 6
2024-12-17 08:43:00 +01:00
Alexandre Mutel
260f4d5acc Merge pull request #832 from snnz/footnote
Set the correct source location in Footnote and FootnoteLinkReferenceDefinition
2024-12-17 08:41:22 +01:00
Alexandre Mutel
102d02a6c1 Merge pull request #831 from snnz/custom-container
Set delimiter char and count in CustomContainerInline instances.
2024-12-17 08:40:55 +01:00
Alexandre Mutel
5ae8ab7a74 Merge pull request #829 from snnz/fix-emphasis-span
Fix incorrect emphasis span calculation.
2024-12-17 08:40:24 +01:00
Sergey Nozhenko
eb28f76588 Set the correct source location to the Footnote and FootnoteLinkReferenceDefinition. 2024-12-15 20:13:35 +03:00
Sergey Nozhenko
d0311b4cea Set delimiter in the CustomContainer instance. 2024-12-15 19:49:56 +03:00
Sergey Nozhenko
a11899a350 Fixes emphasis span calculation. A test is added. 2024-12-09 22:20:32 +03:00
Miha Zupan
40781737c3 Tweak SpecFileGen paths 2024-11-30 03:19:49 +01:00
Miha Zupan
455f8f333d Fix dotnet-versions format 2024-11-29 20:22:40 +01:00
Miha Zupan
98a060f2a3 Add .NET 9 target, drop .NET 6 2024-11-29 19:06:58 +01:00
snnz
49cf59b819 Fix extra line feeds in link title (#826)
* Fix extra line feeds in link title

* Test added.
2024-11-24 11:51:56 +01:00
Alexandre Mutel
310a55c724 Update readme.md 2024-10-30 19:08:35 +01:00
Alexandre Mutel
f734e91568 Merge pull request #823 from xoofx/fix-mermaid
Update DiagramExtension.cs
2024-10-25 22:01:40 +02:00
Alexandre Mutel
090e6d791a Update DiagramExtension.cs
Update tests
2024-10-25 21:58:28 +02:00
Alexandre Mutel
41bdb0f0ab Merge pull request #817 from ehsankalafchi/rename-variable
Rename a variable
2024-10-01 20:53:47 +02:00
Ehsan Kalafchi
b27ef11240 Rename a variable 2024-09-26 13:51:18 +02:00
Alexandre Mutel
dfa2c94b88 Merge pull request #808 from digvijayad/master
Fix mermaid link in readme.md
2024-07-18 06:50:19 +02:00
Digvijay Naruka
89330f3524 Update mermaid link in readme.md
Updated broken old GitHub pages link to the new domain https://mermaid.js.org/
2024-07-17 12:44:26 +05:30
Alexandre Mutel
1a1bbecc46 Merge pull request #786 from MartinZikmund/feature/youtube-short-support
Support for YouTube Shorts embedding
2024-04-09 21:11:30 +02:00
Martin Zikmund
68bd3074b2 Add support for YouTube Shorts embedding 2024-04-01 17:42:46 +02:00
Martin Zikmund
e486903687 Test support for YouTube Shorts embedding 2024-04-01 17:42:12 +02:00
Alexandre Mutel
8e22754db4 Merge pull request #785 from toothache/fix_issues
Fix issues for math span calculation
2024-03-29 21:23:31 +01:00
teethache
93d88ab994 Fix math span calculation. 2024-03-27 07:06:56 +08:00
Alexandre Mutel
000393f46a Fix invalid setext heading (#785) 2024-03-26 21:02:48 +01:00
Alexandre Mutel
a5796890e1 Merge pull request #784 from Abrynos/case-invariant-alerts
Make alert block headers case-invariant
2024-03-25 21:26:19 +01:00
Sebastian Göls
c19ba5b0eb Add fallback value in order to mark unknown alert kinds in some way as well 2024-03-25 12:29:22 +01:00
Sebastian Göls
03390e4f71 Misc. 2024-03-18 10:28:36 +01:00
Sebastian Göls
42bd65caaf Apply feedback 2024-03-18 09:18:39 +01:00
Sebastian Göls
b7ae04bdba Make alert block headers case-invariant 2024-03-18 09:04:47 +01:00
Alexandre Mutel
391f376fa2 Merge pull request #782 from Abrynos/bootstrap-alerts
Add bootstrap alert renderer
2024-03-18 08:11:19 +01:00
Sebastian Göls
f9e96bc9c9 Apply feedback 2024-03-18 07:48:22 +01:00
Alexandre Mutel
c75a11ec32 Update parsing-extensions.md 2024-03-17 15:01:14 +01:00
Alexandre Mutel
fd226d53e9 Update readme.md 2024-03-17 14:59:05 +01:00
Sebastian Göls
7132584996 Add bootstrap alert renderer 2024-03-15 11:28:48 +01:00
Alexandre Mutel
f48331d6c7 Fix missing code in commit for #780 2024-03-14 18:34:44 +01:00
Alexandre Mutel
6549d3b726 Fixes #780 where only the first paragraph of an alert block is processed. 2024-03-14 18:31:11 +01:00
Alexandre Mutel
d434f00355 Merge pull request #779 from toothache/fix_math_span
Fix math source span
2024-03-14 08:14:58 +01:00
Alexandre Mutel
b62a12d32d Add support for GitHub alert blocks (#776)
* Add support for GitHub alert blocks

* Fix alert for "must come first in a quote block"

* Fix comment

* Update src/Markdig/Extensions/Alerts/AlertBlockRenderer.cs

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>

* Update src/Markdig/MarkdownExtensions.cs

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>

* Fix parsing of alert block with multiple children blocks

* Allow null for BlockParser ctor argument of QuoteBlock

---------

Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
2024-03-14 08:09:42 +01:00
teethache
3c7edaa82d Fix math source span. 2024-03-11 20:35:54 +08:00
Alexandre Mutel
fcb56fb037 Merge pull request #773 from hhyyrylainen/patch-1
Fixed a typo in MathSpecs.md
2024-02-29 08:21:10 +01:00
Henri Hyyryläinen
50bc6cadfc Fix typo in MathSpecs.md 2024-02-18 18:58:04 +02:00
Alexandre Mutel
201aa4ef73 Try to use the reusable workflow 2024-02-17 07:11:33 +01:00
Alexandre Mutel
adce9797d6 Use ubuntu for the CI 2024-02-17 06:49:39 +01:00
Alexandre Mutel
fb71dff0ec Fix tests 2024-02-17 06:48:47 +01:00
Alexandre Mutel
399570941a Remove net7.0 from CI 2024-02-17 06:47:31 +01:00
Alexandre Mutel
2f903697e2 Update projects to net8.0 2024-02-17 06:43:07 +01:00
Alexandre Mutel
eb8fe15679 Update ci.yml with nuget config 2024-02-13 09:29:26 +01:00
Alexandre Mutel
8f008e45ca Add nuget.org only config 2024-02-13 09:29:26 +01:00
Alexandre Mutel
0014ec4138 Merge pull request #769 from carbon/cq3
Eliminate various string allocations
2024-02-13 08:25:40 +01:00
Jason Nelson
2ca05ccad7 Eliminate string allocation in CodeInlineRenderer 2024-02-09 13:13:31 -08:00
Jason Nelson
6a15c804bc Add test coverage for headlines with > 6 # characters 2024-02-09 12:54:52 -08:00
Jason Nelson
0446959623 Add TextRendererBase.Write(char c, int count) method, and eliminate various string allocations 2024-02-09 12:54:30 -08:00
Alexandre Mutel
e6afddbaa0 Merge pull request #761 from carbon/collection-expressions
Use C# 12 syntax
2023-12-18 21:11:50 +01:00
Jason Nelson
a377239e91 Use null-coalescing assignment operator 2023-12-14 20:08:53 -08:00
Jason Nelson
35aa304faf Remove unused using statement 2023-12-14 19:56:08 -08:00
Jason Nelson
e4568979ec Fix typo 2023-12-14 19:55:45 -08:00
Jason Nelson
3470ec0d54 Make various members readonly on SourceSpan 2023-12-14 19:55:37 -08:00
Jason Nelson
113ef7f215 Use primary constructors (part 2) 2023-12-14 19:50:09 -08:00
Jason Nelson
4cb4b68883 Use collection expressions (part 5) 2023-12-14 19:43:15 -08:00
Jason Nelson
64ae344b74 Use collection expressions (part 4) 2023-12-14 15:41:07 -08:00
Jason Nelson
b5f3c9fc67 Use collection expressions (part 3) 2023-12-14 12:57:11 -08:00
Jason Nelson
8a88fd0557 Use collection expressions (part 2) 2023-12-14 12:46:40 -08:00
Jason Nelson
cc7623989d Fix typo on private method 2023-12-14 12:39:19 -08:00
Jason Nelson
b6a7acf5fc Use primary constructors 2023-12-14 12:35:22 -08:00
Jason Nelson
804a6f0dbc Use accelerated IndexOfAny in one more case 2023-12-14 12:32:52 -08:00
Jason Nelson
342e264988 Use collection expressions 2023-12-14 12:32:34 -08:00
Alexandre Mutel
f52ecee0b9 Update packages 2023-12-14 06:24:19 +01:00
Alexandre Mutel
a092ec23b3 Merge pull request #760 from zickb/fix_source_span_calculation_for_linebreak_inline
Fix source span calculation for LineBreakInline
2023-12-14 06:18:09 +01:00
Benni
6f1dce6306 Fix last LineBreakInline source span in multi block scenario 2023-12-14 02:53:02 +01:00
Alexandre Mutel
040a778d87 Merge pull request #759 from Akarinnnnn/fix-757
Make StringLineGroup returns a count limited Enumerator
2023-12-12 19:13:52 +01:00
Fa鸽
2ae2cf9263 Add tests for non-boxed enumerator of StringLineGroup. 2023-12-12 16:18:10 +08:00
Fa鸽
ba1e562d2f Fix complication error of StringLineGroup. 2023-12-12 16:15:55 +08:00
Fa鸽
65a02e44ec Add Enumerator GetEnumerator() for StringLineGroup 2023-12-12 15:45:30 +08:00
Alexandre Mutel
e78833ae30 Update src/Markdig/Helpers/StringLineGroup.cs 2023-12-12 08:00:54 +01:00
Fa鸽
2ab716bec1 Make StringLineGroup returns a count limited Enumerator
Fixes #757, before we return the array enumerator directly, enumerate it will get phantom empty lines.
2023-12-11 19:57:51 +08:00
Alexandre Mutel
feeb1867ce Merge pull request #753 from MihaZupan/perf-nov23-3
A few more perf improvements
2023-11-29 10:00:01 +01:00
Miha Zupan
f3aa7e73e3 Avoid Dictionary lookups in RendererBase.Write 2023-11-26 02:27:26 +01:00
Miha Zupan
dce5572356 Create inlining boundaries in MarkdownParser.Parse 2023-11-26 00:59:12 +01:00
Miha Zupan
dbbabd2221 Avoid redundant work in FencedBlockParserBase.TryContinue 2023-11-26 00:59:06 +01:00
Miha Zupan
22145c2fb0 Add UnicodeUtility helper 2023-11-26 00:58:57 +01:00
Miha Zupan
2517003edc Speed up code block arguments string parsing 2023-11-25 21:36:04 +01:00
Miha Zupan
50a3d02c2c Remove NoInlining from throw helper 2023-11-25 20:15:40 +01:00
Alexandre Mutel
40fb2b8249 Merge pull request #751 from MihaZupan/net8
.NET 8.0 and a few other perf improvements
2023-11-25 10:08:32 +01:00
Miha Zupan
5c54968807 Fix polyfill namespace 2023-11-25 03:25:37 +01:00
Miha Zupan
58ea46d58b Also install 6.0, 7.0 SDKs 2023-11-25 02:33:59 +01:00
Miha Zupan
f557e57ab1 Optimize WriteEscapeUrl 2023-11-24 03:34:45 +01:00
Miha Zupan
87aa32e1bd Optimize WriteEscape 2023-11-24 02:57:12 +01:00
Miha Zupan
4f1cb9da08 Avoid allocating strings for known emphasis character fallbacks 2023-11-24 02:44:47 +01:00
Miha Zupan
5cff880c90 Remove temporary string allocations in AutoIdentifierExtension 2023-11-24 02:44:08 +01:00
Miha Zupan
c7aec822b0 Speed up a few character checks 2023-11-24 02:41:08 +01:00
Miha Zupan
b0bde46cc1 Defer position calculations in LiteralInlineParser 2023-11-24 02:39:16 +01:00
Miha Zupan
7803417e5c Rewrite CodeInline matching to make use of vectorization 2023-11-24 02:37:45 +01:00
Miha Zupan
047c4cbcbb Skip _lineBits read on MarkdownObject creation 2023-11-24 02:28:25 +01:00
Miha Zupan
e4f57ca21e Fix build warnings 2023-11-24 02:26:51 +01:00
Miha Zupan
1f1364e69b Add SearchValues polyfill and use it in CharacterMap 2023-11-24 02:23:55 +01:00
Miha Zupan
4eea9db35c Add .NET 8.0 target 2023-11-24 02:17:47 +01:00
Alexandre Mutel
cce7284b84 Merge pull request #749 from michaelvolz/patch-1
Update Visual Studio editor link
2023-11-14 18:36:26 +01:00
Michael A. Volz (Flynn)
8e1e0b9bf3 Update Visual Studio editor link
Markdown Editor v2 (Visual Studio 2022)

This is a complete rewrite of the original Markdown Editor with tons of fixes, tweeks, and performance improvements.
2023-11-14 17:50:51 +01:00
Alexandre Mutel
7d40bc118b Merge pull request #736 from zickb/better_literal_delimiter_content_string_slice
Better literal delimiter content string slice
2023-08-30 07:29:44 +02:00
Benni
dba94a2371 Add documentation for the new constructor. 2023-08-30 00:55:35 +02:00
Benni
6d75eed3bb Don't break external users of the public constructor 2023-08-30 00:53:05 +02:00
Benni
ccb75fd5f0 Merge branch 'master' into better_literal_delimiter_content_string_slice 2023-08-30 00:50:18 +02:00
Benni
06eb6ba774 Better content string slice of delimiter literals:
instead of creating a new StringSlice only containing the delimiter chars, use the provided StringSlice from the match method with an appropriate start and end index
2023-08-30 00:40:10 +02:00
Alexandre Mutel
f15e9f020e Merge pull request #733 from zickb/fix_source_span_calculation
Fix source span calculation
2023-08-26 16:04:01 +02:00
Benni
a70ca6304f Fix source span of paragraphs in table cells and cleanup InlineProcessor 2023-08-25 08:54:30 +02:00
Benni
d26822be05 fix inline source spans calculation 2023-08-22 23:38:37 +02:00
Alexandre Mutel
5e3416f8b7 Merge pull request #726 from mkapahnke/make_allow_null_internal
Make AllowNull internal to prevent conflicts
2023-08-04 06:40:22 +02:00
Alexandre Mutel
012a57d361 Merge pull request #724 from DeveloPoel/markdown.cs_findings
Made the markdown class not partial and fixed the ToPlainText summary.
2023-08-04 06:39:58 +02:00
Alexandre Mutel
053a18c684 Merge pull request #723 from RickStrahl/MediaExtensions-RelativePathSupport
Add relative path support for Audio and Video Urls to  MediaLinks Extension
2023-08-04 06:39:38 +02:00
Maximilian Kapahnke
13265453ac make internal 2023-07-25 18:23:23 +02:00
developoel
8ea0783834 Made the markdown class not partial and fixed the ToPlainText summary. 2023-07-18 14:09:19 +02:00
Rick Strahl
3d29430337 Update src/Markdig/Extensions/MediaLinks/MediaLinkExtension.cs
Co-authored-by: Günther Foidl <gue@korporal.at>
2023-07-13 12:04:52 -07:00
Rick Strahl
81bc58c6c9 Add support for relative Urls for Video and Audio links. 2023-07-11 14:58:43 -07:00
Rick Strahl
bfe3800130 Allow for use of .NET 7.0 SDK (Major Version Roll Forward) 2023-07-11 12:13:05 -07:00
Alexandre Mutel
b7cb169fd3 Merge pull request #710 from valterc/fix-line-group-oob
Add line count check to avoid out of range
2023-04-22 13:23:04 +01:00
Valter Costa
512b28256a Improved tests 2023-04-18 17:44:41 +01:00
Valter Costa
cd5d11eeff Fix index check 2023-04-18 17:44:23 +01:00
Valter Costa
a9118774a8 Added line count check to avoid out of bounds 2023-04-18 12:46:48 +01:00
Alexandre Mutel
8155a1e3d6 Update specs 2023-02-27 07:39:46 +01:00
Alexandre Mutel
0a167248fd Merge pull request #701 from yufeih/yufeih/yamlfrontmatter
Allow YAML front matter in the middle of the document
2023-02-26 21:29:25 +01:00
Yufei Huang
9df67b7934 Allow YAML front matter in the middle of the document 2023-02-23 21:12:50 +08:00
Alexandre Mutel
8f8a145f0e Merge pull request #696 from artempyanykh/fix-setext-span
Fix incorrect setext heading source span
2023-02-16 06:41:11 +01:00
Artem Pyanykh
a6cd283183 Fix incorrect setext heading source span 2023-02-11 17:09:00 +00:00
Alexandre Mutel
851713fad9 Merge pull request #693 from carbon/fsn2
Enable ImplictUsings and use file-scoped namespaces
2023-01-24 07:46:44 +01:00
Jason Nelson
9422764f98 [Markdig] Use global usings 2023-01-21 16:41:18 -08:00
Jason Nelson
74f978ed2d [Markdig] Use file-scoped namespaces 2023-01-21 16:35:41 -08:00
Jason Nelson
66aaffaef1 [mctoc] Enable ImplictUsings and use file-scoped namespaces 2023-01-18 19:53:44 -08:00
Jason Nelson
a18d8dee4f [UnicodeNormDApp] Enable ImplictUsings and use file-scoped namespaces 2023-01-18 19:51:58 -08:00
Jason Nelson
96934214db [SpecFileGen] Enable ImplictUsings and use file-scoped namespaces 2023-01-18 19:51:19 -08:00
Jason Nelson
ca03b7df4f [Benchmarks] Enable ImplictUsings and use file-scoped namespaces 2023-01-18 19:50:40 -08:00
Jason Nelson
4660426719 [WebApp] Enable ImplictUsings and use file-scoped namespaces 2023-01-18 19:49:44 -08:00
Jason Nelson
7ef2959d6e [Tests] Enable ImplictUsings and use file-scoped namespaces 2023-01-18 19:48:52 -08:00
Alexandre Mutel
78c4efb9cc Merge pull request #690 from carbon/net46
Bump  .NET framework target to net462
2023-01-14 18:19:51 +01:00
Jason Nelson
641f3fe0c1 Remove ArrayHelper 2023-01-09 21:52:02 -08:00
Jason Nelson
83145a323b Add back missing using statement 2023-01-09 20:11:44 -08:00
Jason Nelson
1abc804228 Bump .NET framework target to net462 2023-01-09 20:03:49 -08:00
Alexandre Mutel
25323f080c Merge pull request #685 from carbon/netcoreapp3.1
Drop netcoreapp3.1 target
2022-12-20 21:03:43 +01:00
Jason Nelson
38a15ef2a9 Drop netcoreapp3.1 target 2022-12-16 20:17:49 -08:00
Alexandre Mutel
f9bed5d270 Merge pull request #678 from waldyrious/copyedit-grid-tables-docs
Copyedit the grid table extension documentation
2022-12-04 18:38:24 +01:00
Waldir Pimenta
3f7d5c68c2 Copyedit the grid table extension documentation 2022-11-25 11:47:14 +00:00
Alexandre Mutel
936fe35460 Merge pull request #672 from LukeTOBrien/feature/medialink-controls
Option to set controls attribute for MediaLinks
2022-10-18 08:47:46 +02:00
Alexandre Mutel
0dc4857213 Merge pull request #674 from wbaldoumas/contriuting-docs
Add Contributing Docs
2022-10-18 08:47:27 +02:00
Alexandre Mutel
956f109419 Update contributing.md 2022-10-18 08:47:11 +02:00
Will
b1f3229812 Link contributing docs from readme 2022-10-14 08:32:15 -07:00
Will
3ceff1d076 Add Contributing Docs 2022-10-14 08:27:08 -07:00
Luke
e2e557b1c4 Change to make option more explicit 2022-10-14 16:23:53 +01:00
Luke
e155792be5 Option to set controls attribute for MediaLinks 2022-10-13 15:01:51 +01:00
Alexandre Mutel
98c687b4ed Merge pull request #666 from meziantou/issues/665
Fix ANE when parsing empty documents with trackTrivia enabled
2022-09-27 07:24:22 +02:00
Gérald Barré
8e4a732efe Fix ANE when parsing empty documents with trackTrivia enabled 2022-09-26 21:55:28 -04:00
Alexandre Mutel
bce4b70dc6 Merge pull request #649 from MihaZupan/commonmark-whitespace-punctuation
Align Whitespace and Punctuation definitions with CommonMark
2022-08-12 07:46:24 +02:00
Alexandre Mutel
1f71520de9 Merge pull request #650 from gfoidl/htmlhelper-TryParseHtmlTagOpenTag_remove_branches
Remove some branches in HtmlHelper.TryParseHtmlTagOpenTag by using bitmask
2022-08-12 07:45:31 +02:00
Günther Foidl
bfd7b6460c Remove some branches in HtmlHelper.TryParseHtmlTagOpenTag by using bitmasks 2022-07-21 12:11:03 +02:00
Miha Zupan
0e26ec5382 Align Whitespace and Punctuation definitions with CommonMark 2022-07-17 20:22:26 +02:00
Alexandre Mutel
5f80d86265 Merge pull request #642 from mnaoumov/issue-579
More accurate check for YAML renderers
2022-06-15 07:52:51 +02:00
Alexandre Mutel
b85cc0daf5 Merge pull request #644 from PaulVrugt/master
added support for ToPlainText for pipetables extension
2022-06-15 07:52:06 +02:00
Paul Vrugt
76073e81c0 added support for ToPlainText for pipetables extension 2022-06-08 19:47:48 +02:00
Michael Naumov
5b6621d729 Add tests to cover all possible cases of adding Yaml renderers 2022-06-07 15:06:14 -06:00
Michael Naumov
9723eda455 More accurate check for YAML renderers 2022-06-07 14:02:40 -06:00
Alexandre Mutel
7228ad5072 Merge pull request #638 from mnaoumov/issue-579
Add YamlFrontMatterRoundtripRenderer
2022-06-07 21:40:58 +02:00
Alexandre Mutel
96f55d0aa6 Merge pull request #637 from MihaZupan/perf-parse-overhead
Reduce the overhead of Parse calls
2022-06-07 21:38:08 +02:00
Alexandre Mutel
2d69ac4499 Merge pull request #624 from mattj23/docs
Additional parser documentation
2022-06-07 21:37:17 +02:00
Michael Naumov
5e91b9b763 Add YamlFrontMatterRoundtripRenderer 2022-05-17 16:46:24 -06:00
Miha Zupan
e6255de62b Avoid locking overhead in ObjectCache 2022-05-14 19:04:51 +02:00
Miha Zupan
c7b8772669 Avoid SelfPipeline search overhead 2022-05-14 19:03:31 +02:00
Alexandre Mutel
89a10ee76b Remove old benchmark graphs 2022-04-23 17:37:06 +02:00
Alexandre Mutel
7676079b4e Update doc readme with new benchmarks 2022-04-23 17:36:17 +02:00
Alexandre Mutel
495abab743 Update benchmark code and dependencies 2022-04-23 16:51:16 +02:00
Alexandre Mutel
210b39e8fb Improve RenderBase optimization with Type.GetTypeHandle (#632) 2022-04-23 16:21:09 +02:00
Alexandre Mutel
f09d030fd3 Cleanup code after #632 2022-04-23 14:05:26 +02:00
Alexandre Mutel
f2ca6be7a6 Fix name for SpecFileGen 2022-04-23 07:59:18 +02:00
Alexandre Mutel
94e07d11ce Revert optimization for RendererBase to use a plain type for the dictionary key (#632) 2022-04-23 07:46:26 +02:00
Alexandre Mutel
263041e899 Merge pull request #632 from MihaZupan/wasm-mt-ptr
Don't try to use the MT pointer on unsupported platforms
2022-04-23 07:38:16 +02:00
Miha Zupan
e8f9274b64 Don't try to use the MT pointer on unsupported platforms 2022-04-22 20:36:52 +02:00
Alexandre Mutel
b32e71aaeb Merge pull request #630 from MihaZupan/containerblock-copyto-628
Fix typos in ContainerBlock
2022-04-22 07:33:56 +02:00
Miha Zupan
f991b2123b Fix typos in ContainerBlock 2022-04-21 20:48:13 +02:00
Alexandre Mutel
a4a1a177bc Update doc and fix name for CommonMark specs 2022-04-21 07:40:17 +02:00
Alexandre Mutel
59d59694f4 Merge pull request #627 from xoofx/update-commonmark-spec
Update commonmark spec 0.30
2022-04-21 07:30:44 +02:00
Alexandre Mutel
caf3c722e1 Fix unicode case fold for spec 0.30 2022-04-21 07:26:02 +02:00
Alexandre Mutel
47c64d8815 Fix support for textarea with new CommonMark 0.30 specs 2022-04-20 19:04:28 +02:00
Alexandre Mutel
2502fab340 Update CommonMark specs to 0.30 2022-04-20 19:03:47 +02:00
Alexandre Mutel
17b5500b03 Add test related to #625 2022-04-20 18:28:58 +02:00
Alexandre Mutel
b754aef6b0 Update dependencies 2022-04-20 18:28:32 +02:00
Alexandre Mutel
04843a08d2 Merge branch 'fix-escape-line-break' 2022-04-20 18:10:11 +02:00
Alexandre Mutel
fcc73691b6 Fixes escape line break (#620) 2022-04-20 18:09:17 +02:00
Alexandre Mutel
cb8dc99d96 Add test for line break
test
2022-04-20 18:08:28 +02:00
Alexandre Mutel
6f45ac0885 Merge pull request #623 from MihaZupan/globalization-ltr-fix
Fix GlobalizationExtension RTL detection
2022-04-20 15:56:30 +02:00
Alexandre Mutel
891e2fca78 Merge pull request #622 from MihaZupan/simd-character-map
Vectorize CharacterMap.IndexOfOpeningCharacter
2022-04-20 15:56:05 +02:00
Alexandre Mutel
925d4f9227 Merge pull request #621 from MihaZupan/perf-april
April perf improvements
2022-04-20 15:54:48 +02:00
Matt Jarvis
f0c200fc28 Updates to parsing documentation, inlines 2022-04-15 18:16:07 -04:00
Matt Jarvis
70184179b7 Merge remote-tracking branch 'upstream/master' into docs 2022-04-15 15:45:16 -04:00
Miha Zupan
d69b989810 Fix GlobalizationExtension RTL detection 2022-04-15 20:09:50 +02:00
Miha Zupan
da756f4efe Vectorize CharacterMap.IndexOfOpeningCharacter 2022-04-15 19:43:04 +02:00
Miha Zupan
e192831db0 Add tests for LazySubstring 2022-04-15 16:33:36 +02:00
Miha Zupan
be3c93a9b0 Remove trailing space 2022-04-15 16:21:39 +02:00
Miha Zupan
6466f01a80 Add fast-path for simple GetSourcePosition 2022-04-10 18:05:00 +02:00
Alexandre Mutel
cb26f30f7b Merge pull request #607 from mattj23/docs
Start of documentation on parser
2022-04-10 10:09:41 +02:00
Miha Zupan
147c3f059a Improve the fast-path in AutoLink parser 2022-04-08 18:36:41 +02:00
Miha Zupan
3201699053 Avoid allocating CodeInline.Content substrings 2022-04-08 17:22:38 +02:00
Miha Zupan
e86d1ffce5 Avoid OrderedStart allocations in NumberedListItemParser 2022-04-08 16:39:21 +02:00
Miha Zupan
48c979dc74 Use FastStringWriter as the dummy writer
Avoids allocation when setting Writer.NewLine
2022-04-08 00:21:27 +02:00
Miha Zupan
3ae0c8b369 Revert 6eacf8a 2022-04-08 00:09:22 +02:00
Miha Zupan
ee732e5a42 Add ToHtml helper accepting a TextWriter 2022-04-08 00:02:24 +02:00
Miha Zupan
76e25833ad Use method table pointer instead of TypeHandle 2022-04-03 19:31:33 +02:00
Miha Zupan
53dff53260 Make LinkInline SourceSpan fields non-nullable 2022-04-03 16:44:08 +02:00
Miha Zupan
b2eeaf7185 Store trivia next to DataEntries 2022-04-03 16:19:02 +02:00
Miha Zupan
47a22bc5e8 Reduce the size of LiteralInline and EmphasisDelimiterInline 2022-04-03 15:50:07 +02:00
Miha Zupan
89e4c29f9f Inline simple property getters 2022-04-03 15:08:46 +02:00
Miha Zupan
a946c6d0b4 Use local over list access in GetSourcePosition 2022-04-03 15:02:44 +02:00
Miha Zupan
e2770d8c11 Reduce covariance check overhead 2022-04-03 14:28:02 +02:00
Miha Zupan
6eacf8a170 Reduce casts when rendering 2022-04-03 13:34:38 +02:00
Miha Zupan
e11a2630b8 Reduce the size of Inline and casting overhead 2022-04-03 13:34:06 +02:00
Matt Jarvis
4169e538af Merge remote-tracking branch 'upstream/master' into docs 2022-04-02 22:22:11 -04:00
Alexandre Mutel
ccf455d316 Fix AssemblyVersion with MinVer (#612) 2022-03-27 10:15:16 +02:00
Alexandre Mutel
8beb096814 Fix emphasis parsing with table delimiters (#614) 2022-03-27 10:04:14 +02:00
Alexandre Mutel
6a35ec45b9 Merge pull request #611 from MihaZupan/renderer-perf
Improve rendering performance
2022-03-27 09:45:19 +02:00
Miha Zupan
ed83943ba5 Use custom StringWriter for rendering internally 2022-03-20 13:49:38 +01:00
Miha Zupan
9adf60116b More WriteRaw 2022-03-20 11:24:05 +01:00
Miha Zupan
31904f6c53 Avoid allocating WriteEscapeIndexOfAnyChars
Roslyn doesn't support static char arrays yet
2022-03-20 10:54:09 +01:00
Miha Zupan
3f3b3c46b6 Optimize renderers 2022-03-20 10:21:36 +01:00
Miha Zupan
f3d6c2775b Add Unsafe.As polyfill for NETSTANDARD 2.1 2022-03-20 04:02:16 +01:00
Miha Zupan
bb6ace15b7 Optimize RendererBase.Write 2022-03-20 03:39:47 +01:00
Miha Zupan
202ac1e4f9 Simplify RendererBase ctor 2022-03-20 02:36:02 +01:00
Miha Zupan
2604239764 Move TryGetRenderer to cold path 2022-03-20 02:12:35 +01:00
Miha Zupan
cc04208b95 Change IMarkdownObjectRenderer.Accept to take a Type instead of instance 2022-03-20 02:06:08 +01:00
Miha Zupan
14ab45cf8f Move TryWriters to cold path 2022-03-20 01:56:48 +01:00
Miha Zupan
e36d4564f1 Remove NormalizeAutoLinkRenderer 2022-03-20 01:53:20 +01:00
Alexandre Mutel
358a5f09ef Merge pull request #608 from MihaZupan/perf-march-2022-2
Some CPU improvements
2022-03-19 14:24:43 +01:00
Matt Jarvis
25db6cb414 updated with initial comments 2022-03-15 21:01:45 -04:00
Miha Zupan
315ffd42ab Move InternalsVisibleTo from targets to non-signed csproj 2022-03-14 11:33:04 +01:00
Miha Zupan
2675b4dd1e Fixup FencedBlockParserBase nullability 2022-03-14 11:31:42 +01:00
Miha Zupan
58d7fae12d Cleanup exit condition in Unescape 2022-03-14 11:11:35 +01:00
Miha Zupan
e16ed79dcd Optimize StringLineGroup iteration 2022-03-14 10:58:21 +01:00
Miha Zupan
9ef5171369 Reduce type check and casting overhead 2022-03-14 10:56:59 +01:00
Alexandre Mutel
0cfe6d7da4 Merge pull request #606 from MihaZupan/perf-march-2022
Improvements when TrackTrivia is disabled
2022-03-14 09:46:26 +01:00
Miha Zupan
fe65c1b187 Add & use ValueStringBuilder 2022-03-14 07:36:10 +01:00
Miha Zupan
92385ee19a Set SkipLocalsInit for Markdig 2022-03-14 05:05:35 +01:00
Miha Zupan
9f651feac0 Properly trim & cache FencedCodeBlock Info strings 2022-03-14 04:23:44 +01:00
Matt Jarvis
d42b297128 initial documentation on parser 2022-03-13 12:54:41 -04:00
Miha Zupan
b7d02cadbb Fix TrackTrivia /// comments 2022-03-13 01:54:44 +01:00
Miha Zupan
6f75b5156c Aggressively avoid TrackTrivia work and allocations when not requested for Inlines 2022-03-13 01:34:47 +01:00
Miha Zupan
61452c91e9 Aggressively avoid TrackTrivia work and allocations when not requested 2022-03-13 01:21:33 +01:00
Miha Zupan
b697a03c2b Lazily allocate Trivia properties on LinkReferenceDefinition 2022-03-12 23:34:30 +01:00
Miha Zupan
9f734ba3c9 Lazily allocate Trivia properties on LinkInline 2022-03-12 23:28:16 +01:00
Miha Zupan
88cdbf3a17 Lazily allocate CodeBlock.CodeBlockLines 2022-03-12 22:42:15 +01:00
Miha Zupan
fb9561cf89 Fix roughLineCountEstimate calculation (min/max order) 2022-03-12 22:26:41 +01:00
Miha Zupan
9145f47f89 Move TryParseInlineLinkTrivia to cold path 2022-03-12 22:25:52 +01:00
Miha Zupan
1862b37bbd Optimize LineReader.ReadLine 2022-03-12 02:18:50 +01:00
Alexandre Mutel
983187eace Fix emphasis when EnableTrackTrivia() is used (#561) 2022-03-11 08:16:42 +01:00
Alexandre Mutel
94581d9655 Try to workaround track trivia not generating valid HTML (#561) 2022-03-10 09:36:24 +01:00
Alexandre Mutel
6aebd68413 Fix specfilegen 2022-03-10 09:35:08 +01:00
Alexandre Mutel
a3f21b7639 Merge pull request #600 from gauss-lvs/master
Version "Unknown" if AssemblyFileVersionAttribute is missing
2022-03-03 18:43:54 +01:00
Patrick
653c9d7f16 Version returns "Unknown" if the AssemblyFileVersionAttribute is not found. 2022-03-02 09:09:59 +01:00
Alexandre Mutel
9bbf6855e1 Use bash with dotnet-releaser in GitHub Action 2022-03-01 08:27:16 +01:00
Alexandre Mutel
28f5503cfe Fix ci with dotnet-releaser when secrets are not available (bis) 2022-02-28 22:17:49 +01:00
Alexandre Mutel
ba990c739e Fix ci with dotnet-releaser when secrets are not available 2022-02-28 21:43:54 +01:00
Alexandre Mutel
0c45e19723 Remove logs from tests if no errors 2022-02-28 09:47:11 +01:00
Alexandre Mutel
73b8bbe1c7 Update ci to use dotnet-releaser 2022-02-28 09:18:40 +01:00
Alexandre Mutel
996b04cadb Merge pull request #593 from tibel/define-NETCORE-removed
remove NETCORE constant
2022-02-05 14:38:17 +01:00
Thomas Ibel
e7d7cbe010 remove NETCORE constant
and replace by NETSTANDARD2_1_OR_GREATER and NETCOREAPP3_1_OR_GREATER
2022-02-05 07:19:49 +01:00
Alexandre Mutel
ff89b48c33 Merge pull request #591 from carbon/net60
Drop .netcoreapp2.1 target and cross target .net6.0
2022-01-30 18:36:39 +01:00
Jason Nelson
58de5dbcea Update build note 2022-01-28 11:53:06 -08:00
Jason Nelson
fbd7d83cda Future proof NETCORE pragmas 2022-01-28 11:50:34 -08:00
Jason Nelson
13a9c066f7 Use built-in MemberNotNullAttribute on NET5.0+ 2022-01-28 11:47:05 -08:00
Jason Nelson
b7be61a914 Remove NETCOREAPP2_1 conditions 2022-01-27 19:56:53 -08:00
Jason Nelson
c212f9efa8 Use C# 10 2022-01-27 19:56:19 -08:00
Jason Nelson
e0961d7e86 Run benchmarks against net6.0 2022-01-27 19:53:54 -08:00
Jason Nelson
c67a802e69 Drop netcoreapp2.1 target and cross-target net6.0 2022-01-27 19:52:41 -08:00
Alexandre Mutel
5e3527b7d2 Bump to 0.27.0 2022-01-23 16:10:37 +01:00
Alexandre Mutel
d56e080fd7 Merge pull request #590 from yufeih/bug-crlf-link
Fix link reference definition parse bug with title and CRLF
2022-01-23 16:05:02 +01:00
Yufei Huang
d807255e13 Fix link reference definition parse bug with title and CRLF 2022-01-21 23:37:27 +08:00
Alexandre Mutel
029da6c99b Merge pull request #587 from wbaldoumas/add_markdown_colorcode
Add Markdown.ColorCode to README
2022-01-20 13:29:33 +01:00
Alexandre Mutel
b59c9e8925 Merge pull request #586 from tibel/feature/net6.0
.NET 6.0
2022-01-20 13:29:18 +01:00
Alexandre Mutel
22662b248d Update src/global.json 2022-01-20 12:50:58 +01:00
William Baldoumas
9d51ab176c Add Markdown.ColorCode 2022-01-19 21:37:52 -05:00
Thomas Ibel
584378967d fix build warnings 2022-01-19 20:12:41 +01:00
Thomas Ibel
f553bdd4c7 use await for http client 2022-01-19 19:18:46 +01:00
Thomas Ibel
16d77b7bb8 fix github action version 2022-01-19 18:46:54 +01:00
Thomas Ibel
d267cbe173 update github actions 2022-01-19 18:40:19 +01:00
Thomas Ibel
f54944962f update test and benchmark dependencies 2022-01-19 18:32:17 +01:00
Thomas Ibel
5a54af1235 fix warnings 2022-01-19 18:05:28 +01:00
Thomas Ibel
4567d86c15 use .NET 6.0 SDK 2022-01-19 18:01:30 +01:00
Alexandre Mutel
2b6dde9415 Merge pull request #576 from SimonCropp/patch-1
Strike.V8 is deprecated
2021-11-26 08:11:18 +01:00
Simon Cropp
ea8d9b608a Strike.V8 is deprecated 2021-11-15 13:53:50 +11:00
Alexandre Mutel
1469cc8fdb Merge pull request #575 from boxofyellow/master
Fix typo in EmphasisExtraOptions descriptions
2021-10-31 09:14:13 +01:00
Alexandre Mutel
daf2171366 Update src/Markdig/Extensions/EmphasisExtras/EmphasisExtraOptions.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
2021-10-31 08:54:20 +01:00
Alexandre Mutel
63a9f2406b Update src/Markdig/Extensions/EmphasisExtras/EmphasisExtraOptions.cs
Co-authored-by: Miha Zupan <mihazupan.zupan1@gmail.com>
2021-10-31 08:54:12 +01:00
boxofyellow
fd100d1796 Fix typos in EmphasisExtraOptions descriptions 2021-10-30 14:55:59 -04:00
Alexandre Mutel
2c5a51d89b Merge pull request #572 from flcdrg/patch-1
Fix named parameter syntax
2021-09-16 06:21:45 +02:00
Alexandre Mutel
28db7fbd6b Merge pull request #573 from lajjne/target-blank
Fixes #571
2021-09-16 06:21:14 +02:00
Linus Birgerstam
56470a72a8 Fixes #571 2021-09-15 20:08:17 +02:00
David Gardiner
6b163a50c3 Fix named parameter syntax 2021-09-14 18:40:25 +09:30
Alexandre Mutel
c0ee97a803 Bump to 0.26.0 2021-08-27 20:34:30 +02:00
Alexandre Mutel
63ce549ea2 Merge pull request #570 from Sleepyowl/master
Fixes xoofx/markdig#567
2021-08-27 20:24:45 +02:00
Dmtry Soloviev
0faf0ef430 Make Mathematics extension respect EnableHtml* options 2021-08-24 23:03:02 +02:00
Alexandre Mutel
cdd4b40469 Merge pull request #560 from yufeih/yufeih/fix-crlf
Fix rendering diff between line endings
2021-07-06 18:36:32 +02:00
Yufei Huang
54d85ebac6 Fix rendering diff between line endings 2021-06-29 16:18:42 +08:00
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
486 changed files with 71546 additions and 34333 deletions

View File

@@ -12,8 +12,9 @@ insert_final_newline = false
trim_trailing_whitespace = true
# Solution Files
[*.sln]
indent_style = tab
[*.slnx]
indent_size = 2
insert_final_newline = true
# XML Project Files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
@@ -35,3 +36,8 @@ insert_final_newline = true
# Bash Files
[*.sh]
end_of_line = lf
# C# files
[*.cs]
# License header
file_header_template = Copyright (c) Alexandre Mutel. All rights reserved.\nThis file is licensed under the BSD-Clause 2 license.\nSee the license.txt file in the project root for more information.

2
.gitattributes vendored
View File

@@ -1,3 +1,3 @@
* text=auto
*.cs text=auto diff=csharp
*.sln text=auto eol=crlf
*.slnx text=auto eol=crlf

View File

@@ -1,8 +1,5 @@
name: ci
env:
PROJECT_NAME: Markdig
on:
push:
paths-ignore:
@@ -14,49 +11,11 @@ on:
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Install .NET Core
uses: actions/setup-dotnet@v1
- name: Build (Release)
run: dotnet build src -c Release
- name: SpecFileGen
run: dotnet src/SpecFileGen/bin/Release/netcoreapp2.1/SpecFileGen.dll
- name: Test (Release)
run: dotnet test src -c Release
- name: Build & Test (Debug)
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
- 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
- name: Pack
run: dotnet pack src -c Release
- name: Pack Signed
run: dotnet pack -c Release src/${{env.PROJECT_NAME}}.Signed/${{env.PROJECT_NAME}}.Signed.csproj
- name: Publish
if: github.event_name == 'push'
run: |
if ( "${{github.ref}}" -match "^refs/tags/[0-9]+\.[0-9]+\.[0-9]+$" ) {
dotnet nuget push src\${{env.PROJECT_NAME}}\bin\Release\*.nupkg -s nuget.org -k ${{secrets.NUGET_TOKEN}}
dotnet nuget push src\${{env.PROJECT_NAME}}.Signed\bin\Release\*.nupkg -s nuget.org -k ${{secrets.NUGET_TOKEN}}
} else {
echo "publish is only enabled by tagging with a release tag"
}
uses: xoofx/.github/.github/workflows/dotnet.yml@main
with:
dotnet-version: |
6.0
8.0
9.0
secrets:
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>

44
.github/workflows/test-netstandard.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
name: Test netstandard
on: pull_request
jobs:
test-netstandard:
runs-on: ubuntu-latest
strategy:
matrix:
netstandard-version: ['netstandard2.0', 'netstandard2.1']
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
- name: Patch build to test ${{ matrix.netstandard-version }}
run: |
cd src
sed -i 's/<TargetFrameworks>.*<\/TargetFrameworks>/<TargetFrameworks>${{ matrix.netstandard-version }}<\/TargetFrameworks>/' Markdig/Markdig.targets
sed -i 's/<TargetFrameworks>.*<\/TargetFrameworks>/<TargetFrameworks>net8.0;net9.0<\/TargetFrameworks>/' Markdig.Tests/Markdig.Tests.csproj
echo "Markdig.targets TFMs:"
grep "TargetFrameworks" Markdig/Markdig.targets
echo "Markdig.Tests.csproj TFMs:"
grep "TargetFrameworks" Markdig.Tests/Markdig.Tests.csproj
- name: Restore dependencies
run: dotnet restore src/Markdig.Tests/Markdig.Tests.csproj
- name: Test Debug
run: |
dotnet build src/Markdig.Tests/Markdig.Tests.csproj -c Debug --no-restore
dotnet test src/Markdig.Tests/Markdig.Tests.csproj -c Debug --no-build
- name: Test Release
run: |
dotnet build src/Markdig.Tests/Markdig.Tests.csproj -c Release --no-restore
dotnet test src/Markdig.Tests/Markdig.Tests.csproj -c Release --no-build

5
.gitignore vendored
View File

@@ -8,6 +8,8 @@
*.sln.docstates
*.nuget.props
*.nuget.targets
src/.idea
BenchmarkDotNet.Artifacts
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
@@ -239,3 +241,6 @@ _Pvt_Extensions
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
# Remove artifacts produced by dotnet-releaser
artifacts-dotnet-releaser/

View File

@@ -1,11 +1,35 @@
# Changelog
## 0.27.0 (23 Jan 2022)
- Fix link reference definition parse bug with title and CRLF ([PR #590](https://github.com/lunet-io/markdig/pull/590))
- Move tests to net6.0 ([PR #560](https://github.com/lunet-io/markdig/pull/560))
## 0.26.0 (27 Aug 2021)
- Fix rendering diff between line endings ([PR #560](https://github.com/lunet-io/markdig/pull/560))
- Make Mathematics extension respect EnableHtml* options ([PR #570](https://github.com/lunet-io/markdig/pull/570))
## 0.25.0 (10 June 2021)
- Fix regression when parsing link reference definitions (#543)
- Make digits in JiraKey's posible ([PR #548](https://github.com/lunet-io/markdig/pull/548))
## 0.24.0 (20 Mar 2021)
- Add support for roundtrip Markdown ([PR #481](https://github.com/lunet-io/markdig/pull/481))
- Introduction of nullability ([PR #522](https://github.com/lunet-io/markdig/pull/522) [PR #524](https://github.com/lunet-io/markdig/pull/524) [PR #525](https://github.com/lunet-io/markdig/pull/525) [PR #526](https://github.com/lunet-io/markdig/pull/526) [PR #527](https://github.com/lunet-io/markdig/pull/527))
- Various internal cleanup and small performance improvements ([PR #521](https://github.com/lunet-io/markdig/pull/521) [PR #524](https://github.com/lunet-io/markdig/pull/524) [PR #525](https://github.com/lunet-io/markdig/pull/525) [PR #529](https://github.com/lunet-io/markdig/pull/529) [PR #531](https://github.com/lunet-io/markdig/pull/531) [PR #532](https://github.com/lunet-io/markdig/pull/532))
## 0.23.0 (16 Jan 2021)
- Add depth limits to avoid pathological-case parsing times/StackOverflows (#500)
- Breaking change: rename AutolineInlineParser to AutolinkInlineParser
## 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

36
contributing.md Normal file
View File

@@ -0,0 +1,36 @@
# How to Contribute
Thanks for your interest in contributing to `Markdig`! Here are a few general guidelines on contributing and
reporting bugs that we ask you to review. Following these guidelines helps to communicate that you respect the time of
the contributors managing and developing this open source project.
## Reporting Issues
Before reporting a new issue, please ensure that the issue was not already reported or fixed by searching through our
[issues list](https://github.com/xoofx/markdig/issues).
When creating a new issue, please be sure to include a **title and clear description**, as much relevant information as
possible, and, if possible, a test case.
## Sending Pull Requests
Before sending a new pull request, take a look at existing pull requests and issues to see if the proposed change or fix
has been discussed in the past, or if the change was already implemented but not yet released.
We expect new pull requests to include tests for any affected behavior, and, as we follow semantic versioning, we may
reserve breaking changes until the next major version release.
## Other Ways to Contribute
We welcome anyone that wants to contribute to `Markdig` to triage and reply to open issues to help troubleshoot
and fix existing bugs. Here is what you can do:
- Help ensure that existing issues follows the recommendations from the _[Reporting Issues](#reporting-issues)_ section,
providing feedback to the issue's author on what might be missing.
instructions and code samples.
- Review existing pull requests, and testing patches against real existing applications that use `Markdig`.
- Write a test, or add a missing test case to an existing test.
Thanks again for your interest on contributing to `Markdig`!
:heart:

134
doc/parsing-ast.md Normal file
View File

@@ -0,0 +1,134 @@
# The Abstract Syntax Tree
If successful, the `Markdown.Parse(...)` method returns the abstract syntax tree (AST) of the source text.
This will be an object of the `MarkdownDocument` type, which is in turn derived from a more general block container and is part of a larger taxonomy of classes which represent different semantic constructs of a markdown syntax tree.
This document will discuss the different types of elements within the Markdig representation of the AST.
## Structure of the AST
Within Markdig, there are two general types of node in the markdown syntax tree: `Block`, and `Inline`. Block nodes may contain inline nodes, but the reverse is not true. Blocks may contain other blocks, and inlines may contain other inlines.
The root of the AST is the `MarkdownDocument` which is itself derived from a container block but also contains information on the line count and starting positions within the document. Nodes in the AST have links both to parent and children, allowing the edges in the tree to be traversed efficiently in either direction.
Different semantic constructs are represented by types derived from the `Block` and `Inline` types, which are both `abstract` themselves. These elements are produced by `BlockParser` and `InlineParser` derived types, respectively, and so new constructs can be added with the implementation of a new block or inline parser and a new block or inline type, as well as an extension to register it in the pipeline. For more information on extending Markdig this way refer to the [Extensions/Parsers](parsing-extensions.md) document.
The AST is assembled by the static method `Markdown.Parse(...)` using the collections of block and inline parsers contained in the `MarkdownPipeline`. For more detailed information refer to the [Markdig Parsing Overview](parsing-overview.md) document.
### Quick Examples: Descendants API
The easiest way to traverse the abstract syntax tree is with a group of extension methods that have the name `Descendants`. Several different overloads exist to allow it to search for both `Block` and `Inline` elements, starting from any node in the tree.
The `Descendants` methods return `IEnumerable<MarkdownObject>` or `IEnumerable<T>` as their results. Internally they are using `yield return` to perform edge traversals lazily.
#### Depth-First Like Traversal of All Elements
```csharp
MarkdownDocument result = Markdown.Parse(sourceText, pipeline);
// Iterate through all MarkdownObjects in a depth-first order
foreach (var item in result.Descendants())
{
Console.WriteLine(item.GetType());
// You can use pattern matching to isolate elements of certain type,
// otherwise you can use the filtering mechanism demonstrated in the
// next section
if (item is ListItemBlock listItem)
{
// ...
}
}
```
#### Filtering of Specific Child Types
Filtering can be performed using the `Descendants<T>()` method, in which T is required to be derived from `MarkdownObject`.
```csharp
MarkdownDocument result = Markdown.Parse(sourceText, pipeline);
// Iterate through all ListItem blocks
foreach (var item in result.Descendants<ListItemBlock>())
{
// ...
}
// Iterate through all image links
foreach (var item in result.Descendants<LinkInline>().Where(x => x.IsImage))
{
// ...
}
```
#### Combined Hierarchies
The `Descendants` method can be used on any `MarkdownObject`, not just the root node, so complex hierarchies can be queried.
```csharp
MarkdownDocument result = Markdown.Parse(sourceText, pipeline);
// Find all Emphasis inlines which descend from a ListItem block
var items = document.Descendants<ListItemBlock>()
.Select(block => block.Descendants<EmphasisInline>());
// Find all Emphasis inlines whose direct parent block is a ListItem
var other = document.Descendants<EmphasisInline>()
.Where(inline => inline.ParentBlock is ListItemBlock);
```
## Block Elements
Block elements all derive from `Block` and may be one of two types:
1. `ContainerBlock`, which is a block which holds other blocks (`MarkdownDocument` is itself derived from this)
2. `LeafBlock`, which is a block that has no child blocks, but may contain inlines
Block elements in markdown refer to things like paragraphs, headings, lists, code, etc. Most blocks may contain inlines, with the exception of things like code blocks.
### Properties of Blocks
The following are properties of `Block` objects which warrant elaboration. For a full list of properties see the generated API documentation (coming soon).
#### Block Parent
All blocks have a reference to a parent (`Parent`) of type `ContainerBlock?`, which allows for efficient traversal up the abstract syntax tree. The parent will be `null` in the case of the root node (the `MarkdownDocument`).
#### Parser
All blocks have a reference to a parser (`Parser`) of type `BlockParser?` which refers to the instance of the parser which created this block.
#### IsOpen Flag
Blocks have an `IsOpen` boolean flag which is set true while they're being parsed and then closed when parsing is complete.
Blocks are created by `BlockParser` objects which are managed by an instance of a `BlockProcessor` object. During the parsing algorithm the `BlockProcessor` maintains a list of all currently open `Block` objects as it steps through the source line by line. The `IsOpen` flag indicates to the `BlockProcessor` that the block should remain open as the next line begins. If the `IsOpen` flag is not directly set by the `BlockParser` on each line, the `BlockProcessor` will consider the `Block` fully parsed and will no longer call its `BlockParser` on it.
#### IsBreakable Flag
Blocks are either breakable or not, specified by the `IsBreakable` flag. If a block is non-breakable it indicates to the parser that the close condition of any parent container do not apply so long as the non-breakable child block is still open.
The only built-in example of this is the `FencedCodeBlock`, which, if existing as the child of a container block of some sort, will prevent that container from being closed before the `FencedCodeBlock` is closed, since any characters inside the `FencedCodeBlock` are considered to be valid code and not the container's close condition.
#### RemoveAfterProcessInlines
## Inline Elements
Inlines in markdown refer to things like embellishments (italics, bold, underline, etc), links, urls, inline code, images, etc.
Inline elements may be one of two types:
1. `Inline`, whose parent is always a `ContainerInline`
2. `ContainerInline`, derived from `Inline`, which contains other inlines. `ContainerInline` also has a `ParentBlock` property of type `LeafBlock?`
**(Is there anything special worth documenting about inlines or types of inlines?)**
## The SourceSpan Struct
If the pipeline was configured with `.UsePreciseSourceLocation()`, all elements in the abstract syntax tree will contain a reference to the location in the original source where they occurred. This is done with the `SourceSpan` type, a custom Markdig `struct` which provides a start and end location.
All objects derived from `MarkdownObject` contain the `Span` property, which is of type `SourceSpan`.

127
doc/parsing-extensions.md Normal file
View File

@@ -0,0 +1,127 @@
# Extensions and Parsers
Markdig was [implemented in such a way](http://xoofx.github.io/blog/2016/06/13/implementing-a-markdown-processor-for-dotnet/) as to be extremely pluggable, with even basic behaviors being mutable and extendable.
The basic mechanism for extension of Markdig is the `IMarkdownExtension` interface, which allows any implementing class to be registered with the pipeline builder and thus to directly modify the collections of `BlockParser` and `InlineParser` objects which end up in the pipeline.
This document discusses the `IMarkdownExtension` interface, the `BlockParser` abstract base class, and the `InlineParser` abstract base class, which together are the foundation of extending Markdig's parsing machinery.
## Creating Extensions
Extensions can vary from very simple to very complicated.
A simple extension, for example, might simply find a parser already in the pipeline and modify a setting on it. An example of this is the `SoftlineBreakAsHardlineExtension`, which locates the `LineBreakInlineParser` and modifies a single boolean flag on it.
A complex extension, on the other hand, might add an entire taxonomy of new `Block` and `Inline` types, as well as several related parsers and renderers, and require being added to the the pipeline in a specific order in relation to other extensions which are already configured. The `FootnoteExtension` and `PipeTableExtension` are examples of more complex extensions.
For extensions that don't require order considerations, the implementation of the extension itself is adequate, and the extension can be added to the pipeline with the generic `Use<TExtension>()` method on the pipeline builder. For extensions which do require order considerations, it is best to create an extension method on the `MarkdownPipelineBuilder` to perform the registration. See the following two sections for further information.
### Implementation of an Extension
The [IMarkdownExtension.cs](https://github.com/xoofx/markdig/blob/master/src/Markdig/IMarkdownExtension.cs) interface specifies two methods which must be implemented.
The first, which takes only the pipeline builder as an argument, is called when the `Build()` method on the pipeline builder is invoked, and should set up any modifications to the parsers or parser collections. These parsers will then be used by the main parsing algorithm to process the source text.
```csharp
void Setup(MarkdownPipelineBuilder pipeline);
```
The second, which takes the pipeline itself and a renderer, is used to set up a rendering component in order to convert any special `MarkdownObject` types associated with the extension into an output. This is not relevant for parsing, but is necessary for rendering.
```csharp
void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer);
```
The extension can then be registered to the pipeline builder using the `Use<TExtension>()` method. A skeleton example is given below:
```csharp
public class MySpecialBlockParser : BlockParser
{
// ...
}
public class MyExtension : IMarkdownExtension
{
void Setup(MarkdownPipelineBuilder pipeline)
{
pipeline.BlockParsers.AddIfNotAlready<MySpecialBlockParser>();
}
void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) { }
}
```
```csharp
var builder = new MarkdownPipelineBuilder()
.Use<MyExtension>();
```
### Pipeline Builder Extension Methods
For extensions which require specific ordering and/or need to perform multiple operations to register with the builder, it's recommended to create an extension method.
```csharp
public static class MyExtensionMethods
{
public static MarkdownPipelineBuilder UseMyExtension(this MarkdownPipelineBuilder pipeline)
{
// Directly access or modify pipeline.Extensions here, with the ability to
// search for other extensions, insert before or after, remove other extensions,
// or modify their settings.
// ...
return pipeline;
}
}
```
### Simple Extension Example
An example of a simple extension which does not add any new parsers, but instead creates a new, horrific emphasis tag, marked by triple percentage signs. This example is based on [CitationExtension.cs](https://github.com/xoofx/markdig/blob/master/src/Markdig/Extensions/Citations/CitationExtension.cs)
```csharp
/// <summary>
/// An extension which applies to text of the form %%%text%%%
/// </summary>
public class BlinkExtension : IMarkdownExtension
{
// This setup method will be run when the pipeline builder's `Build()` method is invoked. As this
// is a simple, self-contained extension we won't be adding anything new, but rather finding an
// existing parser already in the pipeline and adding some settings to it.
public void Setup(MarkdownPipelineBuilder pipeline)
{
// We check the pipeline builder's inline parser collection and see if we can find a parser
// registered of the type EmphasisInlineParser. This is the parser which nominally handles
// bold and italic emphasis, but we know from its documentation that it is a general parser
// that can have new characters added to it.
var parser = pipeline.InlineParsers.FindExact<EmphasisInlineParser>();
// If we find the parser and it doesn't already have the % character registered, we add
// a descriptor for 3 consecutive % signs. This is specific to the EmphasisInlineParser and
// is just used here as an example.
if (parser is not null && !parser.HasEmphasisChar('%'))
{
parser.EmphasisDescriptors.Add(new EmphasisDescriptor('%', 3, 3, false));
}
}
// This method is called by the pipeline before rendering, which is a separate operation from
// parsing. This implementation is just here for the purpose of the example, in which we
// daisy-chain a delegate specific to the EmphasisInlineRenderer to cause an unconscionable tag
// to be inserted into the HTML output wherever a %%% annotated span was placed in the source.
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
if (renderer is not HtmlRenderer) return;
var emphasisRenderer = renderer.ObjectRenderers.FindExact<EmphasisInlineRenderer>();
if (emphasisRenderer is null) return;
var previousTag = emphasisRenderer.GetTag;
emphasisRenderer.GetTag = inline =>
(inline.DelimiterCount == 3 && inline.DelimiterChar == '%' ? "blink" : null)
?? previousTag(inline);
}
}
```

336
doc/parsing-overview.md Normal file
View File

@@ -0,0 +1,336 @@
# Markdig Parsing
Markdig provides efficient, regex-free parsing of markdown documents directly into an abstract syntax tree (AST). The AST is a representation of the markdown document's semantic constructs, which can be manipulated and explored programmatically.
* This document contains a general overview of the parsing system and components and their use
* The [Abstract Syntax Tree](parsing-ast.md) document contains a discussion of how Markdig represents the product of the parsing operation
* The [Extensions/Parsers](parsing-extensions.md) document explores extensions and block/inline parsers within the context of extending Markdig's parsing capabilities
## Introduction
Markdig's parsing machinery consists of two main components at its surface: the `Markdown.Parse(...)` method and the `MarkdownPipeline` type. The parsed document is represented by a `MarkdownDocument` object, which is a tree of objects derived from `MarkdownObject`, including block and inline elements.
The `Markdown` static class is the main entrypoint to the Markdig API. It contains the `Parse(...)` method, the main algorithm for parsing a markdown document. The `Parse(...)` method in turn uses a `MarkdownPipeline`, which is a sealed internal class which maintains some configuration information and the collections of parsers and extensions. The `MarkdownPipeline` determines how the parser behaves and what its capabilities are. The `MarkdownPipeline` can be modified with built-in as well as user developed extensions.
### Glossary of Relevant Types
The following is a table of some of the types relevant to parsing and mentioned in the related documentation. For an exhaustive list refer to API documentation (coming soon).
|Type|Description|
|-|-|
|`Markdown`|Static class with the entry point to the parsing algorithm via the `Parse(...)` method|
|`MarkdownPipeline`|Configuration object for the parser, contains collections of block and inline parsers and registered extensions|
|`MarkdownPipelineBuilder`|Responsible for constructing the `MarkdownPipeline`, used by client code to configure pipeline options and behaviors|
|`IMarkdownExtension`|Interface for [Extensions](#extensions-imarkdownextension) which alter the behavior of the pipeline, this is the standard mechanism for extending Markdig|
|`BlockParser`|Base type for an individual parsing component meant to identify `Block` elements in the markdown source|
|`InlineParser`|Base type for an individual parsing component meant to identify `Inline` elements within a `Block`|
|`Block`|A node in the AST representing a markdown block element, can either be a `ContainerBlock` or a `LeafBlock`|
|`Inline`|A node in the AST representing a markdown inline element|
|`MarkdownDocument`|The root node of the AST produced by the parser, derived from `ContainerBlock`|
|`MarkdownObject`|The base type of all `Block` and `Inline` derived objects (as well as `HtmlAttributes`)|
### Simple Examples
*The following are simple examples of parsing to help get you started, see the following sections for an in-depth explanation of the different parts of Markdig's parsing mechanisms*
The `MarkdownPipeline` dictate how the parser will behave. The `Markdown.Parse(...)` method will construct a default pipeline if none is provided. A default pipeline will be CommonMark compliant but nothing else.
```csharp
var markdownText = File.ReadAllText("sample.md");
// No pipeline provided means a default pipeline will be used
var document = Markdown.Parse(markdownText);
```
Pipelines can be created and configured manually, however this must be done using a `MarkdownPipelineBuilder` object, which then is configured through a fluent interface composed of extension methods.
```csharp
var markdownText = File.ReadAllText("sample.md");
// Markdig's "UseAdvancedExtensions" option includes many common extensions beyond
// CommonMark, such as citations, figures, footnotes, grid tables, mathematics
// task lists, diagrams, and more.
var pipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Build();
var document = Markdown.Parse(markdownText, pipeline);
```
Extensions can also be added individually:
```csharp
var markdownText = File.ReadAllText("sample.md");
var pipeline = new MarkdownPipelineBuilder()
.UseCitations()
.UseFootnotes()
.UseMyCustomExtension()
.Build();
var document = Markdown.Parse(markdownText, pipeline);
```
## Markdown.Parse and the MarkdownPipeline
As metioned in the [Introduction](#introduction), Markdig's parsing machinery involves two surface components: the `Markdown.Parse(...)` method, and the `MarkdownPipeline` type. The main parsing algorithm (not to be confused with individual `BlockParser` and `InlineParser` components) lives in the `Markdown.Parse(...)` static method. The `MarkdownPipeline` is responsible for configuring the behavior of the parser.
These two components are covered in further detail in the following sections.
### The MarkdownPipeline
The `MarkdownPipeline` is a sealed internal class which dictates what features the parsing algorithm has. The pipeline must be created by using a `MarkdownPipelineBuilder` as shown in the examples above.
The `MarkdownPipeline` holds configuration information and collections of extensions and parsers. Parsers fall into one of two categories:
* Block Parsers (`BlockParser`)
* Inline Parsers (`InlineParser`)
Extensions are classes implementing `IMarkdownExtension` which are allowed to add to the list of parsers, or modify existing parsers and/or renderers. They are invoked to perform their mutations on the pipeline when the pipeline is built by the `MarkdownPipelineBuilder`.
Lastly, the `MarkdownPipeline` contains a few extra elements:
* A configuration setting determining whether or not trivial elements, referred to as *trivia*, (whitespace, extra heading characters, unescaped strings, etc) are to be tracked
* A configuration setting determining whether or not nodes in the resultant abstract syntax tree will refer to their precise original locations in the source
* An optional delegate which will be invoked when the document has been processed.
* An optional `TextWriter` which will get debug logging from the parser
### The Markdown.Parse Method
`Markdown.Parse` is a static method which contains the overall parsing algorithm but not the actual parsing components, which instead are contained within the pipeline.
The `Markdown.Parse(...)` method takes a string containing raw markdown and returns a `MarkdownDocument`, which is the root node in the abstract syntax tree. The `Parse(...)` method optionally takes a pre-configured `MarkdownPipeline`, but if none is given will create a default pipeline which has minimal features.
Within the `Parse(...)` method, the following sequence of operations occur:
1. The block parsers contained in the pipeline are invoked on the raw markdown text, creating the initial tree of block elements
2. If the pipeline is configured to track markdown trivia (trivial/non-contributing elements), the blocks are expanded to absorb neighboring trivia
3. The inline parsers contained in the pipeline are now invoked on the blocks, populating the inline elements of the abstract syntax tree
4. If a delegate has been configured for when the document has completed processing, it is now invoked
5. The abstract syntax tree (`MarkdownDocument` object) is returned
## The Pipeline Builder and Extensions
The `MarkdownPipeline` determines the behavior and capabilities of the parser, and *extensions* added via the `MarkdownPipelineBuilder` determine the configuration of the pipeline.
This section discusses the pipeline builder and the concept of *extensions* in more detail.
### Extensions (IMarkdownExtension)
***Note**: This section discusses how to consume extensions by adding them to pipeline. For a discussion on how to implement an extension, refer to the [Extensions/Parsers](parsing-extensions.md) document.*
Extensions are the primary mechanism for modifying the parsers in the pipeline.
An extension is any class which implements the `IMarkdownExtension` interface found in [IMarkdownExtension.cs](https://github.com/xoofx/markdig/blob/master/src/Markdig/IMarkdownExtension.cs). This interface consists solely of two `Setup(...)` overloads, which both take a `MarkdownPipelineBuilder` as the first argument.
When the `MarkdownPipelineBuilder.Build()` method is invoked as the final stage in pipeline construction, the builder runs through the list of registered extensions in order and calls the `Setup(...)` method on each of them. The extension then has full access to modify both the parser collections themselves (by adding new parsers to it), or to find and modify existing parsers.
Because of this, *some* extensions may need to be ordered in relation to others, for instance if they modify a parser that gets added by a different extension. The `OrderedList<T>` class contains convenience methods to this end, which aid in finding other extensions by type and then being able to added an item before or after them.
### The MarkdownPipelineBuilder
Because the `MarkdownPipeline` is a sealed internal class, it cannot (and *should* not be attempted to) be created directly. Rather, the `MarkdownPipelineBuilder` manages the requisite construction of the pipeline after the configuration has been provided by the client code.
As discussed in the [section above](#the-markdownpipeline), the `MarkdownPipeline` primarily consists of a collection of block parsers and a collection of inline parsers, which are provided to the `Markdown.Parse(...)` method and thus determine its features and behavior. Both the collections and some of the parsers themselves are mutable, and the mechanism of mutation is the `Setup(...)` method of the `IMarkdownExtension` interface. This is covered in more detail in the section on [Extensions](#extensions-imarkdownextension).
#### The Fluent Interface
A collection of extension methods in the [MarkdownExtensions.cs](https://github.com/xoofx/markdig/blob/master/src/Markdig/MarkdownExtensions.cs) source file provides a convenient fluent API for the configuration of the pipeline builder. This should be considered the standard way of configuring the builder.
##### Configuration Options
There are several extension methods which apply configurations to the builder which change settings in the pipeline outside of the use of typical extensions.
|Method|Description|
|-|-|
|`.ConfigureNewLine(...)`|Takes a string which will serve as the newline delimiter during parsing|
|`.DisableHeadings()`|Disables the parsing of ATX and Setex headings|
|`.DisableHtml()`|Disables the parsing of HTML elements|
|`.EnableTrackTrivia()`|Enables the tracking of trivia (trivial elements like whitespace)|
|`.UsePreciseSourceLocation()`|Maps syntax objects to their precise location in the original source, such as would be required for syntax highlighting|
```csharp
var builder = new MarkdownPipelineBuilder()
.ConfigureNewLine("\r\n")
.DisableHeadings()
.DisableHtml()
.EnableTrackTrivia()
.UsePreciseSourceLocation();
var pipeline = builder.Build();
```
##### Adding Extensions
All extensions which ship with Markdig can be added through a dedicated fluent method, while user code which implements the `IMarkdownExtension` interface can be added with one of the `Use()` methods, or via a custom extension method implemented in the client code.
Refer to [MarkdownExtensions.cs](https://github.com/xoofx/markdig/blob/master/src/Markdig/MarkdownExtensions.cs) for a full list of extension methods:
```csharp
var builder = new MarkdownPipelineBuilder()
.UseFootnotes()
.UseFigures();
```
For custom/user-provided extensions, the `Use<TExtension>(...)` methods allow either a type to be directly added or an already constructed instance to be put into the extension container. Internally they will prevent two of the same type of extension from being added to the container.
```csharp
public class MyExtension : IMarkdownExtension
{
// ...
}
// Only works if MyExtension has an empty constructor (aka new())
var builder = new MarkdownPipelineBuilder()
.Use<MyExtension>();
```
Alternatively:
```csharp
public class MyExtension : IMarkdownExtension
{
public MyExtension(object someConfigurationObject) { /* ... */ }
// ...
}
var instance = new MyExtension(configData);
var builder = new MarkdownPipelineBuilder()
.Use(instance);
```
##### Adding Extensions with the Configure Method
The `MarkdownPipelineBuilder` has one additional method for the configuration of extensions worth mentioning: the `Configure(...)` method, which takes a `string?` of `+` delimited tokens specifying which extensions should be dynamically configured. This is a convenience method for the configuration of pipelines whose extensions are only known at runtime.
Refer to [MarkdownExtensions.cs's `Configure(...)`](https://github.com/xoofx/markdig/blob/983187eace6ba02ee16d1443c387267ad6e78f58/src/Markdig/MarkdownExtensions.cs#L538) code for the full list of extensions.
```csharp
var builder = new MarkdownPipelineBuilder()
.Configure("common+footnotes+figures");
var pipeline = builder.Build();
```
#### Manual Configuration
Internally, the fluent interface wraps manual operations on the three primary collections:
* `MarkdownPipelineBuilder.BlockParsers` - this is an `OrderedList<BlockParser>` of the block parsers
* `MarkdownPipelineBuilder.InlineParsers` - this is an `OrderedList<InlineParser>` of the inline element parsers
* `MarkdownPipelineBuilder.Extensions` - this is an `OrderedList<IMarkdownExtension>` of the extensions
All three collections are `OrderedList<T>`, which is a collection type custom to Markdig which contains special methods for finding and inserting derived types. With the builder created, manual configuration can be performed by accessing these collections and their elements and modifying them as necessary.
***Warning**: be aware that it should not be necessary to directly modify either the `BlockParsers` or the `InlineParsers` collections directly during the pipeline configuration. Rather, these can and should be modified whenever possible through the `Setup(...)` method of extensions, which will be deferred until the pipeline is actually built and will allow for ordering such that operations dependent on other operations can be accounted for.*
## Block and Inline Parsers
Let's dive deeper into the parsing system. With a configured pipeline, the `Markdown.Parse` method will run through two two conceptual passes to produce the abstract syntax tree.
1. First, `BlockProcessor.ProcessLine` is called on the file's lines, one by one, trying to identify block elements in the source
2. Next, an `InlineProcessor` is created or borrowed and run on each block to identify inline elements.
These two conceptual operations dictate Markdig's two types of parsers, both of which derive from `ParserBase<TProcessor>`.
Block parsers, derived from `BlockParser`, identify block elements from lines in the source text and push them onto the abstract syntax tree. Inline parsers, derived from `InlineParser`, identify inline elements from `LeafBlock` elements and push them into an attached container: the `ContainerInline? LeafBlock.Inline` property.
Both inline and block parsers are regex-free, and instead work on finding opening characters and then making fast read-only views into the source text.
### Block Parser
**(The contents of this section I am very unsure of, this is from my reading of the code but I could use some guidance here)**
**(Does `CanInterrupt` specifically refer to interrupting a paragraph block?)**
In order to be added to the parsing pipeline, all block parsers must be derived from `BlockParser`.
Internally, the main parsing algorithm will be stepping through the source text, using the `HasOpeningCharacter(char c)` method of the block parser collection to pre-identify parsers which *could* be opening a block at a given position in the text based on the active character. Thus any derived implementation needs to set the value of the `char[]? OpeningCharacter` property with the initial characters that might begin the block.
If a parser can potentially open a block at a place in the source text it should expect to have the `TryOpen(BlockProcessor processor)` method called. This is a virtual method that must be implemented on any derived class. The `BlockProcessor` argument is a reference to an object which stores the current state of parsing and the position in the source.
**(What are the rules concerning how the `BlockState` return type should work for `TryOpen`? I see examples returning `None`, `Continue`, `BreakDiscard`, `ContinueDiscard`. How does the return value change the algorithm behavior?)**
**(Should a new block always be pushed into `processor.NewBlocks` in the `TryOpen` method?)**
As the main parsing algorithm moves forward, it will then call `TryContinue(...)` on blocks that were opened in `TryOpen(..)`.
**(Is this where/how you close a block? Is there anything that needs to be done to perform that beyond `block.UpdateSpanEnd` and returning `BlockState.Break`?)**
### Inline Parsers
Inline parsers extract inline markdown elements from the source, but their starting point is the text of each individual `LeafBlock` produced by the block parsing process. To understand the role of each inline parser it is necessary to first understand the inline parsing process as a whole.
#### The Inline Parsing Process
After the block parsing process has occurred, the abstract syntax tree of the document has been populated only with block elements, starting from the root `MarkdownDocument` node and ending with the individual `LeafBlock` derived block elements, most of which will be `ParagraphBlocks`, but also include things like `CodeBlocks`, `HeadingBlocks`, `FigureCaptions`, and so on.
At this point, the parsing machinery will iterate through each `LeafBlock` one by one, creating and assigning its `LeafBlock.Inline` property with an empty `ContainerInline`, and then sweeping through the `LeafBlock`'s text running the inline parsers. This occurs by the following process:
Starting at the first character of the text it will run through all of its `InlineParser` objects which have that character as a possible opening character for the type of inline they extract. The parsers will run in order (as such ordering is the *only* way which conflicts between parsers are resolved, and thus is important to the overall behavior of the parsing system) and the `Match(...)` method will be called on each candidate parser, in order, until one of them returns `true`.
The `Match(...)` method will be passed a slice of the text beginning at the *specific character* being processed and running until the end of the `LeafBlock`'s complete text. If the parser can create an `Inline` element it will do so and return `true`, otherwise it will return `false`. The parser will store the created `Inline` object in the processor's `InlineProcessor.Inline` property, which as passed into the `Match(...)` method as an argument. The parser will also advance the start of the working `StringSlice` by the characters consumed in the match.
* If the parser has created an inline element and returned `true`, that element is pushed into the deepest open `ContainerInline`
* If `false` was returned, a default `LiteralInlineParser` will run instead:
* If the `InlineProcessor.Inline` property already has an existing `LiteralInline` in it, these characters will be added to the existing `LiteralInline`, effectively growing it
* If no `LiteralInline` exists in the `InlineProcessor.Inline` property, a new one will be created containing the consumed characters and pushed into the deepest open `ContainerInline`
After that, the working text of the `LeafBlock` has been conceptually shortened by the advancing start of the working `StringSlice`, moving the starting character forward. If there is still text remaining, the process repeats from the new starting character until all of the text is consumed.
At this point, when all of the source text from the `LeafBlock` has been consumed, a post-processing step occurs. `InlineParser` objects in the pipeline which also implement `IPostInlineProcessor` are invoked on the `LeafBlock`'s root `ContainerInline`. This, for example, is the mechanism by which the unstructured output of the `EmphasisInlineParser` is then restructured into cleanly nested `EmphasisInline` and `LiteralInline` elements.
#### Responsibilities of an Inline Parser
Like the block parsers, an inline parser must provide an array of opening characters with the `char[]? OpeningCharacter` property.
However, inline parsers only require one other method, the `Match(InlineProcessor processor, ref StringSlice slice)` method, which is expected to determine if a match for the related inline is located at the starting character of the slice.
Within the `Match` method a parser should:
1. Determine if a match begins at the starting character of the `slice` argument
2. If no match exists, the method should return `false` and not advance the `Start` property of the `slice` argument
3. If a match does exist, perform the following actions:
* Instantiate the appropriate `Inline` derived class and assign it to the processor argument with `processor.Inline = myInlineObject`
* Advance the `Start` property of the `slice` argument by the number of characters contained in the match, for example by using the `NextChar()`, `SkipChar()`, or other helper methods of the `StringSlice` class
* Return `true`
While parsing, the `InlineProcessor` performing the processing, which is available to the `Match` function through the `processor` argument, contains a number of properties which can be used to access the current state of parsing. For example, the `processor.Inline` property is the mechanism for returning a new inline element, but before assignment it contains the last created inline, which in turn can be accessed for its parents.
Additionally, in the case of inlines which can be expected to contain other inlines, a possible strategy is to inject an inline element derived from `DelimiterInline` when the opening delimiter is detected, then to replace the opening delimiter with the final desired element when the closing delimiter is found. This is the strategy used by the `LinkInlineParser`, for example. In such cases the tools described in the next section, such as the `ReplaceBy` method, can be used. Note that if this method is used the post-processing should be invoked on the `InlineProcessor` in order to finalize any emphasis elements. For example, in the following code adapted from the `LinkInlineParser`:
```csharp
var parent = processor.Inline?.FirstParentOfType<MyDelimiterInline>();
if (parent is null) return;
var myInline = new MySpecialInline { /* set span and other parameters here */ };
// Replace the delimiter inline with the final inline type, adopting all of its children
parent.ReplaceBy(myInline);
// Notifies processor as we are creating an inline locally
processor.Inline = myInline;
// Process emphasis delimiters
processor.PostProcessInlines(0, myInline, null, false);
```
#### Inline Post-Processing
The purpose of post-processing inlines is typically to re-structure inline elements after the initial parsing is complete and the entire structure of the inline elements within a parent container is now available in a way it was not during the parsing process. Generally this consists of removing, replacing, and re-ordering `Inline` elements.
To this end, the `Inline` abstract base class contains several helper methods intended to allow manipulation of inline elements during the post-processing phase.
|Method|Purpose|
|-|-|
|`InsertAfter(...)`|Takes a new inline as an argument and inserts it into the same parent container after this instance|
|`InsertBefore(...)`|Takes a new inline as an argument and inserts it into the same parent container before this instance|
|`Remove()`|Removes this inline from its parent container|
|`ReplaceBy(...)`|Removes this instance and replaces it with a new inline specified in the argument. Has an option to move all of the original inline's children into the new inline.|
Additionally, the `PreviousSibling` and `NextSibling` properties can be used to determine the siblings of an inline element within its parent container. The `FirstParentOfType<T>()` method can be used to search for a parent element, which is often useful when searching for `DelimiterInline` derived elements, which are implemented as containers.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

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

136
readme.md
View File

@@ -1,8 +1,8 @@
# 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 [![ci](https://github.com/xoofx/markdig/actions/workflows/ci.yml/badge.svg)](https://github.com/xoofx/markdig/actions/workflows/ci.yml) [![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">
Markdig is a fast, powerful, [CommonMark](http://commonmark.org/) compliant, extensible Markdown processor for .NET.
Markdig is a fast, powerful, [CommonMark](https://commonmark.org/) compliant, extensible Markdown processor for .NET.
> NOTE: The repository is under construction. There will be a dedicated website and proper documentation at some point!
@@ -12,18 +12,19 @@ You can **try Markdig online** and compare it to other implementations on [babel
- **Very fast parser and html renderer** (no-regexp), very lightweight in terms of GC pressure. See benchmarks
- **Abstract Syntax Tree** with precise source code location for syntax tree, useful when building a Markdown editor.
- Checkout [MarkdownEditor for Visual Studio](https://visualstudiogallery.msdn.microsoft.com/eaab33c3-437b-4918-8354-872dfe5d1bfe) powered by Markdig!
- Checkout [Markdown Editor v2 for Visual Studio 2022](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.MarkdownEditor2) powered by Markdig!
- Converter to **HTML**
- Passing more than **600+ tests** from the latest [CommonMark specs (0.29)](http://spec.commonmark.org/)
- Passing more than **600+ tests** from the latest [CommonMark specs (0.31.2)](https://spec.commonmark.org/)
- Includes all the core elements of CommonMark:
- 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))
- [**Grid tables**](src/Markdig.Tests/Specs/GridTableSpecs.md) (inspired from [Pandoc - Grid Tables](http://pandoc.org/README.html#extension-grid_tables))
- [**Extra emphasis**](src/Markdig.Tests/Specs/EmphasisExtraSpecs.md) (inspired from [Pandoc - Emphasis](http://pandoc.org/README.html#strikeout) and [Markdown-it](https://markdown-it.github.io/))
- [**Pipe tables**](src/Markdig.Tests/Specs/PipeTableSpecs.md) (inspired from GitHub tables and [PanDoc - Pipe Tables](https://pandoc.org/MANUAL.html#extension-pipe_tables))
- [**Grid tables**](src/Markdig.Tests/Specs/GridTableSpecs.md) (inspired from [Pandoc - Grid Tables](https://pandoc.org/MANUAL.html#extension-grid_tables))
- [**Extra emphasis**](src/Markdig.Tests/Specs/EmphasisExtraSpecs.md) (inspired from [Pandoc - Emphasis](https://pandoc.org/MANUAL.html#strikeout) and [Markdown-it](https://markdown-it.github.io/))
- strike through `~~`,
- Subscript `~`
- Superscript `^`
@@ -32,7 +33,7 @@ You can **try Markdig online** and compare it to other implementations on [babel
- [**Special attributes**](src/Markdig.Tests/Specs/GenericAttributesSpecs.md) or attached HTML attributes (inspired from [PHP Markdown Extra - Special Attributes](https://michelf.ca/projects/php-markdown/extra/#spe-attr))
- [**Definition lists**](src/Markdig.Tests/Specs/DefinitionListSpecs.md) (inspired from [PHP Markdown Extra - Definitions Lists](https://michelf.ca/projects/php-markdown/extra/#def-list))
- [**Footnotes**](src/Markdig.Tests/Specs/FootnotesSpecs.md) (inspired from [PHP Markdown Extra - Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes))
- [**Auto-identifiers**](src/Markdig.Tests/Specs/AutoIdentifierSpecs.md) for headings (similar to [Pandoc - Auto Identifiers](http://pandoc.org/README.html#extension-auto_identifiers))
- [**Auto-identifiers**](src/Markdig.Tests/Specs/AutoIdentifierSpecs.md) for headings (similar to [Pandoc - Auto Identifiers](https://pandoc.org/MANUAL.html#extension-auto_identifiers))
- [**Auto-links**](src/Markdig.Tests/Specs/AutoLinks.md) generates links if a text starts with `http://` or `https://` or `ftp://` or `mailto:` or `www.xxx.yyy`
- [**Task Lists**](src/Markdig.Tests/Specs/TaskListSpecs.md) inspired from [Github Task lists](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments).
- [**Extra bullet lists**](src/Markdig.Tests/Specs/ListExtraSpecs.md), supporting alpha bullet `a.` `b.` and roman bullet (`i`, `ii`...etc.)
@@ -47,7 +48,7 @@ You can **try Markdig online** and compare it to other implementations on [babel
- [**Emoji**](src/Markdig.Tests/Specs/EmojiSpecs.md) support (inspired from [Markdown-it](https://markdown-it.github.io/))
- [**SmartyPants**](src/Markdig.Tests/Specs/SmartyPantsSpecs.md) (inspired from [Daring Fireball - SmartyPants](https://daringfireball.net/projects/smartypants/))
- [**Bootstrap**](src/Markdig.Tests/Specs/BootstrapSpecs.md) class (to output bootstrap class)
- [**Diagrams**](src/Markdig.Tests/Specs/DiagramsSpecs.md) extension whenever a fenced code block contains a special keyword, it will be converted to a div block with the content as-is (currently, supports [`mermaid`](https://knsv.github.io/mermaid/) and [`nomnoml`](https://github.com/skanaar/nomnoml) diagrams)
- [**Diagrams**](src/Markdig.Tests/Specs/DiagramsSpecs.md) extension whenever a fenced code block contains a special keyword, it will be converted to a div block with the content as-is (currently, supports [`mermaid`](https://mermaid.js.org) and [`nomnoml`](https://github.com/skanaar/nomnoml) diagrams)
- [**YAML Front Matter**](src/Markdig.Tests/Specs/YamlSpecs.md) to parse without evaluating the front matter and to discard it from the HTML output (typically used for previewing without the front matter in MarkdownEditor)
- [**JIRA links**](src/Markdig.Tests/Specs/JiraLinks.md) to automatically generate links for JIRA project references (Thanks to @clarkd: https://github.com/clarkd/MarkdigJiraLinker)
- Starting with Markdig version `0.20.0+`, Markdig is compatible only with `NETStandard 2.0`, `NETStandard 2.1`, `NETCoreApp 2.1` and `NETCoreApp 3.1`.
@@ -59,6 +60,8 @@ 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 ColorCode-Universal**: `Markdown.ColorCode`](https://github.com/wbaldoumas/markdown-colorcode)
- [**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
@@ -67,7 +70,7 @@ If you are looking for support for an old .NET Framework 3.5 or 4.0, you can dow
While there is not yet a dedicated documentation, you can find from the [specs documentation](src/Markdig.Tests/Specs/readme.md) how to use these extensions.
In the meantime, you can have a "behind the scene" article about Markdig in my blog post ["Implementing a Markdown Engine for .NET"](http://xoofx.com/blog/2016/06/13/implementing-a-markdown-processor-for-dotnet/)
In the meantime, you can have a "behind the scene" article about Markdig in my blog post ["Implementing a Markdown Engine for .NET"](https://xoofx.github.io/blog/2016/06/13/implementing-a-markdown-processor-for-dotnet/)
## Download
@@ -98,9 +101,13 @@ var result = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
You can have a look at the [MarkdownExtensions](https://github.com/lunet-io/markdig/blob/master/src/Markdig/MarkdownExtensions.cs) that describes all actionable extensions (by modifying the MarkdownPipeline)
## Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated. For detailed contributing guidelines, please see [contributing.md](contributing.md).
## Build
In order to build Markdig, you need to install [.NET Core RTM](https://www.microsoft.com/net/core)
In order to build Markdig, you need to install [.NET 6.0](https://dotnet.microsoft.com/en-us/download)
## License
@@ -109,97 +116,44 @@ This software is released under the [BSD-Clause 2 license](https://github.com/lu
## Benchmarking
This is an early preview of the benchmarking against various implementations:
The latest benchmark was collected on April 23 2022, against the following implementations:
**C implementations**:
- [cmark](https://github.com/jgm/cmark) (version: 0.25.0): Reference C implementation of CommonMark, no support for extensions
- [Moonshine](https://github.com/brandonc/moonshine) (version: : popular C Markdown processor
**.NET implementations**:
- [Markdig](https://github.com/lunet-io/markdig) (version: 0.5.x): itself
- [CommonMark.NET(master)](https://github.com/Knagis/CommonMark.NET) (version: 0.11.0): CommonMark implementation for .NET, no support for extensions, port of cmark
- [CommonMark.NET(pipe_tables)](https://github.com/AMDL/CommonMark.NET/tree/pipe-tables): An evolution of CommonMark.NET, supports extensions, not released yet
- [MarkdownDeep](https://github.com/toptensoftware/markdowndeep) (version: 1.5.0): another .NET implementation
- [MarkdownSharp](https://github.com/Kiri-rin/markdownsharp) (version: 1.13.0): Open source C# implementation of Markdown processor, as featured on Stack Overflow, regexp based.
- [Marked.NET](https://github.com/T-Alex/MarkedNet) (version: 1.0.5) port of original [marked.js](https://github.com/chjj/marked) project
- [Microsoft.DocAsCode.MarkdownLite](https://github.com/dotnet/docfx/tree/dev/src/Microsoft.DocAsCode.MarkdownLite) (version: 2.0.1) used by the [docfx](https://github.com/dotnet/docfx) project
**JavaScript/V8 implementations**:
- [Strike.V8](https://github.com/SimonCropp/Strike) (version: 1.5.0) [marked.js](https://github.com/chjj/marked) running in Google V8 (not .NET based)
### Analysis of the results:
- Markdig is roughly **x100 times faster than MarkdownSharp**, **30x times faster than docfx**
- **Among the best in CPU**, Extremely competitive and often faster than other implementations (not feature wise equivalent)
- **15% to 30% less allocations** and GC pressure
Because Marked.NET, MarkdownSharp and DocAsCode.MarkdownLite are way too slow, they are not included in the following charts:
![BenchMark CPU Time](img/BenchmarkCPU.png)
![BenchMark Memory](img/BenchmarkMemory.png)
### Performance for x86:
- [Markdig](https://github.com/lunet-io/markdig) (version: 0.30.2): itself
- [cmark](https://github.com/commonmark/cmark) (version: 0.30.2): Reference C implementation of CommonMark, no support for extensions
- [CommonMark.NET(master)](https://github.com/Knagis/CommonMark.NET) (version: 0.15.1): CommonMark implementation for .NET, no support for extensions, port of cmark, deprecated.
- [MarkdownSharp](https://github.com/Kiri-rin/markdownsharp) (version: 2.0.5): Open source C# implementation of Markdown processor, as featured previously on Stack Overflow, regexp based.
```
BenchmarkDotNet-Dev=v0.9.7.0+
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-4770 CPU 3.40GHz, ProcessorCount=8
Frequency=3319351 ticks, Resolution=301.2637 ns, Timer=TSC
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
JitModules=clrjit-v4.6.1080.0
// * Summary *
Type=Program Mode=SingleRun LaunchCount=2
WarmupCount=2 TargetCount=10
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
.NET SDK=6.0.202
[Host] : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT
DefaultJob : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT
Method | Median | StdDev |Scaled | Gen 0 | Gen 1| Gen 2|Bytes Allocated/Op |
--------------------------- |------------ |---------- |------ | ------ |------|---------|------------------ |
Markdig | 5.5316 ms | 0.0372 ms | 0.71 | 56.00| 21.00| 49.00| 1,285,917.31 |
CommonMark.NET(master) | 4.7035 ms | 0.0422 ms | 0.60 | 113.00| 7.00| 49.00| 1,502,404.60 |
CommonMark.NET(pipe_tables) | 5.6164 ms | 0.0298 ms | 0.72 | 111.00| 56.00| 49.00| 1,863,128.13 |
MarkdownDeep | 7.8193 ms | 0.0334 ms | 1.00 | 120.00| 56.00| 49.00| 1,884,854.85 |
cmark | 4.2698 ms | 0.1526 ms | 0.55 | -| -| -| NA |
Moonshine | 6.0929 ms | 0.1053 ms | 1.28 | -| -| -| NA |
Strike.V8 | 10.5895 ms | 0.0492 ms | 1.35 | -| -| -| NA |
Marked.NET | 207.3169 ms | 5.2628 ms | 26.51 | 0.00| 0.00| 0.00| 303,125,228.65 |
MarkdownSharp | 675.0185 ms | 2.8447 ms | 86.32 | 40.00| 27.00| 41.00| 2,413,394.17 |
Microsoft DocfxMarkdownLite | 166.3357 ms | 0.4529 ms | 21.27 |4,452.00|948.00|11,167.00| 180,218,359.60 |
| Method | Mean | Error | StdDev |
|------------------ |-----------:|----------:|----------:|
| markdig | 1.979 ms | 0.0221 ms | 0.0185 ms |
| cmark | 2.571 ms | 0.0081 ms | 0.0076 ms |
| CommonMark.NET | 2.016 ms | 0.0169 ms | 0.0158 ms |
| MarkdownSharp | 221.455 ms | 1.4442 ms | 1.3509 ms |
```
### Performance for x64:
- Markdig is roughly **x100 times faster than MarkdownSharp**
- **20% faster than the reference cmark C implementation**
```
BenchmarkDotNet-Dev=v0.9.6.0+
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3319351 ticks, Resolution=301.2637 ns, Timer=TSC
HostCLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT]
JitModules=clrjit-v4.6.1080.0
## Sponsors
Type=Program Mode=SingleRun LaunchCount=2
WarmupCount=2 TargetCount=10
Supports this project with a monthly donation and help me continue improving it. \[[Become a sponsor](https://github.com/sponsors/xoofx)\]
Method | Median | StdDev | Gen 0 | Gen 1 | Gen 2 | Bytes Allocated/Op |
--------------------- |---------- |---------- |------- |------- |------ |------------------- |
TestMarkdig | 5.5276 ms | 0.0402 ms | 109.00 | 96.00 | 84.00 | 1,537,027.66 |
TestCommonMarkNet | 4.4661 ms | 0.1190 ms | 157.00 | 96.00 | 84.00 | 1,747,432.06 |
TestCommonMarkNetNew | 5.3151 ms | 0.0815 ms | 229.00 | 168.00 | 84.00 | 2,323,922.97 |
TestMarkdownDeep | 7.4076 ms | 0.0617 ms | 318.00 | 186.00 | 84.00 | 2,576,728.69 |
```
## Donate
If you are using this library and find it useful for your project, please consider a donation for it!
[![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 src="https://github.com/lilith.png?size=200" width="64px;" style="border-radius: 50%" alt="lilith"/>](https://github.com/lilith) Lilith River, author of [Imageflow Server, an easy on-demand
image editing, optimization, and delivery server](https://github.com/imazen/imageflow-server)
## Credits
Thanks to the fantastic work done by [John Mac Farlane](http://johnmacfarlane.net/) for the CommonMark specs and all the people involved in making Markdown a better standard!
Thanks to the fantastic work done by [John Mac Farlane](https://johnmacfarlane.net/) for the CommonMark specs and all the people involved in making Markdown a better standard!
This project would not have been possible without this huge foundation.
@@ -207,7 +161,7 @@ Thanks also to the project [BenchmarkDotNet](https://github.com/PerfDotNet/Bench
Some decoding part (e.g HTML [EntityHelper.cs](https://github.com/lunet-io/markdig/blob/master/src/Markdig/Helpers/EntityHelper.cs)) have been re-used from [CommonMark.NET](https://github.com/Knagis/CommonMark.NET)
Thanks to the work done by @clarkd on the JIRA Link extension (https://github.com/clarkd/MarkdigJiraLinker), now included with this project!
Thanks to the work done by @clarkd on the [JIRA Link extension](https://github.com/clarkd/MarkdigJiraLinker), now included with this project!
## Author
Alexandre MUTEL aka [xoofx](http://xoofx.com)
Alexandre MUTEL aka [xoofx](https://xoofx.github.io/)

View File

@@ -0,0 +1,23 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
<PackageVersion Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.14.0" />
<PackageVersion Include="CommonMark.NET" Version="0.15.1" />
<PackageVersion Include="Markdown" Version="2.2.1" />
<PackageVersion Include="MarkdownSharp" Version="2.0.5" />
<PackageVersion Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.23.0" />
<PackageVersion Include="Microsoft.Diagnostics.Runtime" Version="3.1.512801" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="MinVer" Version="6.0.0" />
<PackageVersion Include="NUnit" Version="4.4.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="5.2.0" />
<PackageVersion Include="SharpFuzz" Version="2.2.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'netstandard2.0'">
<PackageVersion Include="System.Memory" Version="4.6.3" />
</ItemGroup>
</Project>

View File

@@ -1,40 +1,38 @@
// 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.
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Testamina.Markdig.Benchmarks
{
public static class CommonMarkLib
{
public static string ToHtml(string text)
{
unsafe
{
var textAsArray = Encoding.UTF8.GetBytes(text);
namespace Testamina.Markdig.Benchmarks;
fixed (void* ptext = textAsArray)
public static class CommonMarkLib
{
public static string ToHtml(string text)
{
unsafe
{
var textAsArray = Encoding.UTF8.GetBytes(text);
fixed (void* ptext = textAsArray)
{
var ptr = (byte*)cmark_markdown_to_html(new IntPtr(ptext), text.Length);
int length = 0;
while (ptr[length] != 0)
{
var ptr = (byte*)cmark_markdown_to_html(new IntPtr(ptext), text.Length);
int length = 0;
while (ptr[length] != 0)
{
length++;
}
var buffer = new byte[length];
Marshal.Copy(new IntPtr(ptr), buffer, 0, length);
var result = Encoding.UTF8.GetString(buffer);
Marshal.FreeHGlobal(new IntPtr(ptr));
return result;
length++;
}
var buffer = new byte[length];
Marshal.Copy(new IntPtr(ptr), buffer, 0, length);
var result = Encoding.UTF8.GetString(buffer);
Marshal.FreeHGlobal(new IntPtr(ptr));
return result;
}
}
// char *cmark_markdown_to_html(const char *text, size_t len, int options);
[DllImport("cmark", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr cmark_markdown_to_html(IntPtr charBuffer, int len, int options = 0);
}
// char *cmark_markdown_to_html(const char *text, size_t len, int options);
[DllImport("cmark", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr cmark_markdown_to_html(IntPtr charBuffer, int len, int options = 0);
}

View File

@@ -1,47 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net471</TargetFrameworks>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPackable>false</IsPackable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<None Remove="spec.md" />
</ItemGroup>
<ItemGroup>
<Reference Include="CommonMarkNew, Version=0.1.0.0, Culture=neutral, PublicKeyToken=001ef8810438905d, processorArchitecture=MSIL">
<HintPath>lib\CommonMarkNew.dll</HintPath>
<SpecificVersion>False</SpecificVersion>
<Aliases>newcmark</Aliases>
<Private>True</Private>
</Reference>
<Reference Include="MoonShine">
<HintPath>lib\MoonShine.dll</HintPath>
</Reference>
<Reference Include="MarkdownDeep">
<HintPath>lib\MarkdownDeep.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Content Include="cmark.dll">
<HintPath>cmark.dll</HintPath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="libsundown.dll">
<HintPath>libsundown.dll</HintPath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="spec.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.10.6" />
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.10.6" />
<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="BenchmarkDotNet" />
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" />
<PackageReference Include="CommonMark.NET" />
<PackageReference Include="Markdown" />
<PackageReference Include="MarkdownSharp" />
<PackageReference Include="Microsoft.Diagnostics.Runtime" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Markdig\Markdig.csproj" />

View File

@@ -0,0 +1,81 @@
// 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 BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnosers;
using Markdig;
namespace Testamina.Markdig.Benchmarks.PipeTable;
/// <summary>
/// Benchmark for pipe table parsing performance, especially for large tables.
/// Tests the performance of PipeTableParser with varying table sizes.
/// </summary>
[MemoryDiagnoser]
[GcServer(true)] // Use server GC to get more comprehensive GC stats
public class PipeTableBenchmark
{
private string _100Rows = null!;
private string _500Rows = null!;
private string _1000Rows = null!;
private string _1500Rows = null!;
private string _5000Rows = null!;
private string _10000Rows = null!;
private MarkdownPipeline _pipeline = null!;
[GlobalSetup]
public void Setup()
{
// Pipeline with pipe tables enabled (part of advanced extensions)
_pipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Build();
// Generate tables of various sizes
// Note: Before optimization, 5000+ rows hit depth limit due to nested tree structure.
// After optimization, these should work.
_100Rows = PipeTableGenerator.Generate(rows: 100, columns: 5);
_500Rows = PipeTableGenerator.Generate(rows: 500, columns: 5);
_1000Rows = PipeTableGenerator.Generate(rows: 1000, columns: 5);
_1500Rows = PipeTableGenerator.Generate(rows: 1500, columns: 5);
_5000Rows = PipeTableGenerator.Generate(rows: 5000, columns: 5);
_10000Rows = PipeTableGenerator.Generate(rows: 10000, columns: 5);
}
[Benchmark(Description = "PipeTable 100 rows x 5 cols")]
public string Parse100Rows()
{
return Markdown.ToHtml(_100Rows, _pipeline);
}
[Benchmark(Description = "PipeTable 500 rows x 5 cols")]
public string Parse500Rows()
{
return Markdown.ToHtml(_500Rows, _pipeline);
}
[Benchmark(Description = "PipeTable 1000 rows x 5 cols")]
public string Parse1000Rows()
{
return Markdown.ToHtml(_1000Rows, _pipeline);
}
[Benchmark(Description = "PipeTable 1500 rows x 5 cols")]
public string Parse1500Rows()
{
return Markdown.ToHtml(_1500Rows, _pipeline);
}
[Benchmark(Description = "PipeTable 5000 rows x 5 cols")]
public string Parse5000Rows()
{
return Markdown.ToHtml(_5000Rows, _pipeline);
}
[Benchmark(Description = "PipeTable 10000 rows x 5 cols")]
public string Parse10000Rows()
{
return Markdown.ToHtml(_10000Rows, _pipeline);
}
}

View File

@@ -0,0 +1,61 @@
// 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.Text;
namespace Testamina.Markdig.Benchmarks.PipeTable;
/// <summary>
/// Generates pipe table markdown content for benchmarking purposes.
/// </summary>
public static class PipeTableGenerator
{
private const int DefaultCellWidth = 10;
/// <summary>
/// Generates a pipe table in markdown format.
/// </summary>
/// <param name="rows">Number of data rows (excluding header)</param>
/// <param name="columns">Number of columns</param>
/// <param name="cellWidth">Width of each cell content (default: 10)</param>
/// <returns>Pipe table markdown string</returns>
public static string Generate(int rows, int columns, int cellWidth = DefaultCellWidth)
{
var sb = new StringBuilder();
// Header row
sb.Append('|');
for (int col = 0; col < columns; col++)
{
sb.Append(' ');
sb.Append($"Header {col + 1}".PadRight(cellWidth));
sb.Append(" |");
}
sb.AppendLine();
// Separator row (with dashes)
sb.Append('|');
for (int col = 0; col < columns; col++)
{
sb.Append(new string('-', cellWidth + 2));
sb.Append('|');
}
sb.AppendLine();
// Data rows
for (int row = 0; row < rows; row++)
{
sb.Append('|');
for (int col = 0; col < columns; col++)
{
sb.Append(' ');
sb.Append($"R{row + 1}C{col + 1}".PadRight(cellWidth));
sb.Append(" |");
}
sb.AppendLine();
}
return sb.ToString();
}
}

View File

@@ -1,147 +1,86 @@
// 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.
extern alias newcmark;
using System;
using System.Diagnostics;
using System.IO;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;
using Markdig;
using Testamina.Markdig.Benchmarks.PipeTable;
namespace Testamina.Markdig.Benchmarks
namespace Testamina.Markdig.Benchmarks;
//[BenchmarkTask(platform: BenchmarkPlatform.X64, jitVersion: BenchmarkJitVersion.RyuJit, processCount: 1, warmupIterationCount: 2)]
public class Program
{
//[BenchmarkTask(platform: BenchmarkPlatform.X64, jitVersion: BenchmarkJitVersion.RyuJit, processCount: 1, warmupIterationCount: 2)]
public class Program
private string text;
public Program()
{
private string text;
//text = File.ReadAllText("progit.md");
text = File.ReadAllText("spec.md");
}
public Program()
//[Benchmark(Description = "TestMarkdig", OperationsPerInvoke = 4096)]
[Benchmark(Description = "markdig")]
public void TestMarkdig()
{
//var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
Markdown.ToHtml(text);
//File.WriteAllText("spec.html", writer.ToString());
}
[Benchmark(Description = "cmark")]
public void TestCommonMarkCpp()
{
//var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
CommonMarkLib.ToHtml(text);
//File.WriteAllText("spec.html", writer.ToString());
}
[Benchmark(Description = "CommonMark.NET")]
public void TestCommonMarkNet()
{
////var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
// var reader = new StringReader(text);
//CommonMark.CommonMarkConverter.Parse(reader);
//CommonMark.CommonMarkConverter.Parse(reader);
//reader.Dispose();
//var writer = new StringWriter();
CommonMark.CommonMarkConverter.Convert(text);
//writer.Flush();
//writer.ToString();
}
[Benchmark(Description = "MarkdownSharp")]
public void TestMarkdownSharp()
{
new MarkdownSharp.Markdown().Transform(text);
}
static void Main(string[] args)
{
var config = ManualConfig.Create(DefaultConfig.Instance);
//var gcDiagnoser = new MemoryDiagnoser();
//config.Add(new Job { Mode = Mode.SingleRun, LaunchCount = 2, WarmupCount = 2, IterationTime = 1024, TargetCount = 10 });
//config.Add(new Job { Mode = Mode.Throughput, LaunchCount = 2, WarmupCount = 2, TargetCount = 10 });
//config.Add(gcDiagnoser);
//var config = DefaultConfig.Instance;
// Run specific benchmarks based on command line arguments
if (args.Length > 0 && args[0] == "--pipetable")
{
//text = File.ReadAllText("progit.md");
text = File.ReadAllText("spec.md");
BenchmarkRunner.Run<PipeTableBenchmark>(config);
}
//[Benchmark(Description = "TestMarkdig", OperationsPerInvoke = 4096)]
[Benchmark]
public void TestMarkdig()
else
{
//var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
Markdown.ToHtml(text);
//File.WriteAllText("spec.html", writer.ToString());
}
[Benchmark]
public void TestCommonMarkCpp()
{
//var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
CommonMarkLib.ToHtml(text);
//File.WriteAllText("spec.html", writer.ToString());
}
[Benchmark]
public void TestCommonMarkNet()
{
////var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
// var reader = new StringReader(text);
//CommonMark.CommonMarkConverter.Parse(reader);
//CommonMark.CommonMarkConverter.Parse(reader);
//reader.Dispose();
//var writer = new StringWriter();
CommonMark.CommonMarkConverter.Convert(text);
//writer.Flush();
//writer.ToString();
}
[Benchmark]
public void TestCommonMarkNetNew()
{
////var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
// var reader = new StringReader(text);
//CommonMark.CommonMarkConverter.Parse(reader);
//CommonMark.CommonMarkConverter.Parse(reader);
//reader.Dispose();
//var writer = new StringWriter();
newcmark::CommonMark.CommonMarkConverter.Convert(text);
//writer.Flush();
//writer.ToString();
}
[Benchmark]
public void TestMarkdownDeep()
{
new MarkdownDeep.Markdown().Transform(text);
}
[Benchmark]
public void TestMarkdownSharp()
{
new MarkdownSharp.Markdown().Transform(text);
}
[Benchmark]
public void TestMoonshine()
{
Sundown.MoonShine.Markdownify(text);
}
static void Main(string[] args)
{
bool markdig = args.Length == 0;
bool simpleBench = false;
if (simpleBench)
{
var clock = Stopwatch.StartNew();
var program = new Program();
GC.Collect(2, GCCollectionMode.Forced, true);
var gc0 = GC.CollectionCount(0);
var gc1 = GC.CollectionCount(1);
var gc2 = GC.CollectionCount(2);
const int count = 12*64;
for (int i = 0; i < count; i++)
{
if (markdig)
{
program.TestMarkdig();
}
else
{
program.TestCommonMarkNetNew();
}
}
clock.Stop();
Console.WriteLine((markdig ? "MarkDig" : "CommonMark") + $" => time: {(double)clock.ElapsedMilliseconds/count}ms (total {clock.ElapsedMilliseconds}ms)");
DumpGC(gc0, gc1, gc2);
}
else
{
//new TestMatchPerf().TestMatch();
var config = ManualConfig.Create(DefaultConfig.Instance);
//var gcDiagnoser = new MemoryDiagnoser();
//config.Add(new Job { Mode = Mode.SingleRun, LaunchCount = 2, WarmupCount = 2, IterationTime = 1024, TargetCount = 10 });
//config.Add(new Job { Mode = Mode.Throughput, LaunchCount = 2, WarmupCount = 2, TargetCount = 10 });
//config.Add(gcDiagnoser);
//var config = DefaultConfig.Instance;
BenchmarkRunner.Run<Program>(config);
//BenchmarkRunner.Run<TestDictionary>(config);
//BenchmarkRunner.Run<TestMatchPerf>();
//BenchmarkRunner.Run<TestStringPerf>();
}
}
private static void DumpGC(int gc0, int gc1, int gc2)
{
Console.WriteLine($"gc0: {GC.CollectionCount(0)-gc0}");
Console.WriteLine($"gc1: {GC.CollectionCount(1)-gc1}");
Console.WriteLine($"gc2: {GC.CollectionCount(2)-gc2}");
BenchmarkRunner.Run<Program>(config);
}
//BenchmarkRunner.Run<TestDictionary>(config);
//BenchmarkRunner.Run<TestMatchPerf>();
//BenchmarkRunner.Run<TestStringPerf>();
}
}

View File

@@ -2,81 +2,79 @@
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace Testamina.Markdig.Benchmarks
namespace Testamina.Markdig.Benchmarks;
public class TextRegexHelper
{
public class TextRegexHelper
private readonly Dictionary<string, string> replacers;
private readonly Regex regex;
public TextRegexHelper(Dictionary<string, string> replacers)
{
private readonly Dictionary<string, string> replacers;
private readonly Regex regex;
this.replacers = replacers;
var builder = new StringBuilder();
public TextRegexHelper(Dictionary<string, string> replacers)
// (?<1>:value:?)|(?<1>:noo:?)
foreach (var replace in replacers)
{
this.replacers = replacers;
var builder = new StringBuilder();
// (?<1>:value:?)|(?<1>:noo:?)
foreach (var replace in replacers)
var matchStr = Regex.Escape(replace.Key);
if (builder.Length > 0)
{
var matchStr = Regex.Escape(replace.Key);
if (builder.Length > 0)
{
builder.Append('|');
}
builder.Append("(?<1>").Append(matchStr).Append("?)");
builder.Append('|');
}
regex = new Regex(builder.ToString());
builder.Append("(?<1>").Append(matchStr).Append("?)");
}
public bool TryMatch(string text, int offset, out string matchText, out string replaceText)
{
replaceText = null;
matchText = null;
var result = regex.Match(text, offset);
if (!result.Success)
{
return false;
}
matchText = result.Groups[1].Value;
replaceText = replacers[matchText];
return true;
}
regex = new Regex(builder.ToString());
}
/*
public class TestMatchPerf
public bool TryMatch(string text, int offset, out string matchText, out string replaceText)
{
private readonly TextMatchHelper matcher;
public TestMatchPerf()
replaceText = null;
matchText = null;
var result = regex.Match(text, offset);
if (!result.Success)
{
var replacers = new Dictionary<string, string>();
for (int i = 0; i < 1000; i++)
{
replacers.Add($":z{i}:", i.ToString());
}
replacers.Add(":abc:", "yes");
matcher = new TextMatchHelper(new HashSet<string>(replacers.Keys));
return false;
}
[Benchmark]
public void TestMatch()
{
matchText = result.Groups[1].Value;
replaceText = replacers[matchText];
return true;
}
}
for (int i = 0; i < 1000; i++)
{
string matchText;
//var text = ":z150: this is a long string";
var text = ":z1:";
matcher.TryMatch(text, 0, text.Length, out matchText);
}
/*
public class TestMatchPerf
{
private readonly TextMatchHelper matcher;
public TestMatchPerf()
{
var replacers = new Dictionary<string, string>();
for (int i = 0; i < 1000; i++)
{
replacers.Add($":z{i}:", i.ToString());
}
replacers.Add(":abc:", "yes");
matcher = new TextMatchHelper(new HashSet<string>(replacers.Keys));
}
[Benchmark]
public void TestMatch()
{
for (int i = 0; i < 1000; i++)
{
string matchText;
//var text = ":z150: this is a long string";
var text = ":z1:";
matcher.TryMatch(text, 0, text.Length, out matchText);
}
}
*/
}
}
*/

View File

@@ -1,58 +1,57 @@
using System.IO;
using BenchmarkDotNet.Attributes;
using Markdig.Renderers;
namespace Testamina.Markdig.Benchmarks
namespace Testamina.Markdig.Benchmarks;
public class TestStringPerf
{
public class TestStringPerf
private string text;
public TestStringPerf()
{
private string text;
public TestStringPerf()
{
text = new string('a', 1000);
}
[Benchmark]
public void TestIndexOfAny()
{
var writer = new HtmlRenderer(new StringWriter());
for (int i = 0; i < 100; i++)
{
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
}
}
//[Benchmark]
//public void TestCustomIndexOfAny()
//{
// var writer = new HtmlRenderer(new StringWriter());
// for (int i = 0; i < 100; i++)
// {
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// }
//}
text = new string('a', 1000);
}
[Benchmark]
public void TestIndexOfAny()
{
var writer = new HtmlRenderer(new StringWriter());
for (int i = 0; i < 100; i++)
{
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
writer.WriteEscape(text, 0, text.Length);
}
}
//[Benchmark]
//public void TestCustomIndexOfAny()
//{
// var writer = new HtmlRenderer(new StringWriter());
// for (int i = 0; i < 100; i++)
// {
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// writer.WriteEscapeOptimized(text, 0, text.Length);
// }
//}
}

Binary file not shown.

4
src/Markdig.Fuzzing/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
corpus
libfuzzer-dotnet-windows.exe
crash-*
timeout-*

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SharpFuzz" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Markdig\Markdig.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,71 @@
using Markdig;
using Markdig.Renderers.Roundtrip;
using Markdig.Syntax;
using SharpFuzz;
using System.Diagnostics;
using System.Text;
ReadOnlySpanAction fuzzTarget = ParseRenderFuzzer.FuzzTarget;
if (args.Length > 0)
{
// Run the target on existing inputs
string[] files = Directory.Exists(args[0])
? Directory.GetFiles(args[0])
: [args[0]];
Debugger.Launch();
foreach (string inputFile in files)
{
fuzzTarget(File.ReadAllBytes(inputFile));
}
}
else
{
Fuzzer.LibFuzzer.Run(fuzzTarget);
}
sealed class ParseRenderFuzzer
{
private static readonly MarkdownPipeline s_advancedPipeline = new MarkdownPipelineBuilder()
.UseAdvancedExtensions()
.Build();
private static readonly ResettableRoundtripRenderer _roundtripRenderer = new();
public static void FuzzTarget(ReadOnlySpan<byte> bytes)
{
string text = Encoding.UTF8.GetString(bytes);
try
{
MarkdownDocument document = Markdown.Parse(text);
_ = document.ToHtml();
document = Markdown.Parse(text, s_advancedPipeline);
_ = document.ToHtml(s_advancedPipeline);
document = Markdown.Parse(text, trackTrivia: true);
_ = document.ToHtml();
_roundtripRenderer.Reset();
_roundtripRenderer.Render(document);
_ = Markdown.Normalize(text);
_ = Markdown.ToPlainText(text);
}
catch (Exception ex) when (IsIgnorableException(ex)) { }
}
private static bool IsIgnorableException(Exception exception)
{
return exception.Message.Contains("Markdown elements in the input are too deeply nested", StringComparison.Ordinal);
}
private sealed class ResettableRoundtripRenderer : RoundtripRenderer
{
public ResettableRoundtripRenderer() : base(new StringWriter(new StringBuilder(1024 * 1024))) { }
public new void Reset() => base.Reset();
}
}

View File

@@ -0,0 +1,86 @@
param (
[string]$configuration = $null
)
Set-StrictMode -Version Latest
$libFuzzer = "libfuzzer-dotnet-windows.exe"
$outputDir = "bin"
function Get-LibFuzzer {
param (
[string]$Path
)
$libFuzzerUrl = "https://github.com/Metalnem/libfuzzer-dotnet/releases/download/v2025.05.02.0904/libfuzzer-dotnet-windows.exe"
$expectedHash = "17af5b3f6ff4d2c57b44b9a35c13051b570eb66f0557d00015df3832709050bf"
Write-Output "Downloading libFuzzer from $libFuzzerUrl..."
try {
$tempFile = "$Path.tmp"
Invoke-WebRequest -Uri $libFuzzerUrl -OutFile $tempFile -UseBasicParsing
$downloadedHash = (Get-FileHash -Path $tempFile -Algorithm SHA256).Hash
if ($downloadedHash -eq $ExpectedHash) {
Move-Item -Path $tempFile -Destination $Path -Force
Write-Output "libFuzzer downloaded successfully to $Path"
}
else {
Write-Error "Hash validation failed."
Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue
exit 1
}
}
catch {
Write-Error "Failed to download libFuzzer: $($_.Exception.Message)"
Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue
exit 1
}
}
# Check if libFuzzer exists, download if not
if (-not (Test-Path $libFuzzer)) {
Get-LibFuzzer -Path $libFuzzer
}
$toolListOutput = dotnet tool list --global sharpFuzz.CommandLine 2>$null
if (-not ($toolListOutput -match "sharpfuzz")) {
Write-Output "Installing sharpfuzz CLI"
dotnet tool install --global sharpFuzz.CommandLine
}
if (Test-Path $outputDir) {
Remove-Item -Recurse -Force $outputDir
}
if ($configuration -eq $null) {
$configuration = "Debug"
}
dotnet publish -c $configuration -o $outputDir
$project = Join-Path $outputDir "Markdig.Fuzzing.dll"
$fuzzingTarget = Join-Path $outputDir "Markdig.dll"
Write-Output "Instrumenting $fuzzingTarget"
& sharpfuzz $fuzzingTarget
if ($LastExitCode -ne 0) {
Write-Error "An error occurred while instrumenting $fuzzingTarget"
exit 1
}
New-Item -ItemType Directory -Force -Path corpus | Out-Null
$libFuzzerArgs = @("--target_path=dotnet", "--target_arg=$project", "-timeout=10", "corpus")
# Add any additional arguments passed to the script
if ($args) {
$libFuzzerArgs += $args
}
Write-Output "Starting libFuzzer with arguments: $libFuzzerArgs"
& ./$libFuzzer @libFuzzerArgs

View File

@@ -0,0 +1,5 @@
// 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.
global using Assert = NUnit.Framework.Legacy.ClassicAssert;

View File

@@ -1,24 +1,56 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>13.0</LangVersion>
<StartupObject>Markdig.Tests.Program</StartupObject>
<SpecExecutable>$(MSBuildProjectDirectory)\..\SpecFileGen\bin\$(Configuration)\$(TargetFramework)\SpecFileGen.dll</SpecExecutable>
<SpecTimestamp>$(MSBuildProjectDirectory)\..\SpecFileGen\bin\$(Configuration)\$(TargetFramework)\SpecFileGen.timestamp</SpecTimestamp>
<NoWarn>$(NoWarn);NETSDK1138</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="2.9.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="NUnit" />
<PackageReference Include="NUnit3TestAdapter" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Markdig\Markdig.csproj" />
<ProjectReference Include="..\SpecFileGen\SpecFileGen.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>
<ItemGroup>
<ItemSpecExecutable Include="$(SpecExecutable)" />
<InputSpecFiles Include="Specs\*.md" />
<InputSpecFiles Include="NormalizeSpecs\*.md" />
<InputSpecFiles Include="PlainTextSpecs\*.md" />
<InputSpecFiles Include="RoundtripSpecs\*.md" />
<InputSpecFiles Remove="Specs\readme.md" />
<!-- Allow Visual Studio up-to-date check to verify that nothing has changed - https://github.com/dotnet/project-system/blob/main/docs/up-to-date-check.md -->
<UpToDateCheckInput Include="@(InputSpecFiles)" />
<OutputSpecFiles Include="@(InputSpecFiles-&gt;'%(RelativeDir)%(Filename).generated.cs')" />
</ItemGroup>
<Target Name="GeneratedSpecsFile" BeforeTargets="BeforeCompile;CoreCompile" Inputs="@(ItemSpecExecutable);@(InputSpecFiles)" Outputs="@(ItemSpecExecutable-&gt;'%(RelativeDir)%(Filename).timestamp');@(InputSpecFiles-&gt;'%(RelativeDir)%(Filename).generated.cs')">
<Message Importance="high" Text="Regenerating Specs Files" />
<Exec Command="dotnet $(SpecExecutable)" />
<WriteLinesToFile File="$(SpecTimestamp)" Lines="$([System.DateTime]::Now)" />
<ItemGroup>
<FileWrites Include="$(SpecTimestamp)" />
<_GeneratedSpecsFile Include="Specs\*.generated.cs" />
<_GeneratedSpecsFile Include="NormalizeSpecs\*.generated.cs" />
<_GeneratedSpecsFile Include="PlainTextSpecs\*.generated.cs" />
<_GeneratedSpecsFile Include="RoundtripSpecs\*.generated.cs" />
<_GeneratedSpecsFile Remove="@(Compile)" />
<Compile Include="@(_GeneratedSpecsFile)" />
</ItemGroup>
</Target>
</Project>

View File

@@ -1,169 +1,192 @@
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Markdig.Extensions.AutoLinks;
using Markdig.Extensions.Tables;
using Markdig.Syntax;
using NUnit.Framework;
namespace Markdig.Tests
namespace Markdig.Tests;
public class MiscTests
{
public class MiscTests
[Test]
public void LinkWithInvalidNonAsciiDomainNameIsIgnored()
{
[Test]
public void LinkWithInvalidNonAsciiDomainNameIsIgnored()
// Url from https://github.com/lunet-io/markdig/issues/438
_ = Markdown.ToHtml("[minulém díle](http://V%20minulém%20díle%20jsme%20nainstalovali%20SQL%20Server,%20který%20je%20nutný%20pro%20běh%20Configuration%20Manageru.%20Dnes%20nás%20čeká%20instalace%20WSUS,%20což%20je%20produkt,%20jež%20je%20možné%20používat%20i%20jako%20samostatnou%20funkci%20ve%20Windows%20Serveru,%20který%20se%20stará%20o%20stažení%20a%20instalaci%20aktualizací%20z%20Microsoft%20Update%20na%20klientské%20počítače.%20Stejně%20jako%20v%20předchozích%20dílech,%20tak%20i%20v%20tomto%20si%20ukážeme%20obě%20varianty%20instalace%20%20a%20to%20jak%20instalaci%20z%20PowerShellu,%20tak%20instalaci%20pomocí%20GUI.) ");
// Valid IDN
TestParser.TestSpec("[foo](http://ünicode.com)", "<p><a href=\"http://xn--nicode-2ya.com\">foo</a></p>");
TestParser.TestSpec("[foo](http://ünicode.ünicode.com)", "<p><a href=\"http://xn--nicode-2ya.xn--nicode-2ya.com\">foo</a></p>");
// Invalid IDN
TestParser.TestSpec("[foo](http://ünicode..com)", "<p><a href=\"http://%C3%BCnicode..com\">foo</a></p>");
}
[TestCase("link [foo [bar]]")] // https://spec.commonmark.org/0.29/#example-508
[TestCase("link [foo][bar]")]
[TestCase("link [][foo][bar][]")]
[TestCase("link [][foo][bar][[]]")]
[TestCase("link [foo] [bar]")]
[TestCase("link [[foo] [] [bar] [[abc]def]]")]
[TestCase("[]")]
[TestCase("[ ]")]
[TestCase("[bar][]")]
[TestCase("[bar][ foo]")]
[TestCase("[bar][foo ][]")]
[TestCase("[bar][fo[ ]o ][][]")]
[TestCase("[a]b[c[d[e]f]g]h")]
[TestCase("a[b[c[d]e]f[g]h]i foo [j]k[l[m]n]o")]
[TestCase("a[b[c[d]e]f[g]h]i[] [][foo][bar][] foo [j]k[l[m]n]o")]
[TestCase("a[b[c[d]e]f[g]h]i foo [j]k[l[m]n]o[][]")]
public void LinkTextMayContainBalancedBrackets(string linkText)
{
string markdown = $"[{linkText}](/uri)";
string expected = $@"<p><a href=""/uri"">{linkText}</a></p>";
TestParser.TestSpec(markdown, expected);
// Make the link text unbalanced
foreach (var bracketIndex in linkText
.Select((c, i) => new Tuple<char, int>(c, i))
.Where(t => t.Item1 == '[' || t.Item1 == ']')
.Select(t => t.Item2))
{
// Url from https://github.com/lunet-io/markdig/issues/438
_ = Markdown.ToHtml("[minulém díle](http://V%20minulém%20díle%20jsme%20nainstalovali%20SQL%20Server,%20který%20je%20nutný%20pro%20běh%20Configuration%20Manageru.%20Dnes%20nás%20čeká%20instalace%20WSUS,%20což%20je%20produkt,%20jež%20je%20možné%20používat%20i%20jako%20samostatnou%20funkci%20ve%20Windows%20Serveru,%20který%20se%20stará%20o%20stažení%20a%20instalaci%20aktualizací%20z%20Microsoft%20Update%20na%20klientské%20počítače.%20Stejně%20jako%20v%20předchozích%20dílech,%20tak%20i%20v%20tomto%20si%20ukážeme%20obě%20varianty%20instalace%20%20a%20to%20jak%20instalaci%20z%20PowerShellu,%20tak%20instalaci%20pomocí%20GUI.) ");
string brokenLinkText = linkText.Remove(bracketIndex, 1);
// Valid IDN
TestParser.TestSpec("[foo](http://ünicode.com)", "<p><a href=\"http://xn--nicode-2ya.com\">foo</a></p>");
TestParser.TestSpec("[foo](http://ünicode.ünicode.com)", "<p><a href=\"http://xn--nicode-2ya.xn--nicode-2ya.com\">foo</a></p>");
markdown = $"[{brokenLinkText}](/uri)";
expected = $@"<p><a href=""/uri"">{brokenLinkText}</a></p>";
// Invalid IDN
TestParser.TestSpec("[foo](http://ünicode..com)", "<p><a href=\"http://%C3%BCnicode..com\">foo</a></p>");
string actual = Markdown.ToHtml(markdown);
Assert.AreNotEqual(expected, actual);
}
}
[TestCase("link [foo [bar]]")] // https://spec.commonmark.org/0.29/#example-508
[TestCase("link [foo][bar]")]
[TestCase("link [][foo][bar][]")]
[TestCase("link [][foo][bar][[]]")]
[TestCase("link [foo] [bar]")]
[TestCase("link [[foo] [] [bar] [[abc]def]]")]
[TestCase("[]")]
[TestCase("[ ]")]
[TestCase("[bar][]")]
[TestCase("[bar][ foo]")]
[TestCase("[bar][foo ][]")]
[TestCase("[bar][fo[ ]o ][][]")]
[TestCase("[a]b[c[d[e]f]g]h")]
[TestCase("a[b[c[d]e]f[g]h]i foo [j]k[l[m]n]o")]
[TestCase("a[b[c[d]e]f[g]h]i[] [][foo][bar][] foo [j]k[l[m]n]o")]
[TestCase("a[b[c[d]e]f[g]h]i foo [j]k[l[m]n]o[][]")]
public void LinkTextMayContainBalancedBrackets(string linkText)
[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)
{
string markdown = $"[{linkText}](/uri)";
string expected = $@"<p><a href=""/uri"">{linkText}</a></p>";
TestParser.TestSpec(markdown, expected);
// Make the link text unbalanced
foreach (var bracketIndex in linkText
.Select((c, i) => new Tuple<char, int>(c, i))
.Where(t => t.Item1 == '[' || t.Item1 == ']')
.Select(t => t.Item2))
{
string brokenLinkText = linkText.Remove(bracketIndex, 1);
markdown = $"[{brokenLinkText}](/uri)";
expected = $@"<p><a href=""/uri"">{brokenLinkText}</a></p>";
string actual = Markdown.ToHtml(markdown);
Assert.AreNotEqual(expected, actual);
}
Exception e = Assert.Throws<ArgumentException>(test);
Assert.True(e.Message.Contains("depth limit"));
}
[Test]
public void IsIssue356Corrected()
else
{
string input = @"https://foo.bar/path/\#m4mv5W0GYKZpGvfA.97";
string expected = @"<p><a href=""https://foo.bar/path/%5C#m4mv5W0GYKZpGvfA.97"">https://foo.bar/path/\#m4mv5W0GYKZpGvfA.97</a></p>";
TestParser.TestSpec($"<{input}>", expected);
TestParser.TestSpec(input, expected, "autolinks|advanced");
test();
}
}
[Test]
public void IsIssue365Corrected()
[Test]
public void IsIssue356Corrected()
{
string input = @"https://foo.bar/path/\#m4mv5W0GYKZpGvfA.97";
string expected = @"<p><a href=""https://foo.bar/path/%5C#m4mv5W0GYKZpGvfA.97"">https://foo.bar/path/\#m4mv5W0GYKZpGvfA.97</a></p>";
TestParser.TestSpec($"<{input}>", expected);
TestParser.TestSpec(input, expected, "autolinks|advanced");
}
[Test]
public void IsIssue365Corrected()
{
// The scheme must be escaped too...
string input = "![image](\"onclick=\"alert&amp;#40;'click'&amp;#41;\"://)";
string expected = "<p><img src=\"%22onclick=%22alert&amp;#40;%27click%27&amp;#41;%22://\" alt=\"image\" /></p>";
TestParser.TestSpec(input, expected);
}
[Test]
public void TestAltTextIsCorrectlyEscaped()
{
TestParser.TestSpec(
@"![This is image alt text with quotation ' and double quotation ""hello"" world](girl.png)",
@"<p><img src=""girl.png"" alt=""This is image alt text with quotation ' and double quotation &quot;hello&quot; world"" /></p>");
}
[Test]
public void TestChangelogPRLinksMatchDescription()
{
string solutionFolder = Path.GetFullPath(Path.Combine(TestParser.TestsDirectory, "../.."));
string changelogPath = Path.Combine(solutionFolder, "changelog.md");
string changelog = File.ReadAllText(changelogPath);
var matches = Regex.Matches(changelog, @"\(\[\(PR #(\d+)\)\]\(.*?pull\/(\d+)\)\)");
Assert.Greater(matches.Count, 0);
foreach (Match match in matches)
{
// The scheme must be escaped too...
string input = "![image](\"onclick=\"alert&amp;#40;'click'&amp;#41;\"://)";
string expected = "<p><img src=\"%22onclick=%22alert&amp;#40;%27click%27&amp;#41;%22://\" alt=\"image\" /></p>";
TestParser.TestSpec(input, expected);
Assert.True(int.TryParse(match.Groups[1].Value, out int textNr));
Assert.True(int.TryParse(match.Groups[2].Value, out int linkNr));
Assert.AreEqual(textNr, linkNr);
}
}
[Test]
public void TestAltTextIsCorrectlyEscaped()
{
TestParser.TestSpec(
@"![This is image alt text with quotation ' and double quotation ""hello"" world](girl.png)",
@"<p><img src=""girl.png"" alt=""This is image alt text with quotation ' and double quotation &quot;hello&quot; world"" /></p>");
}
[Test]
public void TestFixHang()
{
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "hang.md"));
_ = Markdown.ToHtml(input);
}
[Test]
public void TestChangelogPRLinksMatchDescription()
{
string solutionFolder = Path.GetFullPath(Path.Combine(TestParser.TestsDirectory, "../.."));
string changelogPath = Path.Combine(solutionFolder, "changelog.md");
string changelog = File.ReadAllText(changelogPath);
var matches = Regex.Matches(changelog, @"\(\[\(PR #(\d+)\)\]\(.*?pull\/(\d+)\)\)");
Assert.Greater(matches.Count, 0);
foreach (Match match in matches)
{
Assert.True(int.TryParse(match.Groups[1].Value, out int textNr));
Assert.True(int.TryParse(match.Groups[2].Value, out int linkNr));
Assert.AreEqual(textNr, linkNr);
}
}
[Test]
public void TestInvalidHtmlEntity()
{
var input = "9&ddr;&*&ddr;&de<64><65>__";
TestParser.TestSpec(input, "<p>9&amp;ddr;&amp;*&amp;ddr;&amp;de<64><65>__</p>");
}
[Test]
public void TestFixHang()
{
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "hang.md"));
_ = Markdown.ToHtml(input);
}
[Test]
public void TestInvalidCharacterHandling()
{
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "ArgumentOutOfRangeException.md"));
_ = Markdown.ToHtml(input);
}
[Test]
public void TestInvalidHtmlEntity()
{
var input = "9&ddr;&*&ddr;&de<64><65>__";
TestParser.TestSpec(input, "<p>9&amp;ddr;&amp;*&amp;ddr;&amp;de<64><65>__</p>");
}
[Test]
public void TestInvalidCodeEscape()
{
var input = "```**Header** ";
_ = Markdown.ToHtml(input);
}
[Test]
public void TestInvalidCharacterHandling()
{
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "ArgumentOutOfRangeException.md"));
_ = Markdown.ToHtml(input);
}
[Test]
public void TestEmphasisAndHtmlEntity()
{
var markdownText = "*Unlimited-Fun&#174;*&#174;";
TestParser.TestSpec(markdownText, "<p><em>Unlimited-Fun®</em>®</p>");
}
[Test]
public void TestInvalidCodeEscape()
{
var input = "```**Header** ";
_ = Markdown.ToHtml(input);
}
[Test]
public void TestEmphasisAndHtmlEntity()
{
var markdownText = "*Unlimited-Fun&#174;*&#174;";
TestParser.TestSpec(markdownText, "<p><em>Unlimited-Fun®</em>®</p>");
}
[Test]
public void TestThematicInsideCodeBlockInsideList()
{
var input = @"1. In the :
[Test]
public void TestThematicInsideCodeBlockInsideList()
{
var input = @"1. In the :
```
Id DisplayName Description
-- ----------- -----------
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
```";
TestParser.TestSpec(input, @"<ol>
TestParser.TestSpec(input, @"<ol>
<li><p>In the :</p>
<pre><code>Id DisplayName Description
-- ----------- -----------
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
</code></pre></li>
</ol>");
}
}
[Test]
public void VisualizeMathExpressions()
{
string math = @"Math expressions
[Test]
public void VisualizeMathExpressions()
{
string math = @"Math expressions
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
@@ -176,90 +199,214 @@ $$
<div class=""math"">
\begin{align}
\sqrt{37} & = \sqrt{\frac{73^2-1}{12^2}} \\
& = \sqrt{\frac{73^2}{12^2}\cdot\frac{73^2-1}{73^2}} \\
& = \sqrt{\frac{73^2}{12^2}\cdot\frac{73^2-1}{73^2}} \\
& = \sqrt{\frac{73^2}{12^2}}\sqrt{\frac{73^2-1}{73^2}} \\
& = \frac{73}{12}\sqrt{1 - \frac{1}{73^2}} \\
& = \frac{73}{12}\sqrt{1 - \frac{1}{73^2}} \\
& \approx \frac{73}{12}\left(1 - \frac{1}{2\cdot73^2}\right)
\end{align}
</div>
";
Console.WriteLine("Math Expressions:\n");
//Console.WriteLine("Math Expressions:\n");
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
var html = Markdown.ToHtml(math, pl);
//Console.WriteLine(html);
}
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
var html = Markdown.ToHtml(math, pl);
Console.WriteLine(html);
}
[Test]
public void InlineMathExpression()
{
string math = @"Math expressions
[Test]
public void InlineMathExpression()
{
string math = @"Math expressions
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
";
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
var html = Markdown.ToHtml(math, pl);
var html = Markdown.ToHtml(math, pl);
var test1 = html.Contains("<p><span class=\"math\">\\(");
var test2 = html.Contains("\\)</span></p>");
if (!test1 || !test2)
{
Console.WriteLine(html);
Assert.IsTrue(html.Contains("<p><span class=\"math\">\\("), "Leading bracket missing");
Assert.IsTrue(html.Contains("\\)</span></p>"), "Trailing bracket missing");
}
[Test]
public void BlockMathExpression()
{
string math = @"Math expressions
Assert.IsTrue(test1, "Leading bracket missing");
Assert.IsTrue(test2, "Trailing bracket missing");
}
[Test]
public void BlockMathExpression()
{
string math = @"Math expressions
$$
\frac{n!}{k!(n-k)!} = \binom{n}{k}
$$
";
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
var html = Markdown.ToHtml(math, pl);
var html = Markdown.ToHtml(math, pl);
var test1 = html.Contains("<div class=\"math\">\n\\[");
var test2 = html.Contains("\\]</div>");
if (!test1 || !test2)
{
Console.WriteLine(html);
Assert.IsTrue(html.Contains("<div class=\"math\">\n\\["), "Leading bracket missing");
Assert.IsTrue(html.Contains("\\]</div>"), "Trailing bracket missing");
}
[Test]
public void CanDisableParsingHeadings()
{
var noHeadingsPipeline = new MarkdownPipelineBuilder().DisableHeadings().Build();
Assert.IsTrue(test1, "Leading bracket missing");
Assert.IsTrue(test2, "Trailing bracket missing");
}
TestParser.TestSpec("Foo\n===", "<h1>Foo</h1>");
TestParser.TestSpec("Foo\n===", "<p>Foo\n===</p>", noHeadingsPipeline);
[Test]
public void CanDisableParsingHeadings()
{
var noHeadingsPipeline = new MarkdownPipelineBuilder().DisableHeadings().Build();
TestParser.TestSpec("# Heading 1", "<h1>Heading 1</h1>");
TestParser.TestSpec("# Heading 1", "<p># Heading 1</p>", noHeadingsPipeline);
TestParser.TestSpec("Foo\n===", "<h1>Foo</h1>");
TestParser.TestSpec("Foo\n===", "<p>Foo\n===</p>", noHeadingsPipeline);
// Does not also disable link reference definitions
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>");
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>", noHeadingsPipeline);
}
TestParser.TestSpec("# Heading 1", "<h1>Heading 1</h1>");
TestParser.TestSpec("# Heading 1", "<p># Heading 1</p>", noHeadingsPipeline);
[Test]
public void CanOpenAutoLinksInNewWindow()
{
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
var newWindowPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { OpenInNewWindow = true }).Build();
// Does not also disable link reference definitions
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>");
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>", noHeadingsPipeline);
}
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\" target=\"blank\">www.foo.bar</a></p>", newWindowPipeline);
}
[Test]
public void CanOpenAutoLinksInNewWindow()
{
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
var newWindowPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { OpenInNewWindow = true }).Build();
[Test]
public void CanUseHttpsPrefixForWWWAutoLinks()
{
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
var httpsPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { UseHttpsForWWWLinks = true }).Build();
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\" target=\"_blank\">www.foo.bar</a></p>", newWindowPipeline);
}
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
TestParser.TestSpec("www.foo.bar", "<p><a href=\"https://www.foo.bar\">www.foo.bar</a></p>", httpsPipeline);
}
[Test]
public void CanUseHttpsPrefixForWWWAutoLinks()
{
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
var httpsPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { UseHttpsForWWWLinks = true }).Build();
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
TestParser.TestSpec("www.foo.bar", "<p><a href=\"https://www.foo.bar\">www.foo.bar</a></p>", httpsPipeline);
}
[Test]
public void RootInlineHasCorrectSourceSpan()
{
var pipeline = new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build();
pipeline.TrackTrivia = true;
var document = Markdown.Parse("0123456789\n", pipeline);
var expectedSourceSpan = new SourceSpan(0, 10);
Assert.That(((LeafBlock)document.LastChild).Inline.Span == expectedSourceSpan);
}
[Test]
public void RootInlineInTableCellHasCorrectSourceSpan()
{
var pipeline = new MarkdownPipelineBuilder().UsePreciseSourceLocation().UseAdvancedExtensions().Build();
pipeline.TrackTrivia = true;
var document = Markdown.Parse("| a | b |\n| --- | --- |\n| <span id=\"dest\"></span><span id=\"DEST\"></span>*dest*<br/> | \\[in\\] The address of the result of the operation.<br/> |", pipeline);
var paragraph = (ParagraphBlock)((TableCell)((TableRow)((Table)document.LastChild).LastChild).First()).LastChild;
Assert.That(paragraph.Inline.Span.Start == paragraph.Inline.FirstChild.Span.Start);
Assert.That(paragraph.Inline.Span.End == paragraph.Inline.LastChild.Span.End);
}
[Test]
public void TestGridTableShortLine()
{
var input = @"
+--+
| |
+-";
var expected = @"<table>
<col style=""width:100%"" />
<tbody>
<tr>
<td></td>
</tr>
</tbody>
</table>
";
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseGridTables().Build());
}
[Test]
public void TestDefinitionListInListItemWithBlankLine()
{
var input = @"
-
term
: definition
";
var expected = @"<ul>
<li>
<dl>
<dt>term</dt>
<dd>definition</dd>
</dl>
</li>
</ul>
";
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseDefinitionLists().Build());
}
[Test]
public void TestAlertWithinAlertOrNestedBlock()
{
var input = @"
>[!NOTE]
[!NOTE]
The second one is not a note.
>>[!NOTE]
Also not a note.
";
var expected = @"<div class=""markdown-alert markdown-alert-note"">
<p class=""markdown-alert-title""><svg viewBox=""0 0 16 16"" version=""1.1"" width=""16"" height=""16"" aria-hidden=""true""><path d=""M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z""></path></svg>Note</p>
<p>[!NOTE]
The second one is not a note.</p>
</div>
<blockquote>
<blockquote>
<p>[!NOTE]
Also not a note.</p>
</blockquote>
</blockquote>
";
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseAlertBlocks().Build());
}
[Test]
public void TestIssue845ListItemBlankLine()
{
TestParser.TestSpec("-\n\n foo",@"
<ul>
<li></li>
</ul>
<p>foo</p>");
TestParser.TestSpec("-\n-\n\n foo",@"
<ul>
<li></li>
<li></li>
</ul>
<p>foo</p>");
TestParser.TestSpec("-\n\n-\n\n foo",@"
<ul>
<li></li>
<li></li>
</ul>
<p>foo</p>");
}
}

View File

@@ -44,8 +44,7 @@ namespace Markdig.Tests.Specs.Normalize.Headings
//
// ###### Heading 6
Console.WriteLine("Example 1\nSection Headings\n");
TestNormalize.TestSpec("# Heading 1\n\n## Heading 2\n\n### Heading 3\n\n#### Heading 4\n\n##### Heading 5\n\n###### Heading 6", "# Heading 1\n\n## Heading 2\n\n### Heading 3\n\n#### Heading 4\n\n##### Heading 5\n\n###### Heading 6", "");
TestNormalize.TestSpec("# Heading 1\n\n## Heading 2\n\n### Heading 3\n\n#### Heading 4\n\n##### Heading 5\n\n###### Heading 6", "# Heading 1\n\n## Heading 2\n\n### Heading 3\n\n#### Heading 4\n\n##### Heading 5\n\n###### Heading 6", "", context: "Example 1\nSection Headings\n");
}
[Test]
@@ -64,8 +63,7 @@ namespace Markdig.Tests.Specs.Normalize.Headings
//
// Text after two newlines
Console.WriteLine("Example 2\nSection Headings\n");
TestNormalize.TestSpec("###### Heading\n\nText after two newlines", "###### Heading\n\nText after two newlines", "");
TestNormalize.TestSpec("###### Heading\n\nText after two newlines", "###### Heading\n\nText after two newlines", "", context: "Example 2\nSection Headings\n");
}
[Test]
@@ -85,8 +83,7 @@ namespace Markdig.Tests.Specs.Normalize.Headings
//
// Text after two newlines 1
Console.WriteLine("Example 3\nSection Headings\n");
TestNormalize.TestSpec("Heading\n=======\n\nText after two newlines 1", "# Heading\n\nText after two newlines 1", "");
TestNormalize.TestSpec("Heading\n=======\n\nText after two newlines 1", "# Heading\n\nText after two newlines 1", "", context: "Example 3\nSection Headings\n");
}
}
}

View File

@@ -27,8 +27,7 @@ namespace Markdig.Tests.Specs.PlainText.Sample
// Hello, world!
//
Console.WriteLine("Example 1\nSection Sample plain text spec\n");
TestPlainText.TestSpec("*Hello*, [world](http://example.com)!", "Hello, world!\n", "");
TestPlainText.TestSpec("*Hello*, [world](http://example.com)!", "Hello, world!\n", "", context: "Example 1\nSection Sample plain text spec\n");
}
}
}

View File

@@ -2,18 +2,15 @@
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
namespace Markdig.Tests;
namespace Markdig.Tests
class Program
{
class Program
public static void Main(string[] args)
{
public static void Main(string[] args)
{
Console.WriteLine("Run NUnit tests runner with this");
// Uncomment the following line to debug a specific tests more easily
//var tests = new Specs.CommonMarkV_0_29.TestLeafBlocksSetextHeadings();
//tests.LeafBlocksSetextHeadings_Example063();
}
Console.WriteLine("Run NUnit tests runner with this");
// Uncomment the following line to debug a specific tests more easily
//var tests = new Specs.CommonMarkV_0_29.TestLeafBlocksSetextHeadings();
//tests.LeafBlocksSetextHeadings_Example063();
}
}

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,53 @@
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,105 @@
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,18 @@
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,84 @@
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,212 @@
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,21 @@
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,189 @@
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,245 @@
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,284 @@
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,51 @@
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,51 @@
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,182 @@
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")]
[TestCase("- 1.\n- 2.")]
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

@@ -0,0 +1,15 @@
using static Markdig.Tests.TestRoundtrip;
namespace Markdig.Tests.RoundtripSpecs;
[TestFixture]
public class TestYamlFrontMatterBlock
{
[TestCase("---\nkey1: value1\nkey2: value2\n---\n\nContent\n")]
[TestCase("No front matter")]
[TestCase("Looks like front matter but actually is not\n---\nkey1: value1\nkey2: value2\n---")]
public void FrontMatterBlockIsPreserved(string value)
{
RoundTrip(value);
}
}

View File

@@ -34,8 +34,7 @@ namespace Markdig.Tests.Specs.Abbreviations
// Should be rendered as:
// <p>Later in a text we are using <abbr title="Hypertext Markup Language">HTML</abbr> and it becomes an abbr tag <abbr title="Hypertext Markup Language">HTML</abbr></p>
Console.WriteLine("Example 1\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[HTML]: Hypertext Markup Language\n\nLater in a text we are using HTML and it becomes an abbr tag HTML", "<p>Later in a text we are using <abbr title=\"Hypertext Markup Language\">HTML</abbr> and it becomes an abbr tag <abbr title=\"Hypertext Markup Language\">HTML</abbr></p>", "abbreviations|advanced");
TestParser.TestSpec("*[HTML]: Hypertext Markup Language\n\nLater in a text we are using HTML and it becomes an abbr tag HTML", "<p>Later in a text we are using <abbr title=\"Hypertext Markup Language\">HTML</abbr> and it becomes an abbr tag <abbr title=\"Hypertext Markup Language\">HTML</abbr></p>", "abbreviations|advanced", context: "Example 1\nSection Extensions / Abbreviation\n");
}
// An abbreviation definition can be indented at most 3 spaces
@@ -53,8 +52,7 @@ namespace Markdig.Tests.Specs.Abbreviations
// <pre><code>*[This]: is not an abbreviation
// </code></pre>
Console.WriteLine("Example 2\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[HTML]: Hypertext Markup Language\n *[This]: is not an abbreviation", "<pre><code>*[This]: is not an abbreviation\n</code></pre>", "abbreviations|advanced");
TestParser.TestSpec("*[HTML]: Hypertext Markup Language\n *[This]: is not an abbreviation", "<pre><code>*[This]: is not an abbreviation\n</code></pre>", "abbreviations|advanced", context: "Example 2\nSection Extensions / Abbreviation\n");
}
// An abbreviation may contain spaces:
@@ -72,8 +70,7 @@ namespace Markdig.Tests.Specs.Abbreviations
// Should be rendered as:
// <p>This is a <abbr title="Super Hypertext Markup Language">SUPER HTML</abbr> document</p>
Console.WriteLine("Example 3\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[SUPER HTML]: Super Hypertext Markup Language\n\nThis is a SUPER HTML document ", "<p>This is a <abbr title=\"Super Hypertext Markup Language\">SUPER HTML</abbr> document</p>", "abbreviations|advanced");
TestParser.TestSpec("*[SUPER HTML]: Super Hypertext Markup Language\n\nThis is a SUPER HTML document ", "<p>This is a <abbr title=\"Super Hypertext Markup Language\">SUPER HTML</abbr> document</p>", "abbreviations|advanced", context: "Example 3\nSection Extensions / Abbreviation\n");
}
// Abbreviation may contain any unicode characters:
@@ -91,8 +88,7 @@ namespace Markdig.Tests.Specs.Abbreviations
// Should be rendered as:
// <p>This is a <abbr title="Hypertext Markup Language">😃 HTML</abbr> document</p>
Console.WriteLine("Example 4\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[😃 HTML]: Hypertext Markup Language\n\nThis is a 😃 HTML document ", "<p>This is a <abbr title=\"Hypertext Markup Language\">😃 HTML</abbr> document</p>", "abbreviations|advanced");
TestParser.TestSpec("*[😃 HTML]: Hypertext Markup Language\n\nThis is a 😃 HTML document ", "<p>This is a <abbr title=\"Hypertext Markup Language\">😃 HTML</abbr> document</p>", "abbreviations|advanced", context: "Example 4\nSection Extensions / Abbreviation\n");
}
// Abbreviations may be similar:
@@ -112,8 +108,7 @@ namespace Markdig.Tests.Specs.Abbreviations
// Should be rendered as:
// <p>We can abbreviate <abbr title="First">1A</abbr>, <abbr title="Second">1A1</abbr> and <abbr title="Third">1A2</abbr>!</p>
Console.WriteLine("Example 5\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[1A]: First\n*[1A1]: Second\n*[1A2]: Third\n\nWe can abbreviate 1A, 1A1 and 1A2!", "<p>We can abbreviate <abbr title=\"First\">1A</abbr>, <abbr title=\"Second\">1A1</abbr> and <abbr title=\"Third\">1A2</abbr>!</p>", "abbreviations|advanced");
TestParser.TestSpec("*[1A]: First\n*[1A1]: Second\n*[1A2]: Third\n\nWe can abbreviate 1A, 1A1 and 1A2!", "<p>We can abbreviate <abbr title=\"First\">1A</abbr>, <abbr title=\"Second\">1A1</abbr> and <abbr title=\"Third\">1A2</abbr>!</p>", "abbreviations|advanced", context: "Example 5\nSection Extensions / Abbreviation\n");
}
// Abbreviations should match whole word only:
@@ -131,8 +126,7 @@ namespace Markdig.Tests.Specs.Abbreviations
// Should be rendered as:
// <p>We should not abbreviate 1.1A or 11A!</p>
Console.WriteLine("Example 6\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[1A]: First\n\nWe should not abbreviate 1.1A or 11A!", "<p>We should not abbreviate 1.1A or 11A!</p>", "abbreviations|advanced");
TestParser.TestSpec("*[1A]: First\n\nWe should not abbreviate 1.1A or 11A!", "<p>We should not abbreviate 1.1A or 11A!</p>", "abbreviations|advanced", context: "Example 6\nSection Extensions / Abbreviation\n");
}
// Abbreviations should match whole word only, even if the word is the entire content:
@@ -150,8 +144,7 @@ namespace Markdig.Tests.Specs.Abbreviations
// Should be rendered as:
// <p>1.1A</p>
Console.WriteLine("Example 7\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[1A]: First\n\n1.1A", "<p>1.1A</p>", "abbreviations|advanced");
TestParser.TestSpec("*[1A]: First\n\n1.1A", "<p>1.1A</p>", "abbreviations|advanced", context: "Example 7\nSection Extensions / Abbreviation\n");
}
// Abbreviations should match whole word only, even if there is another glossary term:
@@ -170,8 +163,7 @@ namespace Markdig.Tests.Specs.Abbreviations
// Should be rendered as:
// <p><abbr title="Second">SCOM</abbr></p>
Console.WriteLine("Example 8\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[SCO]: First\n*[SCOM]: Second\n\nSCOM", "<p><abbr title=\"Second\">SCOM</abbr></p>", "abbreviations|advanced");
TestParser.TestSpec("*[SCO]: First\n*[SCOM]: Second\n\nSCOM", "<p><abbr title=\"Second\">SCOM</abbr></p>", "abbreviations|advanced", context: "Example 8\nSection Extensions / Abbreviation\n");
}
// Abbreviations should only match when surrounded by whitespace:
@@ -189,8 +181,7 @@ namespace Markdig.Tests.Specs.Abbreviations
// Should be rendered as:
// <p>PRAA</p>
Console.WriteLine("Example 9\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[PR]: Pull Request\n\nPRAA", "<p>PRAA</p>", "abbreviations|advanced");
TestParser.TestSpec("*[PR]: Pull Request\n\nPRAA", "<p>PRAA</p>", "abbreviations|advanced", context: "Example 9\nSection Extensions / Abbreviation\n");
}
// Single character abbreviations should be matched
@@ -208,8 +199,7 @@ namespace Markdig.Tests.Specs.Abbreviations
// Should be rendered as:
// <p><abbr title="Foo">A</abbr></p>
Console.WriteLine("Example 10\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[A]: Foo\n\nA", "<p><abbr title=\"Foo\">A</abbr></p>", "abbreviations|advanced");
TestParser.TestSpec("*[A]: Foo\n\nA", "<p><abbr title=\"Foo\">A</abbr></p>", "abbreviations|advanced", context: "Example 10\nSection Extensions / Abbreviation\n");
}
// The longest matching abbreviation should be used
@@ -228,8 +218,7 @@ namespace Markdig.Tests.Specs.Abbreviations
// Should be rendered as:
// <p><abbr title="foo">Foo</abbr> B</p>
Console.WriteLine("Example 11\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[Foo]: foo\n*[Foo Bar]: foobar\n\nFoo B", "<p><abbr title=\"foo\">Foo</abbr> B</p>", "abbreviations|advanced");
TestParser.TestSpec("*[Foo]: foo\n*[Foo Bar]: foobar\n\nFoo B", "<p><abbr title=\"foo\">Foo</abbr> B</p>", "abbreviations|advanced", context: "Example 11\nSection Extensions / Abbreviation\n");
}
}
}

View File

@@ -0,0 +1,179 @@
// --------------------------------
// Alert Blocks
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.AlertBlocks
{
[TestFixture]
public class TestExtensionsAlertBlocks
{
// # Extensions
//
// This section describes the different extensions supported:
//
// ## Alert Blocks
//
// This is supporting the [GitHub Alert blocks](https://github.com/orgs/community/discussions/16925)
[Test]
public void ExtensionsAlertBlocks_Example001()
{
// Example 1
// Section: Extensions / Alert Blocks
//
// The following Markdown:
// > [!NOTE]
// > Highlights information that users should take into account, even when skimming.
//
// > [!TIP]
// > Optional information to help a user be more successful.
//
// > [!IMPORTANT]
// > Crucial information necessary for users to succeed.
//
// > [!WARNING]
// > Critical content demanding immediate user attention due to potential risks.
//
// > [!CAUTION]
// > Negative potential consequences of an action.
//
// Should be rendered as:
// <div class="markdown-alert markdown-alert-note">
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
// <p>Highlights information that users should take into account, even when skimming.</p>
// </div>
// <div class="markdown-alert markdown-alert-tip">
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"></path></svg>Tip</p>
// <p>Optional information to help a user be more successful.</p>
// </div>
// <div class="markdown-alert markdown-alert-important">
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Important</p>
// <p>Crucial information necessary for users to succeed.</p>
// </div>
// <div class="markdown-alert markdown-alert-warning">
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Warning</p>
// <p>Critical content demanding immediate user attention due to potential risks.</p>
// </div>
// <div class="markdown-alert markdown-alert-caution">
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Caution</p>
// <p>Negative potential consequences of an action.</p>
// </div>
TestParser.TestSpec("> [!NOTE] \n> Highlights information that users should take into account, even when skimming.\n\n> [!TIP]\n> Optional information to help a user be more successful.\n\n> [!IMPORTANT] \n> Crucial information necessary for users to succeed.\n\n> [!WARNING] \n> Critical content demanding immediate user attention due to potential risks.\n\n> [!CAUTION]\n> Negative potential consequences of an action.", "<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Note</p>\n<p>Highlights information that users should take into account, even when skimming.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z\"></path></svg>Tip</p>\n<p>Optional information to help a user be more successful.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"></path></svg>Important</p>\n<p>Crucial information necessary for users to succeed.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"></path></svg>Warning</p>\n<p>Critical content demanding immediate user attention due to potential risks.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-caution\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Caution</p>\n<p>Negative potential consequences of an action.</p>\n</div>", "advanced", context: "Example 1\nSection Extensions / Alert Blocks\n");
}
// Example with code blocks and mix formatting:
[Test]
public void ExtensionsAlertBlocks_Example002()
{
// Example 2
// Section: Extensions / Alert Blocks
//
// The following Markdown:
// > [!NOTE]
// > Highlights information that users should take into account, even when skimming.
// > Testing rendering for multiple lines
// > ```csharp
// > var test = "I can also add code to panels
// > ```
// > `Inline code testing`
//
// Should be rendered as:
// <div class="markdown-alert markdown-alert-note">
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
// <p>Highlights information that users should take into account, even when skimming.
// Testing rendering for multiple lines</p>
// <pre><code class="language-csharp">var test = &quot;I can also add code to panels
// </code></pre>
// <p><code>Inline code testing</code></p>
// </div>
TestParser.TestSpec("> [!NOTE]\n> Highlights information that users should take into account, even when skimming.\n> Testing rendering for multiple lines\n> ```csharp\n> var test = \"I can also add code to panels\n> ```\n> `Inline code testing`", "<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Note</p>\n<p>Highlights information that users should take into account, even when skimming.\nTesting rendering for multiple lines</p>\n<pre><code class=\"language-csharp\">var test = &quot;I can also add code to panels\n</code></pre>\n<p><code>Inline code testing</code></p>\n</div>", "advanced", context: "Example 2\nSection Extensions / Alert Blocks\n");
}
// Multiline:
[Test]
public void ExtensionsAlertBlocks_Example003()
{
// Example 3
// Section: Extensions / Alert Blocks
//
// The following Markdown:
// > [!NOTE]
// > Highlights information that users should take into account, even when skimming.
// >
// > Testing rendering for multiple lines
// >
// > `Inline code testing`
// >
// > Other line
// >
// > > Nested quote
// > >
// > > Final nested quote line
// >
// > Final line of alert
//
// Should be rendered as:
// <div class="markdown-alert markdown-alert-note">
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
// <p>Highlights information that users should take into account, even when skimming.</p>
// <p>Testing rendering for multiple lines</p>
// <p><code>Inline code testing</code></p>
// <p>Other line</p>
// <blockquote>
// <p>Nested quote</p>
// <p>Final nested quote line</p>
// </blockquote>
// <p>Final line of alert</p>
// </div>
TestParser.TestSpec("> [!NOTE]\n> Highlights information that users should take into account, even when skimming.\n> \n> Testing rendering for multiple lines\n> \n> `Inline code testing`\n> \n> Other line\n> \n> > Nested quote\n> >\n> > Final nested quote line\n> \n> Final line of alert", "<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Note</p>\n<p>Highlights information that users should take into account, even when skimming.</p>\n<p>Testing rendering for multiple lines</p>\n<p><code>Inline code testing</code></p>\n<p>Other line</p>\n<blockquote>\n<p>Nested quote</p>\n<p>Final nested quote line</p>\n</blockquote>\n<p>Final line of alert</p>\n</div>", "advanced", context: "Example 3\nSection Extensions / Alert Blocks\n");
}
// An alert inline (e.g `[!NOTE]`) must come first in a quote block, and must be followed by optional spaces with a new line. If no new lines are found, it will not be considered as an alert block.
//
// Followed by space and new line:
[Test]
public void ExtensionsAlertBlocks_Example004()
{
// Example 4
// Section: Extensions / Alert Blocks
//
// The following Markdown:
// > [!NOTE] This is invalid because no new line
// > Highlights information that users should take into account, even when skimming.
//
// Should be rendered as:
// <blockquote>
// <p>[!NOTE] This is invalid because no new line
// Highlights information that users should take into account, even when skimming.</p>
// </blockquote>
TestParser.TestSpec("> [!NOTE] This is invalid because no new line\n> Highlights information that users should take into account, even when skimming.", "<blockquote>\n<p>[!NOTE] This is invalid because no new line\nHighlights information that users should take into account, even when skimming.</p>\n</blockquote>", "advanced", context: "Example 4\nSection Extensions / Alert Blocks\n");
}
// Must come first in a quote block:
[Test]
public void ExtensionsAlertBlocks_Example005()
{
// Example 5
// Section: Extensions / Alert Blocks
//
// The following Markdown:
// > This is not a [!NOTE]
// > Highlights information that users should take into account, even when skimming.
//
// Should be rendered as:
// <blockquote>
// <p>This is not a [!NOTE]
// Highlights information that users should take into account, even when skimming.</p>
// </blockquote>
TestParser.TestSpec("> This is not a [!NOTE]\n> Highlights information that users should take into account, even when skimming.", "<blockquote>\n<p>This is not a [!NOTE]\nHighlights information that users should take into account, even when skimming.</p>\n</blockquote>", "advanced", context: "Example 5\nSection Extensions / Alert Blocks\n");
}
}
}

View File

@@ -0,0 +1,127 @@
# Extensions
This section describes the different extensions supported:
## Alert Blocks
This is supporting the [GitHub Alert blocks](https://github.com/orgs/community/discussions/16925)
```````````````````````````````` example
> [!NOTE]
> Highlights information that users should take into account, even when skimming.
> [!TIP]
> Optional information to help a user be more successful.
> [!IMPORTANT]
> Crucial information necessary for users to succeed.
> [!WARNING]
> Critical content demanding immediate user attention due to potential risks.
> [!CAUTION]
> Negative potential consequences of an action.
.
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p>Highlights information that users should take into account, even when skimming.</p>
</div>
<div class="markdown-alert markdown-alert-tip">
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"></path></svg>Tip</p>
<p>Optional information to help a user be more successful.</p>
</div>
<div class="markdown-alert markdown-alert-important">
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Important</p>
<p>Crucial information necessary for users to succeed.</p>
</div>
<div class="markdown-alert markdown-alert-warning">
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Warning</p>
<p>Critical content demanding immediate user attention due to potential risks.</p>
</div>
<div class="markdown-alert markdown-alert-caution">
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Caution</p>
<p>Negative potential consequences of an action.</p>
</div>
````````````````````````````````
Example with code blocks and mix formatting:
```````````````````````````````` example
> [!NOTE]
> Highlights information that users should take into account, even when skimming.
> Testing rendering for multiple lines
> ```csharp
> var test = "I can also add code to panels
> ```
> `Inline code testing`
.
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p>Highlights information that users should take into account, even when skimming.
Testing rendering for multiple lines</p>
<pre><code class="language-csharp">var test = &quot;I can also add code to panels
</code></pre>
<p><code>Inline code testing</code></p>
</div>
````````````````````````````````
Multiline:
```````````````````````````````` example
> [!NOTE]
> Highlights information that users should take into account, even when skimming.
>
> Testing rendering for multiple lines
>
> `Inline code testing`
>
> Other line
>
> > Nested quote
> >
> > Final nested quote line
>
> Final line of alert
.
<div class="markdown-alert markdown-alert-note">
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p>Highlights information that users should take into account, even when skimming.</p>
<p>Testing rendering for multiple lines</p>
<p><code>Inline code testing</code></p>
<p>Other line</p>
<blockquote>
<p>Nested quote</p>
<p>Final nested quote line</p>
</blockquote>
<p>Final line of alert</p>
</div>
````````````````````````````````
An alert inline (e.g `[!NOTE]`) must come first in a quote block, and must be followed by optional spaces with a new line. If no new lines are found, it will not be considered as an alert block.
Followed by space and new line:
```````````````````````````````` example
> [!NOTE] This is invalid because no new line
> Highlights information that users should take into account, even when skimming.
.
<blockquote>
<p>[!NOTE] This is invalid because no new line
Highlights information that users should take into account, even when skimming.</p>
</blockquote>
````````````````````````````````
Must come first in a quote block:
```````````````````````````````` example
> This is not a [!NOTE]
> Highlights information that users should take into account, even when skimming.
.
<blockquote>
<p>This is not a [!NOTE]
Highlights information that users should take into account, even when skimming.</p>
</blockquote>
````````````````````````````````

View File

@@ -30,8 +30,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
// Should be rendered as:
// <h1 id="this-is-a-heading">This is a heading</h1>
Console.WriteLine("Example 1\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# This is a heading", "<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced");
TestParser.TestSpec("# This is a heading", "<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced", context: "Example 1\nSection Extensions / Heading Auto Identifiers\n");
}
// Only punctuation `-`, `_` and `.` is kept, all other non letter characters are discarded.
@@ -49,8 +48,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
// Should be rendered as:
// <h1 id="this-is-a-heading_with.and">This - is a &amp;@! heading _ with . and ! -</h1>
Console.WriteLine("Example 2\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# This - is a &@! heading _ with . and ! -", "<h1 id=\"this-is-a-heading_with.and\">This - is a &amp;@! heading _ with . and ! -</h1>", "autoidentifiers|advanced");
TestParser.TestSpec("# This - is a &@! heading _ with . and ! -", "<h1 id=\"this-is-a-heading_with.and\">This - is a &amp;@! heading _ with . and ! -</h1>", "autoidentifiers|advanced", context: "Example 2\nSection Extensions / Heading Auto Identifiers\n");
}
// Formatting (emphasis) are also discarded:
@@ -66,8 +64,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
// Should be rendered as:
// <h1 id="this-is-a-heading">This is a <em>heading</em></h1>
Console.WriteLine("Example 3\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# This is a *heading*", "<h1 id=\"this-is-a-heading\">This is a <em>heading</em></h1>", "autoidentifiers|advanced");
TestParser.TestSpec("# This is a *heading*", "<h1 id=\"this-is-a-heading\">This is a <em>heading</em></h1>", "autoidentifiers|advanced", context: "Example 3\nSection Extensions / Heading Auto Identifiers\n");
}
// Links are also removed:
@@ -83,8 +80,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
// Should be rendered as:
// <h1 id="this-is-a-heading">This is a <a href="/url">heading</a></h1>
Console.WriteLine("Example 4\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# This is a [heading](/url)", "<h1 id=\"this-is-a-heading\">This is a <a href=\"/url\">heading</a></h1>", "autoidentifiers|advanced");
TestParser.TestSpec("# This is a [heading](/url)", "<h1 id=\"this-is-a-heading\">This is a <a href=\"/url\">heading</a></h1>", "autoidentifiers|advanced", context: "Example 4\nSection Extensions / Heading Auto Identifiers\n");
}
// If multiple heading have the same text, -1, -2...-n will be postfix to the header id.
@@ -102,8 +98,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
// <h1 id="this-is-a-heading">This is a heading</h1>
// <h1 id="this-is-a-heading-1">This is a heading</h1>
Console.WriteLine("Example 5\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# This is a heading\n# This is a heading", "<h1 id=\"this-is-a-heading\">This is a heading</h1>\n<h1 id=\"this-is-a-heading-1\">This is a heading</h1>", "autoidentifiers|advanced");
TestParser.TestSpec("# This is a heading\n# This is a heading", "<h1 id=\"this-is-a-heading\">This is a heading</h1>\n<h1 id=\"this-is-a-heading-1\">This is a heading</h1>", "autoidentifiers|advanced", context: "Example 5\nSection Extensions / Heading Auto Identifiers\n");
}
// The heading Id will start on the first letter character of the heading, all previous characters will be discarded:
@@ -119,8 +114,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
// Should be rendered as:
// <h1 id="this-is-a-heading">1.0 This is a heading</h1>
Console.WriteLine("Example 6\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# 1.0 This is a heading", "<h1 id=\"this-is-a-heading\">1.0 This is a heading</h1>", "autoidentifiers|advanced");
TestParser.TestSpec("# 1.0 This is a heading", "<h1 id=\"this-is-a-heading\">1.0 This is a heading</h1>", "autoidentifiers|advanced", context: "Example 6\nSection Extensions / Heading Auto Identifiers\n");
}
// If the heading is all stripped by the previous rules, the id `section` will be used instead:
@@ -138,8 +132,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
// <h1 id="section">1.0 &amp; ^ % *</h1>
// <h1 id="section-1">1.0 &amp; ^ % *</h1>
Console.WriteLine("Example 7\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# 1.0 & ^ % *\n# 1.0 & ^ % *", "<h1 id=\"section\">1.0 &amp; ^ % *</h1>\n<h1 id=\"section-1\">1.0 &amp; ^ % *</h1>", "autoidentifiers|advanced");
TestParser.TestSpec("# 1.0 & ^ % *\n# 1.0 & ^ % *", "<h1 id=\"section\">1.0 &amp; ^ % *</h1>\n<h1 id=\"section-1\">1.0 &amp; ^ % *</h1>", "autoidentifiers|advanced", context: "Example 7\nSection Extensions / Heading Auto Identifiers\n");
}
// When the options "AutoLink" is setup, it is possible to link to an existing heading by using the
@@ -158,8 +151,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
// <h1 id="this-is-a-heading">This is a heading</h1>
// <p><a href="#this-is-a-heading">This is a heading</a></p>
Console.WriteLine("Example 8\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# This is a heading\n[This is a heading]", "<h1 id=\"this-is-a-heading\">This is a heading</h1>\n<p><a href=\"#this-is-a-heading\">This is a heading</a></p>", "autoidentifiers|advanced");
TestParser.TestSpec("# This is a heading\n[This is a heading]", "<h1 id=\"this-is-a-heading\">This is a heading</h1>\n<p><a href=\"#this-is-a-heading\">This is a heading</a></p>", "autoidentifiers|advanced", context: "Example 8\nSection Extensions / Heading Auto Identifiers\n");
}
// Links before the heading are also working:
@@ -177,8 +169,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
// <p><a href="#this-is-a-heading">This is a heading</a></p>
// <h1 id="this-is-a-heading">This is a heading</h1>
Console.WriteLine("Example 9\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("[This is a heading]\n# This is a heading", "<p><a href=\"#this-is-a-heading\">This is a heading</a></p>\n<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced");
TestParser.TestSpec("[This is a heading]\n# This is a heading", "<p><a href=\"#this-is-a-heading\">This is a heading</a></p>\n<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced", context: "Example 9\nSection Extensions / Heading Auto Identifiers\n");
}
// The text of the link can be changed:
@@ -196,8 +187,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
// <p><a href="#this-is-a-heading">With a new text</a></p>
// <h1 id="this-is-a-heading">This is a heading</h1>
Console.WriteLine("Example 10\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("[With a new text][This is a heading]\n# This is a heading", "<p><a href=\"#this-is-a-heading\">With a new text</a></p>\n<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced");
TestParser.TestSpec("[With a new text][This is a heading]\n# This is a heading", "<p><a href=\"#this-is-a-heading\">With a new text</a></p>\n<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced", context: "Example 10\nSection Extensions / Heading Auto Identifiers\n");
}
// An autoidentifier should not conflict with an existing link:
@@ -216,8 +206,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
// <p><img src="./scenario.png" alt="scenario image" /></p>
// <h2 id="scenario">Scenario</h2>
Console.WriteLine("Example 11\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("![scenario image][scenario]\n## Scenario\n[scenario]: ./scenario.png", "<p><img src=\"./scenario.png\" alt=\"scenario image\" /></p>\n<h2 id=\"scenario\">Scenario</h2>", "autoidentifiers|advanced");
TestParser.TestSpec("![scenario image][scenario]\n## Scenario\n[scenario]: ./scenario.png", "<p><img src=\"./scenario.png\" alt=\"scenario image\" /></p>\n<h2 id=\"scenario\">Scenario</h2>", "autoidentifiers|advanced", context: "Example 11\nSection Extensions / Heading Auto Identifiers\n");
}
}
}

View File

@@ -44,8 +44,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// And a <a href="tel:+1555123456">+1555123456</a>
// And a plain <a href="http://www.google.com">www.google.com</a></p>
Console.WriteLine("Example 1\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("This is a http://www.google.com URL and https://www.google.com\nThis is a ftp://test.com\nAnd a mailto:email@toto.com\nAnd a tel:+1555123456\nAnd a plain www.google.com", "<p>This is a <a href=\"http://www.google.com\">http://www.google.com</a> URL and <a href=\"https://www.google.com\">https://www.google.com</a>\nThis is a <a href=\"ftp://test.com\">ftp://test.com</a>\nAnd a <a href=\"mailto:email@toto.com\">email@toto.com</a>\nAnd a <a href=\"tel:+1555123456\">+1555123456</a>\nAnd a plain <a href=\"http://www.google.com\">www.google.com</a></p>", "autolinks|advanced");
TestParser.TestSpec("This is a http://www.google.com URL and https://www.google.com\nThis is a ftp://test.com\nAnd a mailto:email@toto.com\nAnd a tel:+1555123456\nAnd a plain www.google.com", "<p>This is a <a href=\"http://www.google.com\">http://www.google.com</a> URL and <a href=\"https://www.google.com\">https://www.google.com</a>\nThis is a <a href=\"ftp://test.com\">ftp://test.com</a>\nAnd a <a href=\"mailto:email@toto.com\">email@toto.com</a>\nAnd a <a href=\"tel:+1555123456\">+1555123456</a>\nAnd a plain <a href=\"http://www.google.com\">www.google.com</a></p>", "autolinks|advanced", context: "Example 1\nSection Extensions / AutoLinks\n");
}
// But incomplete links will not be matched:
@@ -69,8 +68,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// And not a plain www. or a www.x
// And not a tel:</p>
Console.WriteLine("Example 2\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("This is not a http:/www.google.com URL and https:/www.google.com\nThis is not a ftp:/test.com\nAnd not a mailto:emailtoto.com\nAnd not a plain www. or a www.x \nAnd not a tel:", "<p>This is not a http:/www.google.com URL and https:/www.google.com\nThis is not a ftp:/test.com\nAnd not a mailto:emailtoto.com\nAnd not a plain www. or a www.x\nAnd not a tel:</p>", "autolinks|advanced");
TestParser.TestSpec("This is not a http:/www.google.com URL and https:/www.google.com\nThis is not a ftp:/test.com\nAnd not a mailto:emailtoto.com\nAnd not a plain www. or a www.x \nAnd not a tel:", "<p>This is not a http:/www.google.com URL and https:/www.google.com\nThis is not a ftp:/test.com\nAnd not a mailto:emailtoto.com\nAnd not a plain www. or a www.x\nAnd not a tel:</p>", "autolinks|advanced", context: "Example 2\nSection Extensions / AutoLinks\n");
}
// Previous character must be a punctuation or a valid space (tab, space, new line):
@@ -86,8 +84,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p>This is not a nhttp://www.google.com URL but this is (<a href="https://www.google.com">https://www.google.com</a>)</p>
Console.WriteLine("Example 3\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("This is not a nhttp://www.google.com URL but this is (https://www.google.com)", "<p>This is not a nhttp://www.google.com URL but this is (<a href=\"https://www.google.com\">https://www.google.com</a>)</p>", "autolinks|advanced");
TestParser.TestSpec("This is not a nhttp://www.google.com URL but this is (https://www.google.com)", "<p>This is not a nhttp://www.google.com URL but this is (<a href=\"https://www.google.com\">https://www.google.com</a>)</p>", "autolinks|advanced", context: "Example 3\nSection Extensions / AutoLinks\n");
}
// An autolink should not interfere with an `<a>` HTML inline:
@@ -103,8 +100,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p>This is an HTML <a href="http://www.google.com">http://www.google.com</a> link</p>
Console.WriteLine("Example 4\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link", "<p>This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link</p>", "autolinks|advanced");
TestParser.TestSpec("This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link", "<p>This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link</p>", "autolinks|advanced", context: "Example 4\nSection Extensions / AutoLinks\n");
}
// or even within emphasis:
@@ -120,8 +116,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p>This is an HTML <a href="http://www.google.com"> <strong>http://www.google.com</strong> </a> link</p>
Console.WriteLine("Example 5\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("This is an HTML <a href=\"http://www.google.com\"> **http://www.google.com** </a> link", "<p>This is an HTML <a href=\"http://www.google.com\"> <strong>http://www.google.com</strong> </a> link</p>", "autolinks|advanced");
TestParser.TestSpec("This is an HTML <a href=\"http://www.google.com\"> **http://www.google.com** </a> link", "<p>This is an HTML <a href=\"http://www.google.com\"> <strong>http://www.google.com</strong> </a> link</p>", "autolinks|advanced", context: "Example 5\nSection Extensions / AutoLinks\n");
}
// An autolink should not interfere with a markdown link:
@@ -137,8 +132,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p>This is an HTML <a href="http://www.google.com">http://www.google.com</a> link</p>
Console.WriteLine("Example 6\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("This is an HTML [http://www.google.com](http://www.google.com) link", "<p>This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link</p>", "autolinks|advanced");
TestParser.TestSpec("This is an HTML [http://www.google.com](http://www.google.com) link", "<p>This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link</p>", "autolinks|advanced", context: "Example 6\nSection Extensions / AutoLinks\n");
}
// A link embraced by pending emphasis should let the emphasis takes precedence if characters are placed at the end of the matched link:
@@ -154,8 +148,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p>Check <strong><a href="http://www.a.com">http://www.a.com</a></strong> or <strong><a href="http://www.b.com">http://www.b.com</a></strong></p>
Console.WriteLine("Example 7\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("Check **http://www.a.com** or __http://www.b.com__", "<p>Check <strong><a href=\"http://www.a.com\">http://www.a.com</a></strong> or <strong><a href=\"http://www.b.com\">http://www.b.com</a></strong></p>", "autolinks|advanced");
TestParser.TestSpec("Check **http://www.a.com** or __http://www.b.com__", "<p>Check <strong><a href=\"http://www.a.com\">http://www.a.com</a></strong> or <strong><a href=\"http://www.b.com\">http://www.b.com</a></strong></p>", "autolinks|advanced", context: "Example 7\nSection Extensions / AutoLinks\n");
}
// It is not mentioned by the spec, but empty emails won't be matched (only a subset of [RFC2368](https://tools.ietf.org/html/rfc2368) is supported by auto links):
@@ -171,8 +164,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p><a href="mailto:email@test.com">email@test.com</a> is okay, but mailto:@test.com is not</p>
Console.WriteLine("Example 8\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("mailto:email@test.com is okay, but mailto:@test.com is not", "<p><a href=\"mailto:email@test.com\">email@test.com</a> is okay, but mailto:@test.com is not</p>", "autolinks|advanced");
TestParser.TestSpec("mailto:email@test.com is okay, but mailto:@test.com is not", "<p><a href=\"mailto:email@test.com\">email@test.com</a> is okay, but mailto:@test.com is not</p>", "autolinks|advanced", context: "Example 8\nSection Extensions / AutoLinks\n");
}
}
@@ -194,8 +186,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p><a href="http://www.commonmark.org">www.commonmark.org</a></p>
Console.WriteLine("Example 9\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("www.commonmark.org", "<p><a href=\"http://www.commonmark.org\">www.commonmark.org</a></p>", "autolinks|advanced");
TestParser.TestSpec("www.commonmark.org", "<p><a href=\"http://www.commonmark.org\">www.commonmark.org</a></p>", "autolinks|advanced", context: "Example 9\nSection Extensions / AutoLinks / GFM Support\n");
}
[Test]
@@ -210,8 +201,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p>Visit <a href="http://www.commonmark.org/help">www.commonmark.org/help</a> for more information.</p>
Console.WriteLine("Example 10\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("Visit www.commonmark.org/help for more information.", "<p>Visit <a href=\"http://www.commonmark.org/help\">www.commonmark.org/help</a> for more information.</p>", "autolinks|advanced");
TestParser.TestSpec("Visit www.commonmark.org/help for more information.", "<p>Visit <a href=\"http://www.commonmark.org/help\">www.commonmark.org/help</a> for more information.</p>", "autolinks|advanced", context: "Example 10\nSection Extensions / AutoLinks / GFM Support\n");
}
[Test]
@@ -229,8 +219,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// <p>Visit <a href="http://www.commonmark.org">www.commonmark.org</a>.</p>
// <p>Visit <a href="http://www.commonmark.org/a.b">www.commonmark.org/a.b</a>.</p>
Console.WriteLine("Example 11\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("Visit www.commonmark.org.\n\nVisit www.commonmark.org/a.b.", "<p>Visit <a href=\"http://www.commonmark.org\">www.commonmark.org</a>.</p>\n<p>Visit <a href=\"http://www.commonmark.org/a.b\">www.commonmark.org/a.b</a>.</p>", "autolinks|advanced");
TestParser.TestSpec("Visit www.commonmark.org.\n\nVisit www.commonmark.org/a.b.", "<p>Visit <a href=\"http://www.commonmark.org\">www.commonmark.org</a>.</p>\n<p>Visit <a href=\"http://www.commonmark.org/a.b\">www.commonmark.org/a.b</a>.</p>", "autolinks|advanced", context: "Example 11\nSection Extensions / AutoLinks / GFM Support\n");
}
[Test]
@@ -248,8 +237,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// <p><a href="http://www.google.com/search?q=Markup+(business)">www.google.com/search?q=Markup+(business)</a></p>
// <p>(<a href="http://www.google.com/search?q=Markup+(business)">www.google.com/search?q=Markup+(business)</a>)</p>
Console.WriteLine("Example 12\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("www.google.com/search?q=Markup+(business)\n\n(www.google.com/search?q=Markup+(business))", "<p><a href=\"http://www.google.com/search?q=Markup+(business)\">www.google.com/search?q=Markup+(business)</a></p>\n<p>(<a href=\"http://www.google.com/search?q=Markup+(business)\">www.google.com/search?q=Markup+(business)</a>)</p>", "autolinks|advanced");
TestParser.TestSpec("www.google.com/search?q=Markup+(business)\n\n(www.google.com/search?q=Markup+(business))", "<p><a href=\"http://www.google.com/search?q=Markup+(business)\">www.google.com/search?q=Markup+(business)</a></p>\n<p>(<a href=\"http://www.google.com/search?q=Markup+(business)\">www.google.com/search?q=Markup+(business)</a>)</p>", "autolinks|advanced", context: "Example 12\nSection Extensions / AutoLinks / GFM Support\n");
}
[Test]
@@ -267,8 +255,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// <p><a href="http://www.google.com/search?q=commonmark&amp;hl=en">www.google.com/search?q=commonmark&amp;hl=en</a></p>
// <p><a href="http://www.google.com/search?q=commonmark">www.google.com/search?q=commonmark</a>&amp;hl;</p>
Console.WriteLine("Example 13\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("www.google.com/search?q=commonmark&hl=en\n\nwww.google.com/search?q=commonmark&hl;", "<p><a href=\"http://www.google.com/search?q=commonmark&amp;hl=en\">www.google.com/search?q=commonmark&amp;hl=en</a></p>\n<p><a href=\"http://www.google.com/search?q=commonmark\">www.google.com/search?q=commonmark</a>&amp;hl;</p>", "autolinks|advanced");
TestParser.TestSpec("www.google.com/search?q=commonmark&hl=en\n\nwww.google.com/search?q=commonmark&hl;", "<p><a href=\"http://www.google.com/search?q=commonmark&amp;hl=en\">www.google.com/search?q=commonmark&amp;hl=en</a></p>\n<p><a href=\"http://www.google.com/search?q=commonmark\">www.google.com/search?q=commonmark</a>&amp;hl;</p>", "autolinks|advanced", context: "Example 13\nSection Extensions / AutoLinks / GFM Support\n");
}
[Test]
@@ -283,8 +270,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p><a href="http://www.commonmark.org/he">www.commonmark.org/he</a>&lt;lp</p>
Console.WriteLine("Example 14\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("www.commonmark.org/he<lp", "<p><a href=\"http://www.commonmark.org/he\">www.commonmark.org/he</a>&lt;lp</p>", "autolinks|advanced");
TestParser.TestSpec("www.commonmark.org/he<lp", "<p><a href=\"http://www.commonmark.org/he\">www.commonmark.org/he</a>&lt;lp</p>", "autolinks|advanced", context: "Example 14\nSection Extensions / AutoLinks / GFM Support\n");
}
[Test]
@@ -305,8 +291,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// <p>(Visit <a href="https://encrypted.google.com/search?q=Markup+(business)">https://encrypted.google.com/search?q=Markup+(business)</a>)</p>
// <p>Anonymous FTP is available at <a href="ftp://foo.bar.baz">ftp://foo.bar.baz</a>.</p>
Console.WriteLine("Example 15\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("http://commonmark.org\n\n(Visit https://encrypted.google.com/search?q=Markup+(business))\n\nAnonymous FTP is available at ftp://foo.bar.baz.", "<p><a href=\"http://commonmark.org\">http://commonmark.org</a></p>\n<p>(Visit <a href=\"https://encrypted.google.com/search?q=Markup+(business)\">https://encrypted.google.com/search?q=Markup+(business)</a>)</p>\n<p>Anonymous FTP is available at <a href=\"ftp://foo.bar.baz\">ftp://foo.bar.baz</a>.</p>", "autolinks|advanced");
TestParser.TestSpec("http://commonmark.org\n\n(Visit https://encrypted.google.com/search?q=Markup+(business))\n\nAnonymous FTP is available at ftp://foo.bar.baz.", "<p><a href=\"http://commonmark.org\">http://commonmark.org</a></p>\n<p>(Visit <a href=\"https://encrypted.google.com/search?q=Markup+(business)\">https://encrypted.google.com/search?q=Markup+(business)</a>)</p>\n<p>Anonymous FTP is available at <a href=\"ftp://foo.bar.baz\">ftp://foo.bar.baz</a>.</p>", "autolinks|advanced", context: "Example 15\nSection Extensions / AutoLinks / GFM Support\n");
}
}
@@ -346,8 +331,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// mailto:email@test.
// mailto:email@.test</p>
Console.WriteLine("Example 16\nSection Extensions / AutoLinks / Valid Domain Tests\n");
TestParser.TestSpec("www..\nwww..com\nhttp://test.\nhttp://.test\nhttp://.\nhttp://..\nftp://test.\nftp://.test\nmailto:email@test.\nmailto:email@.test", "<p>www..\nwww..com\nhttp://test.\nhttp://.test\nhttp://.\nhttp://..\nftp://test.\nftp://.test\nmailto:email@test.\nmailto:email@.test</p>", "autolinks|advanced");
TestParser.TestSpec("www..\nwww..com\nhttp://test.\nhttp://.test\nhttp://.\nhttp://..\nftp://test.\nftp://.test\nmailto:email@test.\nmailto:email@.test", "<p>www..\nwww..com\nhttp://test.\nhttp://.test\nhttp://.\nhttp://..\nftp://test.\nftp://.test\nmailto:email@test.\nmailto:email@.test</p>", "autolinks|advanced", context: "Example 16\nSection Extensions / AutoLinks / Valid Domain Tests\n");
}
// Domain names with too few segments won't be matched
@@ -371,8 +355,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// ftp://test
// mailto:email@test</p>
Console.WriteLine("Example 17\nSection Extensions / AutoLinks / Valid Domain Tests\n");
TestParser.TestSpec("www\nwww.com\nhttp://test\nftp://test\nmailto:email@test", "<p>www\nwww.com\nhttp://test\nftp://test\nmailto:email@test</p>", "autolinks|advanced");
TestParser.TestSpec("www\nwww.com\nhttp://test\nftp://test\nmailto:email@test", "<p>www\nwww.com\nhttp://test\nftp://test\nmailto:email@test</p>", "autolinks|advanced", context: "Example 17\nSection Extensions / AutoLinks / Valid Domain Tests\n");
}
// Domain names that contain an underscores in the last two segments won't be matched
@@ -400,8 +383,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// <p><a href="ftp://test_.foo.bar">ftp://test_.foo.bar</a> is okay, but ftp://test.fo_o is not</p>
// <p><a href="mailto:email@_test.foo.bar">email@_test.foo.bar</a> is okay, but mailto:email@_test.foo is not</p>
Console.WriteLine("Example 18\nSection Extensions / AutoLinks / Valid Domain Tests\n");
TestParser.TestSpec("www._test.foo.bar is okay, but www._test.foo is not\n\nhttp://te_st.foo.bar is okay, as is http://test.foo_.bar.foo\n\nBut http://te_st.foo, http://test.foo_.bar and http://test._foo are not\n\nftp://test_.foo.bar is okay, but ftp://test.fo_o is not\n\nmailto:email@_test.foo.bar is okay, but mailto:email@_test.foo is not", "<p><a href=\"http://www._test.foo.bar\">www._test.foo.bar</a> is okay, but www._test.foo is not</p>\n<p><a href=\"http://te_st.foo.bar\">http://te_st.foo.bar</a> is okay, as is <a href=\"http://test.foo_.bar.foo\">http://test.foo_.bar.foo</a></p>\n<p>But http://te_st.foo, http://test.foo_.bar and http://test._foo are not</p>\n<p><a href=\"ftp://test_.foo.bar\">ftp://test_.foo.bar</a> is okay, but ftp://test.fo_o is not</p>\n<p><a href=\"mailto:email@_test.foo.bar\">email@_test.foo.bar</a> is okay, but mailto:email@_test.foo is not</p>", "autolinks|advanced");
TestParser.TestSpec("www._test.foo.bar is okay, but www._test.foo is not\n\nhttp://te_st.foo.bar is okay, as is http://test.foo_.bar.foo\n\nBut http://te_st.foo, http://test.foo_.bar and http://test._foo are not\n\nftp://test_.foo.bar is okay, but ftp://test.fo_o is not\n\nmailto:email@_test.foo.bar is okay, but mailto:email@_test.foo is not", "<p><a href=\"http://www._test.foo.bar\">www._test.foo.bar</a> is okay, but www._test.foo is not</p>\n<p><a href=\"http://te_st.foo.bar\">http://te_st.foo.bar</a> is okay, as is <a href=\"http://test.foo_.bar.foo\">http://test.foo_.bar.foo</a></p>\n<p>But http://te_st.foo, http://test.foo_.bar and http://test._foo are not</p>\n<p><a href=\"ftp://test_.foo.bar\">ftp://test_.foo.bar</a> is okay, but ftp://test.fo_o is not</p>\n<p><a href=\"mailto:email@_test.foo.bar\">email@_test.foo.bar</a> is okay, but mailto:email@_test.foo is not</p>", "autolinks|advanced", context: "Example 18\nSection Extensions / AutoLinks / Valid Domain Tests\n");
}
// Domain names that contain invalid characters (not AlphaNumberic, -, _ or .) won't be matched
@@ -417,8 +399,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p>https://[your-domain]/api</p>
Console.WriteLine("Example 19\nSection Extensions / AutoLinks / Valid Domain Tests\n");
TestParser.TestSpec("https://[your-domain]/api", "<p>https://[your-domain]/api</p>", "autolinks|advanced");
TestParser.TestSpec("https://[your-domain]/api", "<p>https://[your-domain]/api</p>", "autolinks|advanced", context: "Example 19\nSection Extensions / AutoLinks / Valid Domain Tests\n");
}
// Domain names followed by ?, : or # instead of / are matched
@@ -446,8 +427,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// <p><a href="https://github.com">https://github.com</a>:</p>
// <p><a href="https://github.com:443">https://github.com:443</a></p>
Console.WriteLine("Example 20\nSection Extensions / AutoLinks / Valid Domain Tests\n");
TestParser.TestSpec("https://github.com?\n\nhttps://github.com?a\n\nhttps://github.com#a\n\nhttps://github.com:\n\nhttps://github.com:443", "<p><a href=\"https://github.com\">https://github.com</a>?</p>\n<p><a href=\"https://github.com?a\">https://github.com?a</a></p>\n<p><a href=\"https://github.com#a\">https://github.com#a</a></p>\n<p><a href=\"https://github.com\">https://github.com</a>:</p>\n<p><a href=\"https://github.com:443\">https://github.com:443</a></p>", "autolinks|advanced");
TestParser.TestSpec("https://github.com?\n\nhttps://github.com?a\n\nhttps://github.com#a\n\nhttps://github.com:\n\nhttps://github.com:443", "<p><a href=\"https://github.com\">https://github.com</a>?</p>\n<p><a href=\"https://github.com?a\">https://github.com?a</a></p>\n<p><a href=\"https://github.com#a\">https://github.com#a</a></p>\n<p><a href=\"https://github.com\">https://github.com</a>:</p>\n<p><a href=\"https://github.com:443\">https://github.com:443</a></p>", "autolinks|advanced", context: "Example 20\nSection Extensions / AutoLinks / Valid Domain Tests\n");
}
}
@@ -478,8 +458,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// <p><a href="http://abc.net#%E2%98%83">http://abc.net#☃</a></p>
// <p><a href="http://abc.net/foo#%E2%98%83">http://abc.net/foo#☃</a></p>
Console.WriteLine("Example 21\nSection Extensions / AutoLinks / Unicode support\n");
TestParser.TestSpec("http://abc.net/☃\n\nhttp://abc.net?☃\n\nhttp://abc.net#☃\n\nhttp://abc.net/foo#☃", "<p><a href=\"http://abc.net/%E2%98%83\">http://abc.net/☃</a></p>\n<p><a href=\"http://abc.net?%E2%98%83\">http://abc.net?☃</a></p>\n<p><a href=\"http://abc.net#%E2%98%83\">http://abc.net#☃</a></p>\n<p><a href=\"http://abc.net/foo#%E2%98%83\">http://abc.net/foo#☃</a></p>", "autolinks|advanced");
TestParser.TestSpec("http://abc.net/☃\n\nhttp://abc.net?☃\n\nhttp://abc.net#☃\n\nhttp://abc.net/foo#☃", "<p><a href=\"http://abc.net/%E2%98%83\">http://abc.net/☃</a></p>\n<p><a href=\"http://abc.net?%E2%98%83\">http://abc.net?☃</a></p>\n<p><a href=\"http://abc.net#%E2%98%83\">http://abc.net#☃</a></p>\n<p><a href=\"http://abc.net/foo#%E2%98%83\">http://abc.net/foo#☃</a></p>", "autolinks|advanced", context: "Example 21\nSection Extensions / AutoLinks / Unicode support\n");
}
// Unicode characters in the FQDN are matched and IDNA encoded
@@ -495,8 +474,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p><a href="http://xn--n3h.net?%E2%98%83">http://☃.net?☃</a></p>
Console.WriteLine("Example 22\nSection Extensions / AutoLinks / Unicode support\n");
TestParser.TestSpec("http://☃.net?☃", "<p><a href=\"http://xn--n3h.net?%E2%98%83\">http://☃.net?☃</a></p>", "autolinks|advanced");
TestParser.TestSpec("http://☃.net?☃", "<p><a href=\"http://xn--n3h.net?%E2%98%83\">http://☃.net?☃</a></p>", "autolinks|advanced", context: "Example 22\nSection Extensions / AutoLinks / Unicode support\n");
}
// Same goes for regular autolinks
@@ -521,8 +499,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// <p><a href="http://abc.net#%E2%98%83">http://abc.net#☃</a></p>
// <p><a href="http://abc.net/foo#%E2%98%83">http://abc.net/foo#☃</a></p>
Console.WriteLine("Example 23\nSection Extensions / AutoLinks / Unicode support\n");
TestParser.TestSpec("<http://abc.net/☃>\n\n<http://abc.net?☃>\n\n<http://abc.net#☃>\n\n<http://abc.net/foo#☃>", "<p><a href=\"http://abc.net/%E2%98%83\">http://abc.net/☃</a></p>\n<p><a href=\"http://abc.net?%E2%98%83\">http://abc.net?☃</a></p>\n<p><a href=\"http://abc.net#%E2%98%83\">http://abc.net#☃</a></p>\n<p><a href=\"http://abc.net/foo#%E2%98%83\">http://abc.net/foo#☃</a></p>", "autolinks|advanced");
TestParser.TestSpec("<http://abc.net/☃>\n\n<http://abc.net?☃>\n\n<http://abc.net#☃>\n\n<http://abc.net/foo#☃>", "<p><a href=\"http://abc.net/%E2%98%83\">http://abc.net/☃</a></p>\n<p><a href=\"http://abc.net?%E2%98%83\">http://abc.net?☃</a></p>\n<p><a href=\"http://abc.net#%E2%98%83\">http://abc.net#☃</a></p>\n<p><a href=\"http://abc.net/foo#%E2%98%83\">http://abc.net/foo#☃</a></p>", "autolinks|advanced", context: "Example 23\nSection Extensions / AutoLinks / Unicode support\n");
}
[Test]
@@ -537,8 +514,7 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p><a href="http://xn--n3h.net?%E2%98%83">http://☃.net?☃</a></p>
Console.WriteLine("Example 24\nSection Extensions / AutoLinks / Unicode support\n");
TestParser.TestSpec("<http://☃.net?☃>", "<p><a href=\"http://xn--n3h.net?%E2%98%83\">http://☃.net?☃</a></p>", "autolinks|advanced");
TestParser.TestSpec("<http://☃.net?☃>", "<p><a href=\"http://xn--n3h.net?%E2%98%83\">http://☃.net?☃</a></p>", "autolinks|advanced", context: "Example 24\nSection Extensions / AutoLinks / Unicode support\n");
}
// It also complies with CommonMark's vision of priority.
@@ -555,8 +531,30 @@ namespace Markdig.Tests.Specs.AutoLinks
// Should be rendered as:
// <p><a href="http://xn--fo-gka.bar.%60baz">http://foö.bar.`baz</a>`</p>
Console.WriteLine("Example 25\nSection Extensions / AutoLinks / Unicode support\n");
TestParser.TestSpec("<http://foö.bar.`baz>`", "<p><a href=\"http://xn--fo-gka.bar.%60baz\">http://foö.bar.`baz</a>`</p>", "autolinks|advanced");
TestParser.TestSpec("<http://foö.bar.`baz>`", "<p><a href=\"http://xn--fo-gka.bar.%60baz\">http://foö.bar.`baz</a>`</p>", "autolinks|advanced", context: "Example 25\nSection Extensions / AutoLinks / Unicode support\n");
}
// Unicode punctuation characters are not allowed, but symbols are.
// Note that this does _not_ exactly match CommonMark's "Unicode punctuation character" definition.
[Test]
public void ExtensionsAutoLinksUnicodeSupport_Example026()
{
// Example 26
// Section: Extensions / AutoLinks / Unicode support
//
// The following Markdown:
// http://☃.net?☃ // OtherSymbol
//
// http://🍉.net?🍉 // A UTF-16 surrogate pair, but code point is OtherSymbol
//
// http://‰.net?‰ // OtherPunctuation
//
// Should be rendered as:
// <p><a href="http://xn--n3h.net?%E2%98%83">http://☃.net?☃</a> // OtherSymbol</p>
// <p><a href="http://xn--ji8h.net?%F0%9F%8D%89">http://🍉.net?🍉</a> // A UTF-16 surrogate pair, but code point is OtherSymbol</p>
// <p>http://‰.net?‰ // OtherPunctuation</p>
TestParser.TestSpec("http://☃.net?☃ // OtherSymbol\n\nhttp://🍉.net?🍉 // A UTF-16 surrogate pair, but code point is OtherSymbol\n\nhttp://‰.net?‰ // OtherPunctuation", "<p><a href=\"http://xn--n3h.net?%E2%98%83\">http://☃.net?☃</a> // OtherSymbol</p>\n<p><a href=\"http://xn--ji8h.net?%F0%9F%8D%89\">http://🍉.net?🍉</a> // A UTF-16 surrogate pair, but code point is OtherSymbol</p>\n<p>http://‰.net?‰ // OtherPunctuation</p>", "autolinks|advanced", context: "Example 26\nSection Extensions / AutoLinks / Unicode support\n");
}
}
}

View File

@@ -303,4 +303,19 @@ This will therefore be seen as an autolink and not as code inline.
<http://foö.bar.`baz>`
.
<p><a href="http://xn--fo-gka.bar.%60baz">http://foö.bar.`baz</a>`</p>
````````````````````````````````
Unicode punctuation characters are not allowed, but symbols are.
Note that this does _not_ exactly match CommonMark's "Unicode punctuation character" definition.
```````````````````````````````` example
http://☃.net?☃ // OtherSymbol
http://🍉.net?🍉 // A UTF-16 surrogate pair, but code point is OtherSymbol
http://‰.net?‰ // OtherPunctuation
.
<p><a href="http://xn--n3h.net?%E2%98%83">http://☃.net?☃</a> // OtherSymbol</p>
<p><a href="http://xn--ji8h.net?%F0%9F%8D%89">http://🍉.net?🍉</a> // A UTF-16 surrogate pair, but code point is OtherSymbol</p>
<p>http://‰.net?‰ // OtherPunctuation</p>
````````````````````````````````

View File

@@ -45,8 +45,7 @@ namespace Markdig.Tests.Specs.Bootstrap
// </tbody>
// </table>
Console.WriteLine("Example 1\nSection Extensions / Bootstrap\n");
TestParser.TestSpec("Name | Value\n-----| -----\nAbc | 16", "<table class=\"table\">\n<thead>\n<tr>\n<th>Name</th>\n<th>Value</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Abc</td>\n<td>16</td>\n</tr>\n</tbody>\n</table>", "bootstrap+pipetables+figures+attributes");
TestParser.TestSpec("Name | Value\n-----| -----\nAbc | 16", "<table class=\"table\">\n<thead>\n<tr>\n<th>Name</th>\n<th>Value</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Abc</td>\n<td>16</td>\n</tr>\n</tbody>\n</table>", "bootstrap+pipetables+figures+attributes", context: "Example 1\nSection Extensions / Bootstrap\n");
}
// Adds bootstrap `.blockquote` class to `<blockquote>`:
@@ -64,8 +63,7 @@ namespace Markdig.Tests.Specs.Bootstrap
// <p>This is a blockquote</p>
// </blockquote>
Console.WriteLine("Example 2\nSection Extensions / Bootstrap\n");
TestParser.TestSpec("> This is a blockquote", "<blockquote class=\"blockquote\">\n<p>This is a blockquote</p>\n</blockquote>", "bootstrap+pipetables+figures+attributes");
TestParser.TestSpec("> This is a blockquote", "<blockquote class=\"blockquote\">\n<p>This is a blockquote</p>\n</blockquote>", "bootstrap+pipetables+figures+attributes", context: "Example 2\nSection Extensions / Bootstrap\n");
}
// Adds bootstrap `.figure` class to `<figure>` and `.figure-caption` to `<figcaption>`
@@ -86,8 +84,7 @@ namespace Markdig.Tests.Specs.Bootstrap
// <figcaption class="figure-caption">This is the caption</figcaption>
// </figure>
Console.WriteLine("Example 3\nSection Extensions / Bootstrap\n");
TestParser.TestSpec("^^^\nThis is a text in a caption\n^^^ This is the caption", "<figure class=\"figure\">\n<p>This is a text in a caption</p>\n<figcaption class=\"figure-caption\">This is the caption</figcaption>\n</figure>", "bootstrap+pipetables+figures+attributes");
TestParser.TestSpec("^^^\nThis is a text in a caption\n^^^ This is the caption", "<figure class=\"figure\">\n<p>This is a text in a caption</p>\n<figcaption class=\"figure-caption\">This is the caption</figcaption>\n</figure>", "bootstrap+pipetables+figures+attributes", context: "Example 3\nSection Extensions / Bootstrap\n");
}
// Adds the `.img-fluid` class to all image links `<img>`
@@ -103,8 +100,7 @@ namespace Markdig.Tests.Specs.Bootstrap
// Should be rendered as:
// <p><img src="/url" class="img-fluid" alt="Image Link" /></p>
Console.WriteLine("Example 4\nSection Extensions / Bootstrap\n");
TestParser.TestSpec("![Image Link](/url)", "<p><img src=\"/url\" class=\"img-fluid\" alt=\"Image Link\" /></p>", "bootstrap+pipetables+figures+attributes");
TestParser.TestSpec("![Image Link](/url)", "<p><img src=\"/url\" class=\"img-fluid\" alt=\"Image Link\" /></p>", "bootstrap+pipetables+figures+attributes", context: "Example 4\nSection Extensions / Bootstrap\n");
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -33,8 +33,7 @@ namespace Markdig.Tests.Specs.CustomContainers
// <div class="spoiler"><p>This is a <em>spoiler</em></p>
// </div>
Console.WriteLine("Example 1\nSection Extensions / Custom Container\n");
TestParser.TestSpec(":::spoiler\nThis is a *spoiler*\n:::", "<div class=\"spoiler\"><p>This is a <em>spoiler</em></p>\n</div>", "customcontainers+attributes|advanced");
TestParser.TestSpec(":::spoiler\nThis is a *spoiler*\n:::", "<div class=\"spoiler\"><p>This is a <em>spoiler</em></p>\n</div>", "customcontainers+attributes|advanced", context: "Example 1\nSection Extensions / Custom Container\n");
}
// The text following the opened custom container is optional:
@@ -53,8 +52,7 @@ namespace Markdig.Tests.Specs.CustomContainers
// <div><p>This is a regular div</p>
// </div>
Console.WriteLine("Example 2\nSection Extensions / Custom Container\n");
TestParser.TestSpec(":::\nThis is a regular div\n:::", "<div><p>This is a regular div</p>\n</div>", "customcontainers+attributes|advanced");
TestParser.TestSpec(":::\nThis is a regular div\n:::", "<div><p>This is a regular div</p>\n</div>", "customcontainers+attributes|advanced", context: "Example 2\nSection Extensions / Custom Container\n");
}
// Like for fenced code block, you can use more than 3 `:` characters as long as the closing has the same number of characters:
@@ -73,8 +71,7 @@ namespace Markdig.Tests.Specs.CustomContainers
// <div class="spoiler"><p>This is a spoiler</p>
// </div>
Console.WriteLine("Example 3\nSection Extensions / Custom Container\n");
TestParser.TestSpec("::::::::::::spoiler\nThis is a spoiler\n::::::::::::", "<div class=\"spoiler\"><p>This is a spoiler</p>\n</div>", "customcontainers+attributes|advanced");
TestParser.TestSpec("::::::::::::spoiler\nThis is a spoiler\n::::::::::::", "<div class=\"spoiler\"><p>This is a spoiler</p>\n</div>", "customcontainers+attributes|advanced", context: "Example 3\nSection Extensions / Custom Container\n");
}
// Like for fenced code block, a custom container can span over multiple empty lines in a list block:
@@ -106,8 +103,7 @@ namespace Markdig.Tests.Specs.CustomContainers
// <li>A second item in the list</li>
// </ul>
Console.WriteLine("Example 4\nSection Extensions / Custom Container\n");
TestParser.TestSpec("- This is a list\n :::spoiler\n This is a spoiler\n - item1\n - item2\n :::\n- A second item in the list", "<ul>\n<li>This is a list\n<div class=\"spoiler\">This is a spoiler\n<ul>\n<li>item1</li>\n<li>item2</li>\n</ul>\n</div>\n</li>\n<li>A second item in the list</li>\n</ul>", "customcontainers+attributes|advanced");
TestParser.TestSpec("- This is a list\n :::spoiler\n This is a spoiler\n - item1\n - item2\n :::\n- A second item in the list", "<ul>\n<li>This is a list\n<div class=\"spoiler\">This is a spoiler\n<ul>\n<li>item1</li>\n<li>item2</li>\n</ul>\n</div>\n</li>\n<li>A second item in the list</li>\n</ul>", "customcontainers+attributes|advanced", context: "Example 4\nSection Extensions / Custom Container\n");
}
// Attributes extension is also supported for Custom Container, as long as the Attributes extension is activated after the CustomContainer extension (`.UseCustomContainer().UseAttributes()`)
@@ -126,8 +122,7 @@ namespace Markdig.Tests.Specs.CustomContainers
// <div id="myspoiler" class="spoiler" myprop="yes"><p>This is a spoiler</p>
// </div>
Console.WriteLine("Example 5\nSection Extensions / Custom Container\n");
TestParser.TestSpec(":::spoiler {#myspoiler myprop=yes}\nThis is a spoiler\n:::", "<div id=\"myspoiler\" class=\"spoiler\" myprop=\"yes\"><p>This is a spoiler</p>\n</div>", "customcontainers+attributes|advanced");
TestParser.TestSpec(":::spoiler {#myspoiler myprop=yes}\nThis is a spoiler\n:::", "<div id=\"myspoiler\" class=\"spoiler\" myprop=\"yes\"><p>This is a spoiler</p>\n</div>", "customcontainers+attributes|advanced", context: "Example 5\nSection Extensions / Custom Container\n");
}
// The content of a custom container can contain any blocks:
@@ -146,8 +141,7 @@ namespace Markdig.Tests.Specs.CustomContainers
// <div class="mycontainer"><p>This is a raw spoiler</p>
// </div>
Console.WriteLine("Example 6\nSection Extensions / Custom Container\n");
TestParser.TestSpec(":::mycontainer\n<p>This is a raw spoiler</p>\n:::", "<div class=\"mycontainer\"><p>This is a raw spoiler</p>\n</div>", "customcontainers+attributes|advanced");
TestParser.TestSpec(":::mycontainer\n<p>This is a raw spoiler</p>\n:::", "<div class=\"mycontainer\"><p>This is a raw spoiler</p>\n</div>", "customcontainers+attributes|advanced", context: "Example 6\nSection Extensions / Custom Container\n");
}
}
@@ -169,8 +163,7 @@ namespace Markdig.Tests.Specs.CustomContainers
// Should be rendered as:
// <p>This is a text <span>with special emphasis</span></p>
Console.WriteLine("Example 7\nSection Extensions / Inline Custom Container \n");
TestParser.TestSpec("This is a text ::with special emphasis::", "<p>This is a text <span>with special emphasis</span></p>", "customcontainers+attributes|advanced");
TestParser.TestSpec("This is a text ::with special emphasis::", "<p>This is a text <span>with special emphasis</span></p>", "customcontainers+attributes|advanced", context: "Example 7\nSection Extensions / Inline Custom Container \n");
}
// Any other emphasis inline can be used within this emphasis inline container:
@@ -186,8 +179,7 @@ namespace Markdig.Tests.Specs.CustomContainers
// Should be rendered as:
// <p>This is a text <span>with special <em>emphasis</em></span></p>
Console.WriteLine("Example 8\nSection Extensions / Inline Custom Container \n");
TestParser.TestSpec("This is a text ::with special *emphasis*::", "<p>This is a text <span>with special <em>emphasis</em></span></p>", "customcontainers+attributes|advanced");
TestParser.TestSpec("This is a text ::with special *emphasis*::", "<p>This is a text <span>with special <em>emphasis</em></span></p>", "customcontainers+attributes|advanced", context: "Example 8\nSection Extensions / Inline Custom Container \n");
}
// Attributes can be attached to a inline custom container:
@@ -203,8 +195,7 @@ namespace Markdig.Tests.Specs.CustomContainers
// Should be rendered as:
// <p>This is a text <span id="myId" class="myemphasis">with special emphasis</span></p>
Console.WriteLine("Example 9\nSection Extensions / Inline Custom Container \n");
TestParser.TestSpec("This is a text ::with special emphasis::{#myId .myemphasis}", "<p>This is a text <span id=\"myId\" class=\"myemphasis\">with special emphasis</span></p>", "customcontainers+attributes|advanced");
TestParser.TestSpec("This is a text ::with special emphasis::{#myId .myemphasis}", "<p>This is a text <span id=\"myId\" class=\"myemphasis\">with special emphasis</span></p>", "customcontainers+attributes|advanced", context: "Example 9\nSection Extensions / Inline Custom Container \n");
}
}
}

View File

@@ -71,8 +71,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
// <dd>This is another definition for term2</dd>
// </dl>
Console.WriteLine("Example 1\nSection Extensions / Definition lists\n");
TestParser.TestSpec("\nTerm 1\n: This is a definition item\n With a paragraph\n > This is a block quote\n\n - This is a list\n - with an item2\n\n ```java\n Test\n\n\n ```\n\n And a last line\n: This ia another definition item\n\nTerm2\nTerm3 *with some inline*\n: This is another definition for term2", "<dl>\n<dt>Term 1</dt>\n<dd><p>This is a definition item\nWith a paragraph</p>\n<blockquote>\n<p>This is a block quote</p>\n</blockquote>\n<ul>\n<li>This is a list</li>\n<li>with an item2</li>\n</ul>\n<pre><code class=\"language-java\">Test\n\n\n</code></pre>\n<p>And a last line</p>\n</dd>\n<dd>This ia another definition item</dd>\n<dt>Term2</dt>\n<dt>Term3 <em>with some inline</em></dt>\n<dd>This is another definition for term2</dd>\n</dl>", "definitionlists+attributes|advanced");
TestParser.TestSpec("\nTerm 1\n: This is a definition item\n With a paragraph\n > This is a block quote\n\n - This is a list\n - with an item2\n\n ```java\n Test\n\n\n ```\n\n And a last line\n: This ia another definition item\n\nTerm2\nTerm3 *with some inline*\n: This is another definition for term2", "<dl>\n<dt>Term 1</dt>\n<dd><p>This is a definition item\nWith a paragraph</p>\n<blockquote>\n<p>This is a block quote</p>\n</blockquote>\n<ul>\n<li>This is a list</li>\n<li>with an item2</li>\n</ul>\n<pre><code class=\"language-java\">Test\n\n\n</code></pre>\n<p>And a last line</p>\n</dd>\n<dd>This ia another definition item</dd>\n<dt>Term2</dt>\n<dt>Term3 <em>with some inline</em></dt>\n<dd>This is another definition for term2</dd>\n</dl>", "definitionlists+attributes|advanced", context: "Example 1\nSection Extensions / Definition lists\n");
}
// A definition term can be followed at most by one blank line. Lazy continuations are supported for definitions:
@@ -99,8 +98,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
// </dd>
// </dl>
Console.WriteLine("Example 2\nSection Extensions / Definition lists\n");
TestParser.TestSpec("Term 1\n\n: Definition\nwith lazy continuation.\n\n Second paragraph of the definition.", "<dl>\n<dt>Term 1</dt>\n<dd><p>Definition\nwith lazy continuation.</p>\n<p>Second paragraph of the definition.</p>\n</dd>\n</dl>", "definitionlists+attributes|advanced");
TestParser.TestSpec("Term 1\n\n: Definition\nwith lazy continuation.\n\n Second paragraph of the definition.", "<dl>\n<dt>Term 1</dt>\n<dd><p>Definition\nwith lazy continuation.</p>\n<p>Second paragraph of the definition.</p>\n</dd>\n</dl>", "definitionlists+attributes|advanced", context: "Example 2\nSection Extensions / Definition lists\n");
}
// The definition must be indented to 4 characters including the `:`.
@@ -118,8 +116,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
// <p>Term 1
// : Invalid with less than 3 characters</p>
Console.WriteLine("Example 3\nSection Extensions / Definition lists\n");
TestParser.TestSpec("Term 1\n: Invalid with less than 3 characters", "<p>Term 1\n: Invalid with less than 3 characters</p>", "definitionlists+attributes|advanced");
TestParser.TestSpec("Term 1\n: Invalid with less than 3 characters", "<p>Term 1\n: Invalid with less than 3 characters</p>", "definitionlists+attributes|advanced", context: "Example 3\nSection Extensions / Definition lists\n");
}
// The `:` can be indented up to 3 spaces:
@@ -139,8 +136,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
// <dd>Valid even if <code>:</code> starts at most 3 spaces</dd>
// </dl>
Console.WriteLine("Example 4\nSection Extensions / Definition lists\n");
TestParser.TestSpec("Term 1\n : Valid even if `:` starts at most 3 spaces", "<dl>\n<dt>Term 1</dt>\n<dd>Valid even if <code>:</code> starts at most 3 spaces</dd>\n</dl>", "definitionlists+attributes|advanced");
TestParser.TestSpec("Term 1\n : Valid even if `:` starts at most 3 spaces", "<dl>\n<dt>Term 1</dt>\n<dd>Valid even if <code>:</code> starts at most 3 spaces</dd>\n</dl>", "definitionlists+attributes|advanced", context: "Example 4\nSection Extensions / Definition lists\n");
}
// But more than 3 spaces before `:` will trigger an indented code block:
@@ -160,8 +156,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
// <pre><code>: Not valid
// </code></pre>
Console.WriteLine("Example 5\nSection Extensions / Definition lists\n");
TestParser.TestSpec("Term 1\n\n : Not valid", "<p>Term 1</p>\n<pre><code>: Not valid\n</code></pre>", "definitionlists+attributes|advanced");
TestParser.TestSpec("Term 1\n\n : Not valid", "<p>Term 1</p>\n<pre><code>: Not valid\n</code></pre>", "definitionlists+attributes|advanced", context: "Example 5\nSection Extensions / Definition lists\n");
}
// Definition lists can be nested inside list items
@@ -194,8 +189,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
// </dl></li>
// </ol>
Console.WriteLine("Example 6\nSection Extensions / Definition lists\n");
TestParser.TestSpec("1. First\n \n2. Second\n \n Term 1\n : Definition\n \n Term 2\n : Second Definition", "<ol>\n<li><p>First</p></li>\n<li><p>Second</p>\n<dl>\n<dt>Term 1</dt>\n<dd>Definition</dd>\n<dt>Term 2</dt>\n<dd>Second Definition</dd>\n</dl></li>\n</ol>", "definitionlists+attributes|advanced");
TestParser.TestSpec("1. First\n \n2. Second\n \n Term 1\n : Definition\n \n Term 2\n : Second Definition", "<ol>\n<li><p>First</p></li>\n<li><p>Second</p>\n<dl>\n<dt>Term 1</dt>\n<dd>Definition</dd>\n<dt>Term 2</dt>\n<dd>Second Definition</dd>\n</dl></li>\n</ol>", "definitionlists+attributes|advanced", context: "Example 6\nSection Extensions / Definition lists\n");
}
}
}

View File

@@ -17,7 +17,7 @@ namespace Markdig.Tests.Specs.Diagrams
//
// ## Mermaid diagrams
//
// Using a fenced code block with the `mermaid` language info will output a `<div class='mermaid'>` instead of a `pre/code` block:
// Using a fenced code block with the `mermaid` language info will output a `<pre class='mermaid'>` block (which is the default for other code block):
[Test]
public void ExtensionsMermaidDiagrams_Example001()
{
@@ -34,15 +34,14 @@ namespace Markdig.Tests.Specs.Diagrams
// ```
//
// Should be rendered as:
// <div class="mermaid">graph TD;
// <pre class="mermaid">graph TD;
// A-->B;
// A-->C;
// B-->D;
// C-->D;
// </div>
// </pre>
Console.WriteLine("Example 1\nSection Extensions / Mermaid diagrams\n");
TestParser.TestSpec("```mermaid\ngraph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n```", "<div class=\"mermaid\">graph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n</div>", "diagrams|advanced");
TestParser.TestSpec("```mermaid\ngraph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n```", "<pre class=\"mermaid\">graph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n</pre>", "diagrams|advanced", context: "Example 1\nSection Extensions / Mermaid diagrams\n");
}
}
@@ -85,8 +84,7 @@ namespace Markdig.Tests.Specs.Diagrams
// ]
// </div>
Console.WriteLine("Example 2\nSection Extensions / nomnoml diagrams\n");
TestParser.TestSpec("```nomnoml\n[example|\n propertyA: Int\n propertyB: string\n|\n methodA()\n methodB()\n|\n [subA]--[subB]\n [subA]-:>[sub C]\n]\n```", "<div class=\"nomnoml\">[example|\n propertyA: Int\n propertyB: string\n|\n methodA()\n methodB()\n|\n [subA]--[subB]\n [subA]-:>[sub C]\n]\n</div>", "diagrams|advanced");
TestParser.TestSpec("```nomnoml\n[example|\n propertyA: Int\n propertyB: string\n|\n methodA()\n methodB()\n|\n [subA]--[subB]\n [subA]-:>[sub C]\n]\n```", "<div class=\"nomnoml\">[example|\n propertyA: Int\n propertyB: string\n|\n methodA()\n methodB()\n|\n [subA]--[subB]\n [subA]-:>[sub C]\n]\n</div>", "diagrams|advanced", context: "Example 2\nSection Extensions / nomnoml diagrams\n");
}
// TODO: Add other text diagram languages
}

View File

@@ -4,7 +4,7 @@ Adds support for diagrams extension:
## Mermaid diagrams
Using a fenced code block with the `mermaid` language info will output a `<div class='mermaid'>` instead of a `pre/code` block:
Using a fenced code block with the `mermaid` language info will output a `<pre class='mermaid'>` block (which is the default for other code block):
```````````````````````````````` example
```mermaid
@@ -15,12 +15,12 @@ graph TD;
C-->D;
```
.
<div class="mermaid">graph TD;
<pre class="mermaid">graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
</div>
</pre>
````````````````````````````````
## nomnoml diagrams

View File

@@ -30,8 +30,7 @@ namespace Markdig.Tests.Specs.Emoji
// Should be rendered as:
// <p>This is a test with a 😃 and a 😠 smiley</p>
Console.WriteLine("Example 1\nSection Extensions / Emoji\n");
TestParser.TestSpec("This is a test with a :) and a :angry: smiley", "<p>This is a test with a 😃 and a 😠 smiley</p>", "emojis|advanced+emojis");
TestParser.TestSpec("This is a test with a :) and a :angry: smiley", "<p>This is a test with a 😃 and a 😠 smiley</p>", "emojis|advanced+emojis", context: "Example 1\nSection Extensions / Emoji\n");
}
// An emoji needs to be preceded by a space:
@@ -47,8 +46,7 @@ namespace Markdig.Tests.Specs.Emoji
// Should be rendered as:
// <p>These are not:) an emoji with a:) x:angry:x</p>
Console.WriteLine("Example 2\nSection Extensions / Emoji\n");
TestParser.TestSpec("These are not:) an emoji with a:) x:angry:x", "<p>These are not:) an emoji with a:) x:angry:x</p>", "emojis|advanced+emojis");
TestParser.TestSpec("These are not:) an emoji with a:) x:angry:x", "<p>These are not:) an emoji with a:) x:angry:x</p>", "emojis|advanced+emojis", context: "Example 2\nSection Extensions / Emoji\n");
}
// Emojis can be followed by close punctuation (or any other characters):
@@ -64,8 +62,7 @@ namespace Markdig.Tests.Specs.Emoji
// Should be rendered as:
// <p>We all need 😃, it makes us 💪. (and 👌).</p>
Console.WriteLine("Example 3\nSection Extensions / Emoji\n");
TestParser.TestSpec("We all need :), it makes us :muscle:. (and :ok_hand:).", "<p>We all need 😃, it makes us 💪. (and 👌).</p>", "emojis|advanced+emojis");
TestParser.TestSpec("We all need :), it makes us :muscle:. (and :ok_hand:).", "<p>We all need 😃, it makes us 💪. (and 👌).</p>", "emojis|advanced+emojis", context: "Example 3\nSection Extensions / Emoji\n");
}
// Sentences can end with emojis:
@@ -83,8 +80,7 @@ namespace Markdig.Tests.Specs.Emoji
// <p>This is a sentence 👌
// and keeps going to the next line 😃</p>
Console.WriteLine("Example 4\nSection Extensions / Emoji\n");
TestParser.TestSpec("This is a sentence :ok_hand:\nand keeps going to the next line :)", "<p>This is a sentence 👌\nand keeps going to the next line 😃</p>", "emojis|advanced+emojis");
TestParser.TestSpec("This is a sentence :ok_hand:\nand keeps going to the next line :)", "<p>This is a sentence 👌\nand keeps going to the next line 😃</p>", "emojis|advanced+emojis", context: "Example 4\nSection Extensions / Emoji\n");
}
}
}

View File

@@ -30,8 +30,7 @@ namespace Markdig.Tests.Specs.EmphasisExtra
// Should be rendered as:
// <p>The following text <del>is deleted</del></p>
Console.WriteLine("Example 1\nSection Extensions / Strikethrough\n");
TestParser.TestSpec("The following text ~~is deleted~~", "<p>The following text <del>is deleted</del></p>", "emphasisextras|advanced");
TestParser.TestSpec("The following text ~~is deleted~~", "<p>The following text <del>is deleted</del></p>", "emphasisextras|advanced", context: "Example 1\nSection Extensions / Strikethrough\n");
}
}
@@ -53,8 +52,7 @@ namespace Markdig.Tests.Specs.EmphasisExtra
// Should be rendered as:
// <p>H<sub>2</sub>O is a liquid. 2<sup>10</sup> is 1024</p>
Console.WriteLine("Example 2\nSection Extensions / Superscript and Subscript\n");
TestParser.TestSpec("H~2~O is a liquid. 2^10^ is 1024", "<p>H<sub>2</sub>O is a liquid. 2<sup>10</sup> is 1024</p>", "emphasisextras|advanced");
TestParser.TestSpec("H~2~O is a liquid. 2^10^ is 1024", "<p>H<sub>2</sub>O is a liquid. 2<sup>10</sup> is 1024</p>", "emphasisextras|advanced", context: "Example 2\nSection Extensions / Superscript and Subscript\n");
}
// Certain punctuation characters are exempted from the rule forbidding them within inline delimiters
@@ -73,8 +71,7 @@ namespace Markdig.Tests.Specs.EmphasisExtra
// <p>One quintillionth can be expressed as 10<sup>-18</sup></p>
// <p>Daggers<sup>†</sup> and double-daggers<sup>‡</sup> can be used to denote notes.</p>
Console.WriteLine("Example 3\nSection Extensions / Superscript and Subscript\n");
TestParser.TestSpec("One quintillionth can be expressed as 10^-18^\n\nDaggers^†^ and double-daggers^‡^ can be used to denote notes.", "<p>One quintillionth can be expressed as 10<sup>-18</sup></p>\n<p>Daggers<sup>†</sup> and double-daggers<sup>‡</sup> can be used to denote notes.</p>", "emphasisextras|advanced");
TestParser.TestSpec("One quintillionth can be expressed as 10^-18^\n\nDaggers^†^ and double-daggers^‡^ can be used to denote notes.", "<p>One quintillionth can be expressed as 10<sup>-18</sup></p>\n<p>Daggers<sup>†</sup> and double-daggers<sup>‡</sup> can be used to denote notes.</p>", "emphasisextras|advanced", context: "Example 3\nSection Extensions / Superscript and Subscript\n");
}
}
@@ -96,8 +93,7 @@ namespace Markdig.Tests.Specs.EmphasisExtra
// Should be rendered as:
// <p><ins>Inserted text</ins></p>
Console.WriteLine("Example 4\nSection Extensions / Inserted\n");
TestParser.TestSpec("++Inserted text++", "<p><ins>Inserted text</ins></p>", "emphasisextras|advanced");
TestParser.TestSpec("++Inserted text++", "<p><ins>Inserted text</ins></p>", "emphasisextras|advanced", context: "Example 4\nSection Extensions / Inserted\n");
}
}
@@ -119,8 +115,7 @@ namespace Markdig.Tests.Specs.EmphasisExtra
// Should be rendered as:
// <p><mark>Marked text</mark></p>
Console.WriteLine("Example 5\nSection Extensions / Marked\n");
TestParser.TestSpec("==Marked text==", "<p><mark>Marked text</mark></p>", "emphasisextras|advanced");
TestParser.TestSpec("==Marked text==", "<p><mark>Marked text</mark></p>", "emphasisextras|advanced", context: "Example 5\nSection Extensions / Marked\n");
}
}
@@ -128,6 +123,8 @@ namespace Markdig.Tests.Specs.EmphasisExtra
public class TestExtensionsEmphasisOnHtmlEntities
{
// ## Emphasis on Html Entities
//
// Note that Unicode symbols are treated as punctuation, which are not allowed to open the emphasis unless they are preceded by a space.
[Test]
public void ExtensionsEmphasisOnHtmlEntities_Example006()
{
@@ -137,15 +134,14 @@ namespace Markdig.Tests.Specs.EmphasisExtra
// The following Markdown:
// This is text MyBrand ^&reg;^ and MyTrademark ^&trade;^
// This is text MyBrand^&reg;^ and MyTrademark^&trade;^
// This is text MyBrand~&reg;~ and MyCopyright^&copy;^
// This is text MyBrand ~&reg;~ and MyCopyright ^&copy;^
//
// Should be rendered as:
// <p>This is text MyBrand <sup>®</sup> and MyTrademark <sup>TM</sup>
// This is text MyBrand<sup>®</sup> and MyTrademark<sup>TM</sup>
// This is text MyBrand<sub>®</sub> and MyCopyright<sup>©</sup></p>
// This is text MyBrand^®^ and MyTrademark^TM^
// This is text MyBrand <sub>®</sub> and MyCopyright <sup>©</sup></p>
Console.WriteLine("Example 6\nSection Extensions / Emphasis on Html Entities\n");
TestParser.TestSpec("This is text MyBrand ^&reg;^ and MyTrademark ^&trade;^\nThis is text MyBrand^&reg;^ and MyTrademark^&trade;^\nThis is text MyBrand~&reg;~ and MyCopyright^&copy;^", "<p>This is text MyBrand <sup>®</sup> and MyTrademark <sup>TM</sup>\nThis is text MyBrand<sup>®</sup> and MyTrademark<sup>TM</sup>\nThis is text MyBrand<sub>®</sub> and MyCopyright<sup>©</sup></p>", "emphasisextras|advanced");
TestParser.TestSpec("This is text MyBrand ^&reg;^ and MyTrademark ^&trade;^\nThis is text MyBrand^&reg;^ and MyTrademark^&trade;^\nThis is text MyBrand ~&reg;~ and MyCopyright ^&copy;^", "<p>This is text MyBrand <sup>®</sup> and MyTrademark <sup>TM</sup>\nThis is text MyBrand^®^ and MyTrademark^TM^\nThis is text MyBrand <sub>®</sub> and MyCopyright <sup>©</sup></p>", "emphasisextras|advanced", context: "Example 6\nSection Extensions / Emphasis on Html Entities\n");
}
}
}

View File

@@ -52,16 +52,17 @@ Marked text can be used to specify that a text has been marked in a document. T
.
<p><mark>Marked text</mark></p>
````````````````````````````````
## Emphasis on Html Entities
Note that Unicode symbols are treated as punctuation, which are not allowed to open the emphasis unless they are preceded by a space.
```````````````````````````````` example
This is text MyBrand ^&reg;^ and MyTrademark ^&trade;^
This is text MyBrand^&reg;^ and MyTrademark^&trade;^
This is text MyBrand~&reg;~ and MyCopyright^&copy;^
This is text MyBrand ~&reg;~ and MyCopyright ^&copy;^
.
<p>This is text MyBrand <sup>®</sup> and MyTrademark <sup>TM</sup>
This is text MyBrand<sup>®</sup> and MyTrademark<sup>TM</sup>
This is text MyBrand<sub>®</sub> and MyCopyright<sup>©</sup></p>
This is text MyBrand^®^ and MyTrademark^TM^
This is text MyBrand <sub>®</sub> and MyCopyright <sup>©</sup></p>
````````````````````````````````

View File

@@ -35,8 +35,7 @@ namespace Markdig.Tests.Specs.FiguresFootersAndCites
// <figcaption>This is a <em>caption</em></figcaption>
// </figure>
Console.WriteLine("Example 1\nSection Extensions / Figures\n");
TestParser.TestSpec("^^^\nThis is a figure\n^^^ This is a *caption*", "<figure>\n<p>This is a figure</p>\n<figcaption>This is a <em>caption</em></figcaption>\n</figure>", "figures+footers+citations|advanced");
TestParser.TestSpec("^^^\nThis is a figure\n^^^ This is a *caption*", "<figure>\n<p>This is a figure</p>\n<figcaption>This is a <em>caption</em></figcaption>\n</figure>", "figures+footers+citations|advanced", context: "Example 1\nSection Extensions / Figures\n");
}
}
@@ -60,8 +59,7 @@ namespace Markdig.Tests.Specs.FiguresFootersAndCites
// <footer>This is a footer
// multi-line</footer>
Console.WriteLine("Example 2\nSection Extensions / Footers\n");
TestParser.TestSpec("^^ This is a footer\n^^ multi-line", "<footer>This is a footer\nmulti-line</footer>", "figures+footers+citations|advanced");
TestParser.TestSpec("^^ This is a footer\n^^ multi-line", "<footer>This is a footer\nmulti-line</footer>", "figures+footers+citations|advanced", context: "Example 2\nSection Extensions / Footers\n");
}
}
@@ -83,8 +81,7 @@ namespace Markdig.Tests.Specs.FiguresFootersAndCites
// Should be rendered as:
// <p>This is a <cite>citation of someone</cite></p>
Console.WriteLine("Example 3\nSection Extensions / Cite\n");
TestParser.TestSpec("This is a \"\"citation of someone\"\"", "<p>This is a <cite>citation of someone</cite></p>", "figures+footers+citations|advanced");
TestParser.TestSpec("This is a \"\"citation of someone\"\"", "<p>This is a <cite>citation of someone</cite></p>", "figures+footers+citations|advanced", context: "Example 3\nSection Extensions / Cite\n");
}
}
}

View File

@@ -79,8 +79,7 @@ namespace Markdig.Tests.Specs.Footnotes
// </ol>
// </div>
Console.WriteLine("Example 1\nSection Extensions / Footnotes\n");
TestParser.TestSpec("Here is a footnote reference,[^1] and another.[^longnote]\n\nThis is another reference to [^1]\n\n[^1]: Here is the footnote.\n\nAnd another reference to [^longnote]\n\n[^longnote]: Here's one with multiple blocks.\n\n Subsequent paragraphs are indented to show that they\nbelong to the previous footnote.\n\n > This is a block quote\n > Inside a footnote\n\n { some.code }\n\n The whole paragraph can be indented, or just the first\n line. In this way, multi-paragraph footnotes work like\n multi-paragraph list items.\n\nThis paragraph won't be part of the note, because it\nisn't indented.", "<p>Here is a footnote reference,<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a> and another.<a id=\"fnref:3\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a></p>\n<p>This is another reference to <a id=\"fnref:2\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a></p>\n<p>And another reference to <a id=\"fnref:4\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a></p>\n<p>This paragraph won't be part of the note, because it\nisn't indented.</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Here is the footnote.<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a><a href=\"#fnref:2\" class=\"footnote-back-ref\">&#8617;</a></p>\n</li>\n<li id=\"fn:2\">\n<p>Here's one with multiple blocks.</p>\n<p>Subsequent paragraphs are indented to show that they\nbelong to the previous footnote.</p>\n<blockquote>\n<p>This is a block quote\nInside a footnote</p>\n</blockquote>\n<pre><code>{ some.code }\n</code></pre>\n<p>The whole paragraph can be indented, or just the first\nline. In this way, multi-paragraph footnotes work like\nmulti-paragraph list items.<a href=\"#fnref:3\" class=\"footnote-back-ref\">&#8617;</a><a href=\"#fnref:4\" class=\"footnote-back-ref\">&#8617;</a></p>\n</li>\n</ol>\n</div>", "footnotes|advanced");
TestParser.TestSpec("Here is a footnote reference,[^1] and another.[^longnote]\n\nThis is another reference to [^1]\n\n[^1]: Here is the footnote.\n\nAnd another reference to [^longnote]\n\n[^longnote]: Here's one with multiple blocks.\n\n Subsequent paragraphs are indented to show that they\nbelong to the previous footnote.\n\n > This is a block quote\n > Inside a footnote\n\n { some.code }\n\n The whole paragraph can be indented, or just the first\n line. In this way, multi-paragraph footnotes work like\n multi-paragraph list items.\n\nThis paragraph won't be part of the note, because it\nisn't indented.", "<p>Here is a footnote reference,<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a> and another.<a id=\"fnref:3\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a></p>\n<p>This is another reference to <a id=\"fnref:2\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a></p>\n<p>And another reference to <a id=\"fnref:4\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a></p>\n<p>This paragraph won't be part of the note, because it\nisn't indented.</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Here is the footnote.<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a><a href=\"#fnref:2\" class=\"footnote-back-ref\">&#8617;</a></p>\n</li>\n<li id=\"fn:2\">\n<p>Here's one with multiple blocks.</p>\n<p>Subsequent paragraphs are indented to show that they\nbelong to the previous footnote.</p>\n<blockquote>\n<p>This is a block quote\nInside a footnote</p>\n</blockquote>\n<pre><code>{ some.code }\n</code></pre>\n<p>The whole paragraph can be indented, or just the first\nline. In this way, multi-paragraph footnotes work like\nmulti-paragraph list items.<a href=\"#fnref:3\" class=\"footnote-back-ref\">&#8617;</a><a href=\"#fnref:4\" class=\"footnote-back-ref\">&#8617;</a></p>\n</li>\n</ol>\n</div>", "footnotes|advanced", context: "Example 1\nSection Extensions / Footnotes\n");
}
// Check with multiple consecutive footnotes:
@@ -120,8 +119,7 @@ namespace Markdig.Tests.Specs.Footnotes
// </ol>
// </div>
Console.WriteLine("Example 2\nSection Extensions / Footnotes\n");
TestParser.TestSpec("Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].\n\n[^1]: Footnote 1 text\n\n[^2]: Footnote 2 text\n\na\n\n[^3]: Footnote 3 text\n\n[^4]: Footnote 4 text", "<p>Here is a footnote<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a>. And another one<a id=\"fnref:2\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a>. And a third one<a id=\"fnref:3\" href=\"#fn:3\" class=\"footnote-ref\"><sup>3</sup></a>. And a fourth<a id=\"fnref:4\" href=\"#fn:4\" class=\"footnote-ref\"><sup>4</sup></a>.</p>\n<p>a</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Footnote 1 text<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:2\">\n<p>Footnote 2 text<a href=\"#fnref:2\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:3\">\n<p>Footnote 3 text<a href=\"#fnref:3\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:4\">\n<p>Footnote 4 text<a href=\"#fnref:4\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n</ol>\n</div>", "footnotes|advanced");
TestParser.TestSpec("Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].\n\n[^1]: Footnote 1 text\n\n[^2]: Footnote 2 text\n\na\n\n[^3]: Footnote 3 text\n\n[^4]: Footnote 4 text", "<p>Here is a footnote<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a>. And another one<a id=\"fnref:2\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a>. And a third one<a id=\"fnref:3\" href=\"#fn:3\" class=\"footnote-ref\"><sup>3</sup></a>. And a fourth<a id=\"fnref:4\" href=\"#fn:4\" class=\"footnote-ref\"><sup>4</sup></a>.</p>\n<p>a</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Footnote 1 text<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:2\">\n<p>Footnote 2 text<a href=\"#fnref:2\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:3\">\n<p>Footnote 3 text<a href=\"#fnref:3\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:4\">\n<p>Footnote 4 text<a href=\"#fnref:4\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n</ol>\n</div>", "footnotes|advanced", context: "Example 2\nSection Extensions / Footnotes\n");
}
// Another test with consecutive footnotes without a blank line separator:
@@ -155,8 +153,7 @@ namespace Markdig.Tests.Specs.Footnotes
// </ol>
// </div>
Console.WriteLine("Example 3\nSection Extensions / Footnotes\n");
TestParser.TestSpec("Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].\n\n[^1]: Footnote 1 text\n[^2]: Footnote 2 text\n[^3]: Footnote 3 text\n[^4]: Footnote 4 text", "<p>Here is a footnote<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a>. And another one<a id=\"fnref:2\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a>. And a third one<a id=\"fnref:3\" href=\"#fn:3\" class=\"footnote-ref\"><sup>3</sup></a>. And a fourth<a id=\"fnref:4\" href=\"#fn:4\" class=\"footnote-ref\"><sup>4</sup></a>.</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Footnote 1 text<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:2\">\n<p>Footnote 2 text<a href=\"#fnref:2\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:3\">\n<p>Footnote 3 text<a href=\"#fnref:3\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:4\">\n<p>Footnote 4 text<a href=\"#fnref:4\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n</ol>\n</div>", "footnotes|advanced");
TestParser.TestSpec("Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].\n\n[^1]: Footnote 1 text\n[^2]: Footnote 2 text\n[^3]: Footnote 3 text\n[^4]: Footnote 4 text", "<p>Here is a footnote<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a>. And another one<a id=\"fnref:2\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a>. And a third one<a id=\"fnref:3\" href=\"#fn:3\" class=\"footnote-ref\"><sup>3</sup></a>. And a fourth<a id=\"fnref:4\" href=\"#fn:4\" class=\"footnote-ref\"><sup>4</sup></a>.</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Footnote 1 text<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:2\">\n<p>Footnote 2 text<a href=\"#fnref:2\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:3\">\n<p>Footnote 3 text<a href=\"#fnref:3\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:4\">\n<p>Footnote 4 text<a href=\"#fnref:4\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n</ol>\n</div>", "footnotes|advanced", context: "Example 3\nSection Extensions / Footnotes\n");
}
// A footnote link inside a list should work as well:
@@ -185,8 +182,7 @@ namespace Markdig.Tests.Specs.Footnotes
// </ol>
// </div>
Console.WriteLine("Example 4\nSection Extensions / Footnotes\n");
TestParser.TestSpec("- abc\n- def[^1]\n\n[^1]: Here is the footnote.", "<ul>\n<li>abc</li>\n<li>def<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a></li>\n</ul>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Here is the footnote.<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n</ol>\n</div>", "footnotes|advanced");
TestParser.TestSpec("- abc\n- def[^1]\n\n[^1]: Here is the footnote.", "<ul>\n<li>abc</li>\n<li>def<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a></li>\n</ul>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Here is the footnote.<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n</ol>\n</div>", "footnotes|advanced", context: "Example 4\nSection Extensions / Footnotes\n");
}
}
}

View File

@@ -54,8 +54,7 @@ namespace Markdig.Tests.Specs.GenericAttributes
// <h2 id="heading-link2">This is a heading</h2>
// <p id="myparagraph" attached-bool-property="" attached-bool-property2="">This is a paragraph with an attached attributes </p>
Console.WriteLine("Example 1\nSection Extensions / Generic Attributes\n");
TestParser.TestSpec("# This is a heading with an an attribute{#heading-link}\n\n# This is a heading # {#heading-link2}\n\n[This is a link](http://google.com){#a-link .myclass data-lang=fr data-value=\"This is a value\"}\n\nThis is a heading{#heading-link2}\n-----------------\n\nThis is a paragraph with an attached attributes {#myparagraph attached-bool-property attached-bool-property2}", "<h1 id=\"heading-link\">This is a heading with an an attribute</h1>\n<h1 id=\"heading-link2\">This is a heading</h1>\n<p><a href=\"http://google.com\" id=\"a-link\" class=\"myclass\" data-lang=\"fr\" data-value=\"This is a value\">This is a link</a></p>\n<h2 id=\"heading-link2\">This is a heading</h2>\n<p id=\"myparagraph\" attached-bool-property=\"\" attached-bool-property2=\"\">This is a paragraph with an attached attributes </p>", "attributes|advanced");
TestParser.TestSpec("# This is a heading with an an attribute{#heading-link}\n\n# This is a heading # {#heading-link2}\n\n[This is a link](http://google.com){#a-link .myclass data-lang=fr data-value=\"This is a value\"}\n\nThis is a heading{#heading-link2}\n-----------------\n\nThis is a paragraph with an attached attributes {#myparagraph attached-bool-property attached-bool-property2}", "<h1 id=\"heading-link\">This is a heading with an an attribute</h1>\n<h1 id=\"heading-link2\">This is a heading</h1>\n<p><a href=\"http://google.com\" id=\"a-link\" class=\"myclass\" data-lang=\"fr\" data-value=\"This is a value\">This is a link</a></p>\n<h2 id=\"heading-link2\">This is a heading</h2>\n<p id=\"myparagraph\" attached-bool-property=\"\" attached-bool-property2=\"\">This is a paragraph with an attached attributes </p>", "attributes|advanced", context: "Example 1\nSection Extensions / Generic Attributes\n");
}
// The following shows that attributes can be attached to the next block if they are used inside a single line just preceding the block (and preceded by a blank line or beginning of a block container):
@@ -75,8 +74,7 @@ namespace Markdig.Tests.Specs.GenericAttributes
// <pre><code id="fenced-id" class="fenced-class">This is a fenced with attached attributes
// </code></pre>
Console.WriteLine("Example 2\nSection Extensions / Generic Attributes\n");
TestParser.TestSpec("{#fenced-id .fenced-class}\n~~~\nThis is a fenced with attached attributes\n~~~ ", "<pre><code id=\"fenced-id\" class=\"fenced-class\">This is a fenced with attached attributes\n</code></pre>", "attributes|advanced");
TestParser.TestSpec("{#fenced-id .fenced-class}\n~~~\nThis is a fenced with attached attributes\n~~~ ", "<pre><code id=\"fenced-id\" class=\"fenced-class\">This is a fenced with attached attributes\n</code></pre>", "attributes|advanced", context: "Example 2\nSection Extensions / Generic Attributes\n");
}
// Attribute values can be one character long
@@ -98,8 +96,24 @@ namespace Markdig.Tests.Specs.GenericAttributes
// <p><a href="url" data-x="1">Foo</a></p>
// <p><a href="url" data-x="11">Foo</a></p>
Console.WriteLine("Example 3\nSection Extensions / Generic Attributes\n");
TestParser.TestSpec("[Foo](url){data-x=1}\n\n[Foo](url){data-x='1'}\n\n[Foo](url){data-x=11}", "<p><a href=\"url\" data-x=\"1\">Foo</a></p>\n<p><a href=\"url\" data-x=\"1\">Foo</a></p>\n<p><a href=\"url\" data-x=\"11\">Foo</a></p>", "attributes|advanced");
TestParser.TestSpec("[Foo](url){data-x=1}\n\n[Foo](url){data-x='1'}\n\n[Foo](url){data-x=11}", "<p><a href=\"url\" data-x=\"1\">Foo</a></p>\n<p><a href=\"url\" data-x=\"1\">Foo</a></p>\n<p><a href=\"url\" data-x=\"11\">Foo</a></p>", "attributes|advanced", context: "Example 3\nSection Extensions / Generic Attributes\n");
}
// Attributes that occur immediately before a block element, on a line by themselves, affect that element
[Test]
public void ExtensionsGenericAttributes_Example004()
{
// Example 4
// Section: Extensions / Generic Attributes
//
// The following Markdown:
// {.center}
// A paragraph
//
// Should be rendered as:
// <p class="center">A paragraph</p>
TestParser.TestSpec("{.center}\nA paragraph", "<p class=\"center\">A paragraph</p>", "attributes|advanced", context: "Example 4\nSection Extensions / Generic Attributes\n");
}
}
}

View File

@@ -61,3 +61,12 @@ Attribute values can be one character long
<p><a href="url" data-x="1">Foo</a></p>
<p><a href="url" data-x="11">Foo</a></p>
````````````````````````````````
Attributes that occur immediately before a block element, on a line by themselves, affect that element
```````````````````````````````` example
{.center}
A paragraph
.
<p class="center">A paragraph</p>
````````````````````````````````

View File

@@ -56,8 +56,7 @@ namespace Markdig.Tests.Specs.Globalization
// -- نەزانراو</p>
// </blockquote>
Console.WriteLine("Example 1\nSection Extensions / Globalization\n");
TestParser.TestSpec("# Fruits\nIn botany, a [fruit](https://en.wikipedia.org/wiki/Fruit) is the seed-bearing structure in flowering plants (also known as angiosperms) formed from the ovary after flowering.\n\n> Fruits are good for health\n-- Anonymous\n\n# میوە\n[میوە](https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95) یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو\n\n> میوە بۆ تەندروستی باشە\n-- نەزانراو", "<h1 id=\"fruits\">Fruits</h1>\n<p>In botany, a <a href=\"https://en.wikipedia.org/wiki/Fruit\">fruit</a> is the seed-bearing structure in flowering plants (also known as angiosperms) formed from the ovary after flowering.</p>\n<blockquote>\n<p>Fruits are good for health\n-- Anonymous</p>\n</blockquote>\n<h1 id=\"section\" dir=\"rtl\">میوە</h1>\n<p dir=\"rtl\"><a href=\"https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95\" dir=\"rtl\">میوە</a> یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو</p>\n<blockquote dir=\"rtl\">\n<p dir=\"rtl\">میوە بۆ تەندروستی باشە\n-- نەزانراو</p>\n</blockquote>", "globalization+advanced+emojis");
TestParser.TestSpec("# Fruits\nIn botany, a [fruit](https://en.wikipedia.org/wiki/Fruit) is the seed-bearing structure in flowering plants (also known as angiosperms) formed from the ovary after flowering.\n\n> Fruits are good for health\n-- Anonymous\n\n# میوە\n[میوە](https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95) یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو\n\n> میوە بۆ تەندروستی باشە\n-- نەزانراو", "<h1 id=\"fruits\">Fruits</h1>\n<p>In botany, a <a href=\"https://en.wikipedia.org/wiki/Fruit\">fruit</a> is the seed-bearing structure in flowering plants (also known as angiosperms) formed from the ovary after flowering.</p>\n<blockquote>\n<p>Fruits are good for health\n-- Anonymous</p>\n</blockquote>\n<h1 id=\"section\" dir=\"rtl\">میوە</h1>\n<p dir=\"rtl\"><a href=\"https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95\" dir=\"rtl\">میوە</a> یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو</p>\n<blockquote dir=\"rtl\">\n<p dir=\"rtl\">میوە بۆ تەندروستی باشە\n-- نەزانراو</p>\n</blockquote>", "globalization+advanced+emojis", context: "Example 1\nSection Extensions / Globalization\n");
}
// Lists:
@@ -160,8 +159,7 @@ namespace Markdig.Tests.Specs.Globalization
// <li class="task-list-item"><input disabled="disabled" type="checkbox" /> هەنجیر</li>
// </ul>
Console.WriteLine("Example 2\nSection Extensions / Globalization\n");
TestParser.TestSpec("## Types of fruits\n- Berries\n - Strawberry\n - kiwifruit\n- Citrus\n - Orange\n - Lemon\n\n## Examples of fruits :yum:\n1. Apple\n2. Banana\n3. Orange\n\n## Grocery List\n- [X] 􏿽 Watermelon\n- [X] Apricot\n- [ ] Fig \n\n## نموونەی میوە :yum:\n1. ? سێو\n2. 5 مۆز \n3. 􏿽 پرتەقاڵ\n\n## جۆرەکانی میوە\n- توو\n - فڕاولە\n - کیوی\n- مزرەمەنی\n - پڕتەقاڵ\n - لیمۆ\n\n## لیستی کڕین\n- [X] شووتی\n- [X] قەیسی\n- [ ] هەنجیر", "<h2 id=\"types-of-fruits\">Types of fruits</h2>\n<ul>\n<li>Berries\n<ul>\n<li>Strawberry</li>\n<li>kiwifruit</li>\n</ul>\n</li>\n<li>Citrus\n<ul>\n<li>Orange</li>\n<li>Lemon</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"examples-of-fruits\">Examples of fruits 😋</h2>\n<ol>\n<li>Apple</li>\n<li>Banana</li>\n<li>Orange</li>\n</ol>\n<h2 id=\"grocery-list\">Grocery List</h2>\n<ul class=\"contains-task-list\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> 􏿽 Watermelon</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> Apricot</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> Fig</li>\n</ul>\n<h2 id=\"section\" dir=\"rtl\">نموونەی میوە 😋</h2>\n<ol dir=\"rtl\">\n<li>? سێو</li>\n<li>5 مۆز</li>\n<li>􏿽 پرتەقاڵ</li>\n</ol>\n<h2 id=\"section-1\" dir=\"rtl\">جۆرەکانی میوە</h2>\n<ul dir=\"rtl\">\n<li>توو\n<ul dir=\"rtl\">\n<li>فڕاولە</li>\n<li>کیوی</li>\n</ul>\n</li>\n<li>مزرەمەنی\n<ul dir=\"rtl\">\n<li>پڕتەقاڵ</li>\n<li>لیمۆ</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"section-2\" dir=\"rtl\">لیستی کڕین</h2>\n<ul class=\"contains-task-list\" dir=\"rtl\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> شووتی</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> قەیسی</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> هەنجیر</li>\n</ul>", "globalization+advanced+emojis");
TestParser.TestSpec("## Types of fruits\n- Berries\n - Strawberry\n - kiwifruit\n- Citrus\n - Orange\n - Lemon\n\n## Examples of fruits :yum:\n1. Apple\n2. Banana\n3. Orange\n\n## Grocery List\n- [X] 􏿽 Watermelon\n- [X] Apricot\n- [ ] Fig \n\n## نموونەی میوە :yum:\n1. ? سێو\n2. 5 مۆز \n3. 􏿽 پرتەقاڵ\n\n## جۆرەکانی میوە\n- توو\n - فڕاولە\n - کیوی\n- مزرەمەنی\n - پڕتەقاڵ\n - لیمۆ\n\n## لیستی کڕین\n- [X] شووتی\n- [X] قەیسی\n- [ ] هەنجیر", "<h2 id=\"types-of-fruits\">Types of fruits</h2>\n<ul>\n<li>Berries\n<ul>\n<li>Strawberry</li>\n<li>kiwifruit</li>\n</ul>\n</li>\n<li>Citrus\n<ul>\n<li>Orange</li>\n<li>Lemon</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"examples-of-fruits\">Examples of fruits 😋</h2>\n<ol>\n<li>Apple</li>\n<li>Banana</li>\n<li>Orange</li>\n</ol>\n<h2 id=\"grocery-list\">Grocery List</h2>\n<ul class=\"contains-task-list\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> 􏿽 Watermelon</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> Apricot</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> Fig</li>\n</ul>\n<h2 id=\"section\" dir=\"rtl\">نموونەی میوە 😋</h2>\n<ol dir=\"rtl\">\n<li>? سێو</li>\n<li>5 مۆز</li>\n<li>􏿽 پرتەقاڵ</li>\n</ol>\n<h2 id=\"section-1\" dir=\"rtl\">جۆرەکانی میوە</h2>\n<ul dir=\"rtl\">\n<li>توو\n<ul dir=\"rtl\">\n<li>فڕاولە</li>\n<li>کیوی</li>\n</ul>\n</li>\n<li>مزرەمەنی\n<ul dir=\"rtl\">\n<li>پڕتەقاڵ</li>\n<li>لیمۆ</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"section-2\" dir=\"rtl\">لیستی کڕین</h2>\n<ul class=\"contains-task-list\" dir=\"rtl\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> شووتی</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> قەیسی</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> هەنجیر</li>\n</ul>", "globalization+advanced+emojis", context: "Example 2\nSection Extensions / Globalization\n");
}
// Tables:
@@ -226,8 +224,29 @@ namespace Markdig.Tests.Specs.Globalization
// </tbody>
// </table>
Console.WriteLine("Example 3\nSection Extensions / Globalization\n");
TestParser.TestSpec("Nutrition |Apple | Oranges\n--|-- | --\nCalories|52|47\nSugar|10g|9g\n\n پێکهاتە |سێو | پڕتەقاڵ\n--|-- | --\nکالۆری|٥٢|٤٧\nشەکر| ١٠گ|٩گ", "<table>\n<thead>\n<tr>\n<th>Nutrition</th>\n<th>Apple</th>\n<th>Oranges</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Calories</td>\n<td>52</td>\n<td>47</td>\n</tr>\n<tr>\n<td>Sugar</td>\n<td>10g</td>\n<td>9g</td>\n</tr>\n</tbody>\n</table>\n<table dir=\"rtl\" align=\"right\">\n<thead>\n<tr>\n<th>پێکهاتە</th>\n<th>سێو</th>\n<th>پڕتەقاڵ</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>کالۆری</td>\n<td>٥٢</td>\n<td>٤٧</td>\n</tr>\n<tr>\n<td>شەکر</td>\n<td>١٠گ</td>\n<td>٩گ</td>\n</tr>\n</tbody>\n</table>", "globalization+advanced+emojis");
TestParser.TestSpec("Nutrition |Apple | Oranges\n--|-- | --\nCalories|52|47\nSugar|10g|9g\n\n پێکهاتە |سێو | پڕتەقاڵ\n--|-- | --\nکالۆری|٥٢|٤٧\nشەکر| ١٠گ|٩گ", "<table>\n<thead>\n<tr>\n<th>Nutrition</th>\n<th>Apple</th>\n<th>Oranges</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Calories</td>\n<td>52</td>\n<td>47</td>\n</tr>\n<tr>\n<td>Sugar</td>\n<td>10g</td>\n<td>9g</td>\n</tr>\n</tbody>\n</table>\n<table dir=\"rtl\" align=\"right\">\n<thead>\n<tr>\n<th>پێکهاتە</th>\n<th>سێو</th>\n<th>پڕتەقاڵ</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>کالۆری</td>\n<td>٥٢</td>\n<td>٤٧</td>\n</tr>\n<tr>\n<td>شەکر</td>\n<td>١٠گ</td>\n<td>٩گ</td>\n</tr>\n</tbody>\n</table>", "globalization+advanced+emojis", context: "Example 3\nSection Extensions / Globalization\n");
}
// But if text starts with LTR characters, no attributes are added.
[Test]
public void ExtensionsGlobalization_Example004()
{
// Example 4
// Section: Extensions / Globalization
//
// The following Markdown:
// Foo میوە
//
// میوە bar
//
// Baz میوە
//
// Should be rendered as:
// <p>Foo میوە</p>
// <p dir="rtl">میوە bar</p>
// <p>Baz میوە</p>
TestParser.TestSpec("Foo میوە\n\nمیوە bar\n\nBaz میوە", "<p>Foo میوە</p>\n<p dir=\"rtl\">میوە bar</p>\n<p>Baz میوە</p>", "globalization+advanced+emojis", context: "Example 4\nSection Extensions / Globalization\n");
}
}
}

View File

@@ -186,4 +186,18 @@ Sugar|10g|9g
</tr>
</tbody>
</table>
````````````````````````````````
But if text starts with LTR characters, no attributes are added.
```````````````````````````````` example
Foo میوە
میوە bar
Baz میوە
.
<p>Foo میوە</p>
<p dir="rtl">میوە bar</p>
<p>Baz میوە</p>
````````````````````````````````

View File

@@ -17,7 +17,8 @@ namespace Markdig.Tests.Specs.GridTables
//
// ## Grid Table
//
// A grid table allows to have multiple lines per cells and allows to span cells over multiple columns. The following shows a simple grid table
// A grid table allows having multiple lines per cells and allows spanning cells over multiple columns.
// The following shows a simple grid table:
//
// ```
// +---------+---------+
@@ -38,17 +39,20 @@ namespace Markdig.Tests.Specs.GridTables
// ```
//
// **Rule #1**
// The first line of a grid table must a **row separator**. It must start with the column separator character `+` used to separate columns in a row separator. Each column separator is:
// - starting by optional spaces
// - followed by an optional `:` to specify left align, followed by optional spaces
// - followed by a sequence of at least one `-` character, followed by optional spaces
// - followed by an optional `:` to specify right align (or center align if left align is also defined)
// - ending by optional spaces
// The first line of a grid table must be a **row separator**.
// It must start with the column separator character `+` used to separate columns in a row separator.
//
// The first row separator must be followed by a *regular row*. A regular row must start with the character `|` that is starting at the same position than the column separator `+` of the first row separator.
// Each column separator:
// - starts with optional spaces
// - followed by an optional `:` to specify left align, followed by optional spaces
// - followed by a sequence of one or more `-` characters, followed by optional spaces
// - followed by an optional `:` to specify right align (or center align if left align is also defined)
// - ends with optional spaces
//
// The first row separator must be followed by a *regular row*.
// A regular row must start with a `|` character starting at the same position as the column separator `+` of the first row separator.
//
// The following is a valid row separator
// The following is a valid row separator:
[Test]
public void ExtensionsGridTable_Example001()
{
@@ -71,11 +75,10 @@ namespace Markdig.Tests.Specs.GridTables
// </tbody>
// </table>
Console.WriteLine("Example 1\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---------+---------+\n| This is | a table |", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
TestParser.TestSpec("+---------+---------+\n| This is | a table |", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced", context: "Example 1\nSection Extensions / Grid Table\n");
}
// The following is not a valid row separator
// The following is not a valid row separator:
[Test]
public void ExtensionsGridTable_Example002()
{
@@ -90,12 +93,12 @@ namespace Markdig.Tests.Specs.GridTables
// <p>|-----xxx----+---------+
// | This is | not a table</p>
Console.WriteLine("Example 2\nSection Extensions / Grid Table\n");
TestParser.TestSpec("|-----xxx----+---------+\n| This is | not a table", "<p>|-----xxx----+---------+\n| This is | not a table</p>", "gridtables|advanced");
TestParser.TestSpec("|-----xxx----+---------+\n| This is | not a table", "<p>|-----xxx----+---------+\n| This is | not a table</p>", "gridtables|advanced", context: "Example 2\nSection Extensions / Grid Table\n");
}
// **Rule #2**
// A regular row can continue a previous regular row when column separator `|` are positioned at the same position than the previous line. If they are positioned at the same location, the column may span over multiple columns:
// A regular row can continue a previous regular row when the column separators `|` are positioned at the same position as those of the previous line.
// If they are positioned at the same location, the column may span over multiple columns:
[Test]
public void ExtensionsGridTable_Example003()
{
@@ -133,8 +136,7 @@ namespace Markdig.Tests.Specs.GridTables
// </tbody>
// </table>
Console.WriteLine("Example 3\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---------+---------+---------+\n| Col1 | Col2 | Col3 |\n| Col1a | Col2a | Col3a |\n| Col1b | Col3b |\n| Col1c |", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td>Col1\nCol1a</td>\n<td>Col2\nCol2a</td>\n<td>Col3\nCol3a</td>\n</tr>\n<tr>\n<td colspan=\"2\">Col1b</td>\n<td>Col3b</td>\n</tr>\n<tr>\n<td colspan=\"3\">Col1c</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
TestParser.TestSpec("+---------+---------+---------+\n| Col1 | Col2 | Col3 |\n| Col1a | Col2a | Col3a |\n| Col1b | Col3b |\n| Col1c |", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td>Col1\nCol1a</td>\n<td>Col2\nCol2a</td>\n<td>Col3\nCol3a</td>\n</tr>\n<tr>\n<td colspan=\"2\">Col1b</td>\n<td>Col3b</td>\n</tr>\n<tr>\n<td colspan=\"3\">Col1c</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced", context: "Example 3\nSection Extensions / Grid Table\n");
}
// A row header is separated using `+========+` instead of `+---------+`:
@@ -161,8 +163,7 @@ namespace Markdig.Tests.Specs.GridTables
// </thead>
// </table>
Console.WriteLine("Example 4\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---------+---------+\n| This is | a table |\n+=========+=========+", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<thead>\n<tr>\n<th>This is</th>\n<th>a table</th>\n</tr>\n</thead>\n</table>", "gridtables|advanced");
TestParser.TestSpec("+---------+---------+\n| This is | a table |\n+=========+=========+", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<thead>\n<tr>\n<th>This is</th>\n<th>a table</th>\n</tr>\n</thead>\n</table>", "gridtables|advanced", context: "Example 4\nSection Extensions / Grid Table\n");
}
// The last column separator `|` may be omitted:
@@ -188,19 +189,16 @@ namespace Markdig.Tests.Specs.GridTables
// </tbody>
// </table>
Console.WriteLine("Example 5\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---------+---------+\n| This is | a table with a longer text in the second column", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table with a longer text in the second column</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
TestParser.TestSpec("+---------+---------+\n| This is | a table with a longer text in the second column", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table with a longer text in the second column</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced", context: "Example 5\nSection Extensions / Grid Table\n");
}
// The respective width of the columns are calculated from the ratio between the total size of the first table row without counting the `+`: `+----+--------+----+` would be divided between:
// The respective widths of the columns are calculated from the ratio between the total size of the first table row without counting the `+`: `+----+--------+----+` would be divided between:
//
// Total size is : 16
// - `----` → 4 characters
// - `--------` → 8 characters
// - `----` → 4 characters
//
// - `----` -> 4
// - `--------` -> 8
// - `----` -> 4
//
// So the width would be 4/16 = 25%, 8/16 = 50%, 4/16 = 25%
// The total size is 16 characters, so the widths would be 4/16 = 25%, 8/16 = 50%, and 4/16 = 25%.
[Test]
public void ExtensionsGridTable_Example006()
{
@@ -226,8 +224,7 @@ namespace Markdig.Tests.Specs.GridTables
// </tbody>
// </table>
Console.WriteLine("Example 6\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+----+--------+----+\n| A | B C D | E |\n+----+--------+----+", "<table>\n<col style=\"width:25%\" />\n<col style=\"width:50%\" />\n<col style=\"width:25%\" />\n<tbody>\n<tr>\n<td>A</td>\n<td>B C D</td>\n<td>E</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
TestParser.TestSpec("+----+--------+----+\n| A | B C D | E |\n+----+--------+----+", "<table>\n<col style=\"width:25%\" />\n<col style=\"width:50%\" />\n<col style=\"width:25%\" />\n<tbody>\n<tr>\n<td>A</td>\n<td>B C D</td>\n<td>E</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced", context: "Example 6\nSection Extensions / Grid Table\n");
}
// Alignment might be specified on the first row using the character `:`:
@@ -256,8 +253,7 @@ namespace Markdig.Tests.Specs.GridTables
// </tbody>
// </table>
Console.WriteLine("Example 7\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+-----+:---:+-----+\n| A | B | C |\n+-----+-----+-----+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td>A</td>\n<td style=\"text-align: center;\">B</td>\n<td>C</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
TestParser.TestSpec("+-----+:---:+-----+\n| A | B | C |\n+-----+-----+-----+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td>A</td>\n<td style=\"text-align: center;\">B</td>\n<td>C</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced", context: "Example 7\nSection Extensions / Grid Table\n");
}
// A grid table may have cells spanning both columns and rows:
@@ -300,11 +296,10 @@ namespace Markdig.Tests.Specs.GridTables
// </tbody>
// </table>
Console.WriteLine("Example 8\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+---+---+ B +\n| D | E | B |\n+ D +---+---+\n| D | CCCCC |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td colspan=\"2\">AAAAA</td>\n<td rowspan=\"2\">B\nB\nB</td>\n</tr>\n<tr>\n<td rowspan=\"2\">D\nD\nD</td>\n<td>E</td>\n</tr>\n<tr>\n<td colspan=\"2\">CCCCC</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+---+---+ B +\n| D | E | B |\n+ D +---+---+\n| D | CCCCC |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td colspan=\"2\">AAAAA</td>\n<td rowspan=\"2\">B\nB\nB</td>\n</tr>\n<tr>\n<td rowspan=\"2\">D\nD\nD</td>\n<td>E</td>\n</tr>\n<tr>\n<td colspan=\"2\">CCCCC</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced", context: "Example 8\nSection Extensions / Grid Table\n");
}
// A grid table may have cells with both colspan and rowspan:
// A grid table may have cells with both `colspan` and `rowspan`:
[Test]
public void ExtensionsGridTable_Example009()
{
@@ -343,8 +338,7 @@ namespace Markdig.Tests.Specs.GridTables
// </tbody>
// </table>
Console.WriteLine("Example 9\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+ AAAAA +---+\n| AAAAA | C |\n+---+---+---+\n| D | E | F |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td colspan=\"2\" rowspan=\"2\">AAAAA\nAAAAA\nAAAAA</td>\n<td>B</td>\n</tr>\n<tr>\n<td>C</td>\n</tr>\n<tr>\n<td>D</td>\n<td>E</td>\n<td>F</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+ AAAAA +---+\n| AAAAA | C |\n+---+---+---+\n| D | E | F |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td colspan=\"2\" rowspan=\"2\">AAAAA\nAAAAA\nAAAAA</td>\n<td>B</td>\n</tr>\n<tr>\n<td>C</td>\n</tr>\n<tr>\n<td>D</td>\n<td>E</td>\n<td>F</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced", context: "Example 9\nSection Extensions / Grid Table\n");
}
// A grid table may not have irregularly shaped cells:
@@ -372,8 +366,7 @@ namespace Markdig.Tests.Specs.GridTables
// | DDDDD | E |
// +---+---+---+</p>
Console.WriteLine("Example 10\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+ A +---+ B +\n| A | C | B |\n+---+---+---+\n| DDDDD | E |\n+---+---+---+", "<p>+---+---+---+\n| AAAAA | B |\n+ A +---+ B +\n| A | C | B |\n+---+---+---+\n| DDDDD | E |\n+---+---+---+</p>", "gridtables|advanced");
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+ A +---+ B +\n| A | C | B |\n+---+---+---+\n| DDDDD | E |\n+---+---+---+", "<p>+---+---+---+\n| AAAAA | B |\n+ A +---+ B +\n| A | C | B |\n+---+---+---+\n| DDDDD | E |\n+---+---+---+</p>", "gridtables|advanced", context: "Example 10\nSection Extensions / Grid Table\n");
}
// An empty `+` on a line should result in a simple empty list output:
@@ -391,8 +384,36 @@ namespace Markdig.Tests.Specs.GridTables
// <li></li>
// </ul>
Console.WriteLine("Example 11\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+", "<ul>\n<li></li>\n</ul>", "gridtables|advanced");
TestParser.TestSpec("+", "<ul>\n<li></li>\n</ul>", "gridtables|advanced", context: "Example 11\nSection Extensions / Grid Table\n");
}
// A table may begin right after a paragraph without an empty line in between:
[Test]
public void ExtensionsGridTable_Example012()
{
// Example 12
// Section: Extensions / Grid Table
//
// The following Markdown:
// Some
// **text**.
// +---+
// | A |
// +---+
//
// Should be rendered as:
// <p>Some
// <strong>text</strong>.</p>
// <table>
// <col style="width:100%" />
// <tbody>
// <tr>
// <td>A</td>
// </tr>
// </tbody>
// </table>
TestParser.TestSpec("Some\n**text**.\n+---+\n| A |\n+---+", "<p>Some\n<strong>text</strong>.</p>\n<table>\n<col style=\"width:100%\" />\n<tbody>\n<tr>\n<td>A</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced", context: "Example 12\nSection Extensions / Grid Table\n");
}
}
}

View File

@@ -4,7 +4,8 @@ This section describes the different extensions supported:
## Grid Table
A grid table allows to have multiple lines per cells and allows to span cells over multiple columns. The following shows a simple grid table
A grid table allows having multiple lines per cells and allows spanning cells over multiple columns.
The following shows a simple grid table:
```
+---------+---------+
@@ -25,17 +26,20 @@ A grid table allows to have multiple lines per cells and allows to span cells ov
```
**Rule #1**
The first line of a grid table must a **row separator**. It must start with the column separator character `+` used to separate columns in a row separator. Each column separator is:
- starting by optional spaces
- followed by an optional `:` to specify left align, followed by optional spaces
- followed by a sequence of at least one `-` character, followed by optional spaces
- followed by an optional `:` to specify right align (or center align if left align is also defined)
- ending by optional spaces
The first line of a grid table must be a **row separator**.
It must start with the column separator character `+` used to separate columns in a row separator.
The first row separator must be followed by a *regular row*. A regular row must start with the character `|` that is starting at the same position than the column separator `+` of the first row separator.
Each column separator:
- starts with optional spaces
- followed by an optional `:` to specify left align, followed by optional spaces
- followed by a sequence of one or more `-` characters, followed by optional spaces
- followed by an optional `:` to specify right align (or center align if left align is also defined)
- ends with optional spaces
The first row separator must be followed by a *regular row*.
A regular row must start with a `|` character starting at the same position as the column separator `+` of the first row separator.
The following is a valid row separator
The following is a valid row separator:
```````````````````````````````` example
+---------+---------+
@@ -53,8 +57,8 @@ The following is a valid row separator
</table>
````````````````````````````````
The following is not a valid row separator:
The following is not a valid row separator
```````````````````````````````` example
|-----xxx----+---------+
| This is | not a table
@@ -64,7 +68,8 @@ The following is not a valid row separator
````````````````````````````````
**Rule #2**
A regular row can continue a previous regular row when column separator `|` are positioned at the same position than the previous line. If they are positioned at the same location, the column may span over multiple columns:
A regular row can continue a previous regular row when the column separators `|` are positioned at the same position as those of the previous line.
If they are positioned at the same location, the column may span over multiple columns:
```````````````````````````````` example
+---------+---------+---------+
@@ -134,15 +139,13 @@ The last column separator `|` may be omitted:
</table>
````````````````````````````````
The respective width of the columns are calculated from the ratio between the total size of the first table row without counting the `+`: `+----+--------+----+` would be divided between:
The respective widths of the columns are calculated from the ratio between the total size of the first table row without counting the `+`: `+----+--------+----+` would be divided between:
Total size is : 16
- `----` → 4 characters
- `--------` → 8 characters
- `----` → 4 characters
- `----` -> 4
- `--------` -> 8
- `----` -> 4
So the width would be 4/16 = 25%, 8/16 = 50%, 4/16 = 25%
The total size is 16 characters, so the widths would be 4/16 = 25%, 8/16 = 50%, and 4/16 = 25%.
```````````````````````````````` example
+----+--------+----+
@@ -165,7 +168,6 @@ So the width would be 4/16 = 25%, 8/16 = 50%, 4/16 = 25%
Alignment might be specified on the first row using the character `:`:
```````````````````````````````` example
+-----+:---:+-----+
| A | B | C |
@@ -220,7 +222,7 @@ D</td>
</table>
````````````````````````````````
A grid table may have cells with both colspan and rowspan:
A grid table may have cells with both `colspan` and `rowspan`:
```````````````````````````````` example
+---+---+---+
@@ -276,7 +278,6 @@ A grid table may not have irregularly shaped cells:
An empty `+` on a line should result in a simple empty list output:
```````````````````````````````` example
+
.
@@ -284,3 +285,24 @@ An empty `+` on a line should result in a simple empty list output:
<li></li>
</ul>
````````````````````````````````
A table may begin right after a paragraph without an empty line in between:
```````````````````````````````` example
Some
**text**.
+---+
| A |
+---+
.
<p>Some
<strong>text</strong>.</p>
<table>
<col style="width:100%" />
<tbody>
<tr>
<td>A</td>
</tr>
</tbody>
</table>
````````````````````````````````

View File

@@ -32,8 +32,7 @@ namespace Markdig.Tests.Specs.HardlineBreaks
// <p>This is a paragraph<br />
// with a break inside</p>
Console.WriteLine("Example 1\nSection Extensions / Hardline break\n");
TestParser.TestSpec("This is a paragraph\nwith a break inside", "<p>This is a paragraph<br />\nwith a break inside</p>", "hardlinebreak|advanced+hardlinebreak");
TestParser.TestSpec("This is a paragraph\nwith a break inside", "<p>This is a paragraph<br />\nwith a break inside</p>", "hardlinebreak|advanced+hardlinebreak", context: "Example 1\nSection Extensions / Hardline break\n");
}
}
}

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.
@@ -40,10 +40,9 @@ namespace Markdig.Tests.Specs.JiraLinks
// This is a ABCD-123 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/ABCD-123" target="_blank">ABCD-123</a> issue</p>
Console.WriteLine("Example 1\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 ABCD-123 issue", "<p>This is a <a href=\"http://your.company.abc/browse/ABCD-123\" target=\"_blank\">ABCD-123</a> issue</p>", "jiralinks", context: "Example 1\nSection Jira Links\n");
}
[Test]
@@ -53,13 +52,12 @@ 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", context: "Example 2\nSection Jira Links\n");
}
[Test]
@@ -69,16 +67,14 @@ 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", context: "Example 3\nSection Jira Links\n");
}
// These are also valid links with `(` and `)`:
[Test]
public void JiraLinks_Example004()
{
@@ -86,13 +82,12 @@ 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", context: "Example 4\nSection Jira Links\n");
}
[Test]
@@ -102,15 +97,15 @@ 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", context: "Example 5\nSection Jira Links\n");
}
// These are also valid links with `(` and `)`:
[Test]
public void JiraLinks_Example006()
{
@@ -118,16 +113,14 @@ 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", context: "Example 6\nSection Jira Links\n");
}
// These are not valid links:
[Test]
public void JiraLinks_Example007()
{
@@ -135,13 +128,12 @@ 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", context: "Example 7\nSection Jira Links\n");
}
[Test]
@@ -151,13 +143,12 @@ 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", context: "Example 8\nSection Jira Links\n");
}
[Test]
@@ -167,13 +158,88 @@ 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>
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", context: "Example 9\nSection Jira Links\n");
}
// 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>
TestParser.TestSpec("This is not aJIRA-123 issue", "<p>This is not aJIRA-123 issue</p>", "jiralinks", context: "Example 10\nSection Jira Links\n");
}
[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>
TestParser.TestSpec("This is not 4JIRA-123 issue", "<p>This is not 4JIRA-123 issue</p>", "jiralinks", context: "Example 11\nSection Jira Links\n");
}
[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>
TestParser.TestSpec("This is not JIRA-123a issue", "<p>This is not JIRA-123a issue</p>", "jiralinks", context: "Example 12\nSection Jira Links\n");
}
[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");
TestParser.TestSpec("This is not JIRA- issue", "<p>This is not JIRA- issue</p>", "jiralinks");
TestParser.TestSpec("This is not JIRA- issue", "<p>This is not JIRA- issue</p>", "jiralinks", context: "Example 13\nSection Jira Links\n");
}
[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>
TestParser.TestSpec("This is not JIR4- issue", "<p>This is not JIR4- issue</p>", "jiralinks", context: "Example 14\nSection Jira Links\n");
}
}
}

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.
@@ -21,19 +21,31 @@ The following are valid examples:
```````````````````````````````` example
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>
<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
.
<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/KIRA-1" target="_blank">KIRA-1</a> issue</p>
````````````````````````````````
```````````````````````````````` example
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>
<p>This is a <a href="http://your.company.abc/browse/Z-1" target="_blank">Z-1</a> issue</p>
````````````````````````````````
These are also valid links with `(` and `)`:
@@ -41,19 +53,25 @@ These are also valid links with `(` and `)`:
```````````````````````````````` example
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>
<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
.
<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/KIRA-1" target="_blank">KIRA-1</a>) issue</p>
````````````````````````````````
```````````````````````````````` example
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>
<p>This is a (<a href="http://your.company.abc/browse/Z-1" target="_blank">Z-1</a>) issue</p>
````````````````````````````````
These are not valid links:
@@ -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

@@ -36,8 +36,7 @@ namespace Markdig.Tests.Specs.ListExtras
// <li>Last item</li>
// </ol>
Console.WriteLine("Example 1\nSection Extensions / Ordered list with alpha letter\n");
TestParser.TestSpec("a. First item\nb. Second item\nc. Last item", "<ol type=\"a\">\n<li>First item</li>\n<li>Second item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced");
TestParser.TestSpec("a. First item\nb. Second item\nc. Last item", "<ol type=\"a\">\n<li>First item</li>\n<li>Second item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced", context: "Example 1\nSection Extensions / Ordered list with alpha letter\n");
}
// It works also for uppercase alpha:
@@ -59,8 +58,7 @@ namespace Markdig.Tests.Specs.ListExtras
// <li>Last item</li>
// </ol>
Console.WriteLine("Example 2\nSection Extensions / Ordered list with alpha letter\n");
TestParser.TestSpec("A. First item\nB. Second item\nC. Last item", "<ol type=\"A\">\n<li>First item</li>\n<li>Second item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced");
TestParser.TestSpec("A. First item\nB. Second item\nC. Last item", "<ol type=\"A\">\n<li>First item</li>\n<li>Second item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced", context: "Example 2\nSection Extensions / Ordered list with alpha letter\n");
}
// Like for numbered list, a list can start with a different letter
@@ -80,8 +78,7 @@ namespace Markdig.Tests.Specs.ListExtras
// <li>Second item</li>
// </ol>
Console.WriteLine("Example 3\nSection Extensions / Ordered list with alpha letter\n");
TestParser.TestSpec("b. First item\nc. Second item", "<ol type=\"a\" start=\"2\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "listextras|advanced");
TestParser.TestSpec("b. First item\nc. Second item", "<ol type=\"a\" start=\"2\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "listextras|advanced", context: "Example 3\nSection Extensions / Ordered list with alpha letter\n");
}
// A different type of list will break the existing list:
@@ -105,8 +102,7 @@ namespace Markdig.Tests.Specs.ListExtras
// <li>First item2</li>
// </ol>
Console.WriteLine("Example 4\nSection Extensions / Ordered list with alpha letter\n");
TestParser.TestSpec("a. First item1\nb. Second item\nA. First item2", "<ol type=\"a\">\n<li>First item1</li>\n<li>Second item</li>\n</ol>\n<ol type=\"A\">\n<li>First item2</li>\n</ol>", "listextras|advanced");
TestParser.TestSpec("a. First item1\nb. Second item\nA. First item2", "<ol type=\"a\">\n<li>First item1</li>\n<li>Second item</li>\n</ol>\n<ol type=\"A\">\n<li>First item2</li>\n</ol>", "listextras|advanced", context: "Example 4\nSection Extensions / Ordered list with alpha letter\n");
}
}
@@ -136,8 +132,7 @@ namespace Markdig.Tests.Specs.ListExtras
// <li>Last item</li>
// </ol>
Console.WriteLine("Example 5\nSection Extensions / Ordered list with roman letter\n");
TestParser.TestSpec("i. First item\nii. Second item\niii. Third item\niv. Last item", "<ol type=\"i\">\n<li>First item</li>\n<li>Second item</li>\n<li>Third item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced");
TestParser.TestSpec("i. First item\nii. Second item\niii. Third item\niv. Last item", "<ol type=\"i\">\n<li>First item</li>\n<li>Second item</li>\n<li>Third item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced", context: "Example 5\nSection Extensions / Ordered list with roman letter\n");
}
// It works also for uppercase alpha:
@@ -161,8 +156,7 @@ namespace Markdig.Tests.Specs.ListExtras
// <li>Last item</li>
// </ol>
Console.WriteLine("Example 6\nSection Extensions / Ordered list with roman letter\n");
TestParser.TestSpec("I. First item\nII. Second item\nIII. Third item\nIV. Last item", "<ol type=\"I\">\n<li>First item</li>\n<li>Second item</li>\n<li>Third item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced");
TestParser.TestSpec("I. First item\nII. Second item\nIII. Third item\nIV. Last item", "<ol type=\"I\">\n<li>First item</li>\n<li>Second item</li>\n<li>Third item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced", context: "Example 6\nSection Extensions / Ordered list with roman letter\n");
}
// Like for numbered list, a list can start with a different letter
@@ -182,8 +176,7 @@ namespace Markdig.Tests.Specs.ListExtras
// <li>Second item</li>
// </ol>
Console.WriteLine("Example 7\nSection Extensions / Ordered list with roman letter\n");
TestParser.TestSpec("ii. First item\niii. Second item", "<ol type=\"i\" start=\"2\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "listextras|advanced");
TestParser.TestSpec("ii. First item\niii. Second item", "<ol type=\"i\" start=\"2\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "listextras|advanced", context: "Example 7\nSection Extensions / Ordered list with roman letter\n");
}
// Lists can be restarted, specifying the start point.
@@ -209,8 +202,7 @@ namespace Markdig.Tests.Specs.ListExtras
// <li>Second item</li>
// </ol>
Console.WriteLine("Example 8\nSection Extensions / Ordered list with roman letter\n");
TestParser.TestSpec("1. First item\n\nSome text\n\n2. Second item", "<ol>\n<li>First item</li>\n</ol>\n<p>Some text</p>\n<ol start=\"2\">\n<li>Second item</li>\n</ol>", "listextras|advanced");
TestParser.TestSpec("1. First item\n\nSome text\n\n2. Second item", "<ol>\n<li>First item</li>\n</ol>\n<p>Some text</p>\n<ol start=\"2\">\n<li>Second item</li>\n</ol>", "listextras|advanced", context: "Example 8\nSection Extensions / Ordered list with roman letter\n");
}
}
}

View File

@@ -17,7 +17,7 @@ namespace Markdig.Tests.Specs.Math
//
// ## Math Inline
//
// Allows to define a mathematic block embraced by `$...$`
// Allows to define a mathematic inline block embraced by `$...$`
[Test]
public void ExtensionsMathInline_Example001()
{
@@ -25,13 +25,12 @@ namespace Markdig.Tests.Specs.Math
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $math block$
// This is a $math inline$
//
// Should be rendered as:
// <p>This is a <span class="math">\(math block\)</span></p>
// <p>This is a <span class="math">\(math inline\)</span></p>
Console.WriteLine("Example 1\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $math block$", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
TestParser.TestSpec("This is a $math inline$", "<p>This is a <span class=\"math\">\\(math inline\\)</span></p>", "mathematics|advanced", context: "Example 1\nSection Extensions / Math Inline\n");
}
// Or by `$$...$$` embracing it by:
@@ -42,13 +41,12 @@ namespace Markdig.Tests.Specs.Math
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $$math block$$
// This is a $$math inline$$
//
// Should be rendered as:
// <p>This is a <span class="math">\(math block\)</span></p>
// <p>This is a <span class="math">\(math inline\)</span></p>
Console.WriteLine("Example 2\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $$math block$$", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
TestParser.TestSpec("This is a $$math inline$$", "<p>This is a <span class=\"math\">\\(math inline\\)</span></p>", "mathematics|advanced", context: "Example 2\nSection Extensions / Math Inline\n");
}
// Newlines inside an inline math are not allowed:
@@ -60,14 +58,13 @@ namespace Markdig.Tests.Specs.Math
//
// The following Markdown:
// This is not a $$math
// block$$ and? this is a $$math block$$
// inline$$ and? this is a $$math inline$$
//
// Should be rendered as:
// <p>This is not a $$math
// block$$ and? this is a <span class="math">\(math block\)</span></p>
// inline$$ and? this is a <span class="math">\(math inline\)</span></p>
Console.WriteLine("Example 3\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is not a $$math \nblock$$ and? this is a $$math block$$", "<p>This is not a $$math\nblock$$ and? this is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
TestParser.TestSpec("This is not a $$math \ninline$$ and? this is a $$math inline$$", "<p>This is not a $$math\ninline$$ and? this is a <span class=\"math\">\\(math inline\\)</span></p>", "mathematics|advanced", context: "Example 3\nSection Extensions / Math Inline\n");
}
[Test]
@@ -78,14 +75,13 @@ namespace Markdig.Tests.Specs.Math
//
// The following Markdown:
// This is not a $math
// block$ and? this is a $math block$
// inline$ and? this is a $math inline$
//
// Should be rendered as:
// <p>This is not a $math
// block$ and? this is a <span class="math">\(math block\)</span></p>
// inline$ and? this is a <span class="math">\(math inline\)</span></p>
Console.WriteLine("Example 4\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is not a $math \nblock$ and? this is a $math block$", "<p>This is not a $math\nblock$ and? this is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
TestParser.TestSpec("This is not a $math \ninline$ and? this is a $math inline$", "<p>This is not a $math\ninline$ and? this is a <span class=\"math\">\\(math inline\\)</span></p>", "mathematics|advanced", context: "Example 4\nSection Extensions / Math Inline\n");
}
// An opening `$` can be followed by a space if the closing is also preceded by a space `$`:
@@ -96,13 +92,12 @@ namespace Markdig.Tests.Specs.Math
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $ math block $
// This is a $ math inline $
//
// Should be rendered as:
// <p>This is a <span class="math">\(math block\)</span></p>
// <p>This is a <span class="math">\(math inline\)</span></p>
Console.WriteLine("Example 5\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $ math block $", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
TestParser.TestSpec("This is a $ math inline $", "<p>This is a <span class=\"math\">\\(math inline\\)</span></p>", "mathematics|advanced", context: "Example 5\nSection Extensions / Math Inline\n");
}
[Test]
@@ -112,13 +107,12 @@ namespace Markdig.Tests.Specs.Math
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $ math block $ after
// This is a $ math inline $ after
//
// Should be rendered as:
// <p>This is a <span class="math">\(math block\)</span> after</p>
// <p>This is a <span class="math">\(math inline\)</span> after</p>
Console.WriteLine("Example 6\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $ math block $ after", "<p>This is a <span class=\"math\">\\(math block\\)</span> after</p>", "mathematics|advanced");
TestParser.TestSpec("This is a $ math inline $ after", "<p>This is a <span class=\"math\">\\(math inline\\)</span> after</p>", "mathematics|advanced", context: "Example 6\nSection Extensions / Math Inline\n");
}
[Test]
@@ -128,13 +122,12 @@ namespace Markdig.Tests.Specs.Math
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $$ math block $$ after
// This is a $$ math inline $$ after
//
// Should be rendered as:
// <p>This is a <span class="math">\(math block\)</span> after</p>
// <p>This is a <span class="math">\(math inline\)</span> after</p>
Console.WriteLine("Example 7\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $$ math block $$ after", "<p>This is a <span class=\"math\">\\(math block\\)</span> after</p>", "mathematics|advanced");
TestParser.TestSpec("This is a $$ math inline $$ after", "<p>This is a <span class=\"math\">\\(math inline\\)</span> after</p>", "mathematics|advanced", context: "Example 7\nSection Extensions / Math Inline\n");
}
[Test]
@@ -144,13 +137,12 @@ namespace Markdig.Tests.Specs.Math
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a not $ math block$ because there is not a whitespace before the closing
// This is a not $ math inline$ because there is not a whitespace before the closing
//
// Should be rendered as:
// <p>This is a not $ math block$ because there is not a whitespace before the closing</p>
// <p>This is a not $ math inline$ because there is not a whitespace before the closing</p>
Console.WriteLine("Example 8\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a not $ math block$ because there is not a whitespace before the closing", "<p>This is a not $ math block$ because there is not a whitespace before the closing</p>", "mathematics|advanced");
TestParser.TestSpec("This is a not $ math inline$ because there is not a whitespace before the closing", "<p>This is a not $ math inline$ because there is not a whitespace before the closing</p>", "mathematics|advanced", context: "Example 8\nSection Extensions / Math Inline\n");
}
// For the opening `$` it requires a space or a punctuation before (but cannot be used within a word):
@@ -161,13 +153,12 @@ namespace Markdig.Tests.Specs.Math
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is not a m$ath block$
// This is not a m$ath inline$
//
// Should be rendered as:
// <p>This is not a m$ath block$</p>
// <p>This is not a m$ath inline$</p>
Console.WriteLine("Example 9\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is not a m$ath block$", "<p>This is not a m$ath block$</p>", "mathematics|advanced");
TestParser.TestSpec("This is not a m$ath inline$", "<p>This is not a m$ath inline$</p>", "mathematics|advanced", context: "Example 9\nSection Extensions / Math Inline\n");
}
// For the closing `$` it requires a space after or a punctuation (but cannot be preceded by a space and cannot be used within a word):
@@ -178,13 +169,12 @@ namespace Markdig.Tests.Specs.Math
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is not a $math bloc$k
// This is not a $math inlin$e
//
// Should be rendered as:
// <p>This is not a $math bloc$k</p>
// <p>This is not a $math inlin$e</p>
Console.WriteLine("Example 10\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is not a $math bloc$k", "<p>This is not a $math bloc$k</p>", "mathematics|advanced");
TestParser.TestSpec("This is not a $math inlin$e", "<p>This is not a $math inlin$e</p>", "mathematics|advanced", context: "Example 10\nSection Extensions / Math Inline\n");
}
// For the closing `$` it requires a space after or a punctuation (but cannot be preceded by a space and cannot be used within a word):
@@ -200,8 +190,7 @@ namespace Markdig.Tests.Specs.Math
// Should be rendered as:
// <p>This is should not match a 16$ or a $15</p>
Console.WriteLine("Example 11\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is should not match a 16$ or a $15", "<p>This is should not match a 16$ or a $15</p>", "mathematics|advanced");
TestParser.TestSpec("This is should not match a 16$ or a $15", "<p>This is should not match a 16$ or a $15</p>", "mathematics|advanced", context: "Example 11\nSection Extensions / Math Inline\n");
}
// A `$` can be escaped between a math inline block by using the escape `\\`
@@ -212,13 +201,12 @@ namespace Markdig.Tests.Specs.Math
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $math \$ block$
// This is a $math \$ inline$
//
// Should be rendered as:
// <p>This is a <span class="math">\(math \$ block\)</span></p>
// <p>This is a <span class="math">\(math \$ inline\)</span></p>
Console.WriteLine("Example 12\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $math \\$ block$", "<p>This is a <span class=\"math\">\\(math \\$ block\\)</span></p>", "mathematics|advanced");
TestParser.TestSpec("This is a $math \\$ inline$", "<p>This is a <span class=\"math\">\\(math \\$ inline\\)</span></p>", "mathematics|advanced", context: "Example 12\nSection Extensions / Math Inline\n");
}
// At most, two `$` will be matched for the opening and closing:
@@ -229,13 +217,12 @@ namespace Markdig.Tests.Specs.Math
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $$$math block$$$
// This is a $$$math inline$$$
//
// Should be rendered as:
// <p>This is a <span class="math">\($math block$\)</span></p>
// <p>This is a <span class="math">\($math inline$\)</span></p>
Console.WriteLine("Example 13\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $$$math block$$$", "<p>This is a <span class=\"math\">\\($math block$\\)</span></p>", "mathematics|advanced");
TestParser.TestSpec("This is a $$$math inline$$$", "<p>This is a <span class=\"math\">\\($math inline$\\)</span></p>", "mathematics|advanced", context: "Example 13\nSection Extensions / Math Inline\n");
}
// Regular text can come both before and after the math inline
@@ -246,16 +233,15 @@ namespace Markdig.Tests.Specs.Math
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $math block$ with text on both sides.
// This is a $math inline$ with text on both sides.
//
// Should be rendered as:
// <p>This is a <span class="math">\(math block\)</span> with text on both sides.</p>
// <p>This is a <span class="math">\(math inline\)</span> with text on both sides.</p>
Console.WriteLine("Example 14\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $math block$ with text on both sides.", "<p>This is a <span class=\"math\">\\(math block\\)</span> with text on both sides.</p>", "mathematics|advanced");
TestParser.TestSpec("This is a $math inline$ with text on both sides.", "<p>This is a <span class=\"math\">\\(math inline\\)</span> with text on both sides.</p>", "mathematics|advanced", context: "Example 14\nSection Extensions / Math Inline\n");
}
// A mathematic block takes precedence over standard emphasis `*` `_`:
// A mathematic inline block takes precedence over standard emphasis `*` `_`:
[Test]
public void ExtensionsMathInline_Example015()
{
@@ -263,16 +249,15 @@ namespace Markdig.Tests.Specs.Math
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is *a $math* block$
// This is *a $math* inline$
//
// Should be rendered as:
// <p>This is *a <span class="math">\(math* block\)</span></p>
// <p>This is *a <span class="math">\(math* inline\)</span></p>
Console.WriteLine("Example 15\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is *a $math* block$", "<p>This is *a <span class=\"math\">\\(math* block\\)</span></p>", "mathematics|advanced");
TestParser.TestSpec("This is *a $math* inline$", "<p>This is *a <span class=\"math\">\\(math* inline\\)</span></p>", "mathematics|advanced", context: "Example 15\nSection Extensions / Math Inline\n");
}
// An opening $$ at the beginning of a line should not be interpreted as a Math block:
// An opening $$ at the beginning of a line should not be interpreted as a Math inline:
[Test]
public void ExtensionsMathInline_Example016()
{
@@ -285,8 +270,7 @@ namespace Markdig.Tests.Specs.Math
// Should be rendered as:
// <p><span class="math">\(math\)</span> starting at a line</p>
Console.WriteLine("Example 16\nSection Extensions / Math Inline\n");
TestParser.TestSpec("$$ math $$ starting at a line", "<p><span class=\"math\">\\(math\\)</span> starting at a line</p>", "mathematics|advanced");
TestParser.TestSpec("$$ math $$ starting at a line", "<p><span class=\"math\">\\(math\\)</span> starting at a line</p>", "mathematics|advanced", context: "Example 16\nSection Extensions / Math Inline\n");
}
}
@@ -295,7 +279,7 @@ namespace Markdig.Tests.Specs.Math
{
// ## Math Block
//
// The match block can spawn on multiple lines by having a $$ starting on a line.
// The math block can spawn on multiple lines by having a $$ starting on a line.
// It is working as a fenced code block.
[Test]
public void ExtensionsMathBlock_Example017()
@@ -320,8 +304,7 @@ namespace Markdig.Tests.Specs.Math
// \end{equation}
// \]</div>
Console.WriteLine("Example 17\nSection Extensions / Math Block\n");
TestParser.TestSpec("$$\n\\begin{equation}\n \\int_0^\\infty \\frac{x^3}{e^x-1}\\,dx = \\frac{\\pi^4}{15}\n \\label{eq:sample}\n\\end{equation}\n$$", "<div class=\"math\">\n\\[\n\\begin{equation}\n \\int_0^\\infty \\frac{x^3}{e^x-1}\\,dx = \\frac{\\pi^4}{15}\n \\label{eq:sample}\n\\end{equation}\n\\]</div>", "mathematics|advanced");
TestParser.TestSpec("$$\n\\begin{equation}\n \\int_0^\\infty \\frac{x^3}{e^x-1}\\,dx = \\frac{\\pi^4}{15}\n \\label{eq:sample}\n\\end{equation}\n$$", "<div class=\"math\">\n\\[\n\\begin{equation}\n \\int_0^\\infty \\frac{x^3}{e^x-1}\\,dx = \\frac{\\pi^4}{15}\n \\label{eq:sample}\n\\end{equation}\n\\]</div>", "mathematics|advanced", context: "Example 17\nSection Extensions / Math Block\n");
}
}
}

View File

@@ -4,79 +4,79 @@ Adds support for mathematics spans:
## Math Inline
Allows to define a mathematic block embraced by `$...$`
Allows to define a mathematic inline block embraced by `$...$`
```````````````````````````````` example
This is a $math block$
This is a $math inline$
.
<p>This is a <span class="math">\(math block\)</span></p>
<p>This is a <span class="math">\(math inline\)</span></p>
````````````````````````````````
Or by `$$...$$` embracing it by:
```````````````````````````````` example
This is a $$math block$$
This is a $$math inline$$
.
<p>This is a <span class="math">\(math block\)</span></p>
<p>This is a <span class="math">\(math inline\)</span></p>
````````````````````````````````
Newlines inside an inline math are not allowed:
```````````````````````````````` example
This is not a $$math
block$$ and? this is a $$math block$$
inline$$ and? this is a $$math inline$$
.
<p>This is not a $$math
block$$ and? this is a <span class="math">\(math block\)</span></p>
inline$$ and? this is a <span class="math">\(math inline\)</span></p>
````````````````````````````````
```````````````````````````````` example
This is not a $math
block$ and? this is a $math block$
inline$ and? this is a $math inline$
.
<p>This is not a $math
block$ and? this is a <span class="math">\(math block\)</span></p>
inline$ and? this is a <span class="math">\(math inline\)</span></p>
````````````````````````````````
An opening `$` can be followed by a space if the closing is also preceded by a space `$`:
```````````````````````````````` example
This is a $ math block $
This is a $ math inline $
.
<p>This is a <span class="math">\(math block\)</span></p>
<p>This is a <span class="math">\(math inline\)</span></p>
````````````````````````````````
```````````````````````````````` example
This is a $ math block $ after
This is a $ math inline $ after
.
<p>This is a <span class="math">\(math block\)</span> after</p>
<p>This is a <span class="math">\(math inline\)</span> after</p>
````````````````````````````````
```````````````````````````````` example
This is a $$ math block $$ after
This is a $$ math inline $$ after
.
<p>This is a <span class="math">\(math block\)</span> after</p>
<p>This is a <span class="math">\(math inline\)</span> after</p>
````````````````````````````````
```````````````````````````````` example
This is a not $ math block$ because there is not a whitespace before the closing
This is a not $ math inline$ because there is not a whitespace before the closing
.
<p>This is a not $ math block$ because there is not a whitespace before the closing</p>
<p>This is a not $ math inline$ because there is not a whitespace before the closing</p>
````````````````````````````````
For the opening `$` it requires a space or a punctuation before (but cannot be used within a word):
```````````````````````````````` example
This is not a m$ath block$
This is not a m$ath inline$
.
<p>This is not a m$ath block$</p>
<p>This is not a m$ath inline$</p>
````````````````````````````````
For the closing `$` it requires a space after or a punctuation (but cannot be preceded by a space and cannot be used within a word):
```````````````````````````````` example
This is not a $math bloc$k
This is not a $math inlin$e
.
<p>This is not a $math bloc$k</p>
<p>This is not a $math inlin$e</p>
````````````````````````````````
For the closing `$` it requires a space after or a punctuation (but cannot be preceded by a space and cannot be used within a word):
@@ -90,34 +90,34 @@ This is should not match a 16$ or a $15
A `$` can be escaped between a math inline block by using the escape `\\`
```````````````````````````````` example
This is a $math \$ block$
This is a $math \$ inline$
.
<p>This is a <span class="math">\(math \$ block\)</span></p>
<p>This is a <span class="math">\(math \$ inline\)</span></p>
````````````````````````````````
At most, two `$` will be matched for the opening and closing:
```````````````````````````````` example
This is a $$$math block$$$
This is a $$$math inline$$$
.
<p>This is a <span class="math">\($math block$\)</span></p>
<p>This is a <span class="math">\($math inline$\)</span></p>
````````````````````````````````
Regular text can come both before and after the math inline
```````````````````````````````` example
This is a $math block$ with text on both sides.
This is a $math inline$ with text on both sides.
.
<p>This is a <span class="math">\(math block\)</span> with text on both sides.</p>
<p>This is a <span class="math">\(math inline\)</span> with text on both sides.</p>
````````````````````````````````
A mathematic block takes precedence over standard emphasis `*` `_`:
A mathematic inline block takes precedence over standard emphasis `*` `_`:
```````````````````````````````` example
This is *a $math* block$
This is *a $math* inline$
.
<p>This is *a <span class="math">\(math* block\)</span></p>
<p>This is *a <span class="math">\(math* inline\)</span></p>
````````````````````````````````
An opening $$ at the beginning of a line should not be interpreted as a Math block:
An opening $$ at the beginning of a line should not be interpreted as a Math inline:
```````````````````````````````` example
$$ math $$ starting at a line
@@ -127,7 +127,7 @@ $$ math $$ starting at a line
## Math Block
The match block can spawn on multiple lines by having a $$ starting on a line.
The math block can spawn on multiple lines by having a $$ starting on a line.
It is working as a fenced code block.
```````````````````````````````` example

View File

@@ -57,8 +57,7 @@ namespace Markdig.Tests.Specs.Media
// <p><iframe src="https://music.yandex.ru/iframe/#track/4402274/411845/" class="yandex" width="500" height="281" frameborder="0"></iframe></p>
// <p><iframe src="https://ok.ru/videoembed/26870090463" class="odnoklassniki" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
Console.WriteLine("Example 1\nSection Extensions / Media links\n");
TestParser.TestSpec("![youtube.com](https://www.youtube.com/watch?v=mswPy5bt3TQ)\n\n![youtube.com with t](https://www.youtube.com/watch?v=mswPy5bt3TQ&t=100)\n\n![youtu.be](https://youtu.be/mswPy5bt3TQ)\n\n![youtu.be with t](https://youtu.be/mswPy5bt3TQ?t=100)\n\n![youtube.com/embed 1](https://www.youtube.com/embed/mswPy5bt3TQ?start=100&rel=0)\n\n![youtube.com/embed 2](https://www.youtube.com/embed?listType=playlist&list=PLC77007E23FF423C6)\n\n![vimeo](https://vimeo.com/8607834)\n\n![static mp4](https://sample.com/video.mp4)\n\n![yandex.ru](https://music.yandex.ru/album/411845/track/4402274)\n\n![ok.ru](https://ok.ru/video/26870090463)", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100&amp;rel=0\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed?listType=playlist&amp;list=PLC77007E23FF423C6\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://player.vimeo.com/video/8607834\" class=\"vimeo\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"https://sample.com/video.mp4\"></source></video></p>\n<p><iframe src=\"https://music.yandex.ru/iframe/#track/4402274/411845/\" class=\"yandex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n<p><iframe src=\"https://ok.ru/videoembed/26870090463\" class=\"odnoklassniki\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>", "medialinks|advanced+medialinks");
TestParser.TestSpec("![youtube.com](https://www.youtube.com/watch?v=mswPy5bt3TQ)\n\n![youtube.com with t](https://www.youtube.com/watch?v=mswPy5bt3TQ&t=100)\n\n![youtu.be](https://youtu.be/mswPy5bt3TQ)\n\n![youtu.be with t](https://youtu.be/mswPy5bt3TQ?t=100)\n\n![youtube.com/embed 1](https://www.youtube.com/embed/mswPy5bt3TQ?start=100&rel=0)\n\n![youtube.com/embed 2](https://www.youtube.com/embed?listType=playlist&list=PLC77007E23FF423C6)\n\n![vimeo](https://vimeo.com/8607834)\n\n![static mp4](https://sample.com/video.mp4)\n\n![yandex.ru](https://music.yandex.ru/album/411845/track/4402274)\n\n![ok.ru](https://ok.ru/video/26870090463)", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100&amp;rel=0\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed?listType=playlist&amp;list=PLC77007E23FF423C6\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://player.vimeo.com/video/8607834\" class=\"vimeo\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"https://sample.com/video.mp4\"></source></video></p>\n<p><iframe src=\"https://music.yandex.ru/iframe/#track/4402274/411845/\" class=\"yandex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n<p><iframe src=\"https://ok.ru/videoembed/26870090463\" class=\"odnoklassniki\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>", "medialinks|advanced+medialinks", context: "Example 1\nSection Extensions / Media links\n");
}
}
}

View File

@@ -30,8 +30,7 @@ namespace Markdig.Tests.Specs.NoHtml
// Should be rendered as:
// <p>this is some text&lt;/td&gt;&lt;/tr&gt;</p>
Console.WriteLine("Example 1\nSection Extensions / NoHTML\n");
TestParser.TestSpec("this is some text</td></tr>", "<p>this is some text&lt;/td&gt;&lt;/tr&gt;</p>", "nohtml");
TestParser.TestSpec("this is some text</td></tr>", "<p>this is some text&lt;/td&gt;&lt;/tr&gt;</p>", "nohtml", context: "Example 1\nSection Extensions / NoHTML\n");
}
// For Block HTML:
@@ -51,8 +50,7 @@ namespace Markdig.Tests.Specs.NoHtml
// this is some text
// &lt;/div&gt;</p>
Console.WriteLine("Example 2\nSection Extensions / NoHTML\n");
TestParser.TestSpec("<div>\nthis is some text\n</div>", "<p>&lt;div&gt;\nthis is some text\n&lt;/div&gt;</p>", "nohtml");
TestParser.TestSpec("<div>\nthis is some text\n</div>", "<p>&lt;div&gt;\nthis is some text\n&lt;/div&gt;</p>", "nohtml", context: "Example 2\nSection Extensions / NoHTML\n");
}
}
}

View File

@@ -58,8 +58,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 1\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a | b\n-- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("a | b\n-- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 1\nSection Extensions / Gfm Pipe Table\n");
}
// The following is also considered as a table, even if the second line starts like a list:
@@ -90,8 +89,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 2\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a | b\n- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("a | b\n- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 2\nSection Extensions / Gfm Pipe Table\n");
}
// A pipe table with only one header row is allowed:
@@ -115,8 +113,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </thead>
// </table>
Console.WriteLine("Example 3\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "gfm-pipetables");
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "gfm-pipetables", context: "Example 3\nSection Extensions / Gfm Pipe Table\n");
}
// After a row separator header, they will be interpreted as plain column:
@@ -147,8 +144,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 4\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a | b\n-- | --\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>--</td>\n<td>--</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("a | b\n-- | --\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>--</td>\n<td>--</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 4\nSection Extensions / Gfm Pipe Table\n");
}
// But if a table doesn't start with a column delimiter, it is not interpreted as a table, even if following lines have a column delimiter
@@ -168,8 +164,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// c | d
// e | f</p>
Console.WriteLine("Example 5\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a b\nc | d\ne | f", "<p>a b\nc | d\ne | f</p>", "gfm-pipetables");
TestParser.TestSpec("a b\nc | d\ne | f", "<p>a b\nc | d\ne | f</p>", "gfm-pipetables", context: "Example 5\nSection Extensions / Gfm Pipe Table\n");
}
// If a line doesn't have a column delimiter `|` the table is not detected
@@ -187,8 +182,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// <p>a | b
// c no d</p>
Console.WriteLine("Example 6\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a | b\nc no d", "<p>a | b\nc no d</p>", "gfm-pipetables");
TestParser.TestSpec("a | b\nc no d", "<p>a | b\nc no d</p>", "gfm-pipetables", context: "Example 6\nSection Extensions / Gfm Pipe Table\n");
}
// If a row contains more columns than the header row, the extra columns will be ignored:
@@ -229,8 +223,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 7\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a | b \n-- | --\n0 | 1 | 2\n3 | 4\n5 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>3</td>\n<td>4</td>\n</tr>\n<tr>\n<td>5</td>\n<td></td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("a | b \n-- | --\n0 | 1 | 2\n3 | 4\n5 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>3</td>\n<td>4</td>\n</tr>\n<tr>\n<td>5</td>\n<td></td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 7\nSection Extensions / Gfm Pipe Table\n");
}
// **Rule #2**
@@ -265,8 +258,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 8\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a | b |\n-- | --\n0 | 1 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("a | b |\n-- | --\n0 | 1 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 8\nSection Extensions / Gfm Pipe Table\n");
}
// **Rule #4**
@@ -308,8 +300,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 9\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec(" a | b |\n-- | --\n| 0 | 1\n| 2 | 3 |\n 4 | 5 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n<tr>\n<td>4</td>\n<td>5</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec(" a | b |\n-- | --\n| 0 | 1\n| 2 | 3 |\n 4 | 5 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n<tr>\n<td>4</td>\n<td>5</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 9\nSection Extensions / Gfm Pipe Table\n");
}
// A pipe may be present at both the beginning/ending of each line:
@@ -340,8 +331,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 10\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("|a|b|\n|-|-|\n|0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("|a|b|\n|-|-|\n|0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 10\nSection Extensions / Gfm Pipe Table\n");
}
// Or may be omitted on one side:
@@ -372,8 +362,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 11\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a|b|\n-|-|\n0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("a|b|\n-|-|\n0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 11\nSection Extensions / Gfm Pipe Table\n");
}
[Test]
@@ -403,8 +392,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 12\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("|a|b\n|-|-\n|0|1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("|a|b\n|-|-\n|0|1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 12\nSection Extensions / Gfm Pipe Table\n");
}
// Single column table can be declared with lines starting only by a column delimiter:
@@ -437,8 +425,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 13\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("| a\n| --\n| b\n| c ", "<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>b</td>\n</tr>\n<tr>\n<td>c</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("| a\n| --\n| b\n| c ", "<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>b</td>\n</tr>\n<tr>\n<td>c</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 13\nSection Extensions / Gfm Pipe Table\n");
}
// **Rule #5**
@@ -482,8 +469,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 14\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec(" a | b \n-------|-------\n 0 | 1 \n 2 | 3 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec(" a | b \n-------|-------\n 0 | 1 \n 2 | 3 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 14\nSection Extensions / Gfm Pipe Table\n");
}
// The text alignment is defined by default to be center for header and left for cells. If the left alignment is applied, it will force the column heading to be left aligned.
@@ -524,8 +510,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 15\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec(" a | b | c \n:------|:-------:| ----:\n 0 | 1 | 2 \n 3 | 4 | 5 ", "<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">a</th>\n<th style=\"text-align: center;\">b</th>\n<th style=\"text-align: right;\">c</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\">0</td>\n<td style=\"text-align: center;\">1</td>\n<td style=\"text-align: right;\">2</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">3</td>\n<td style=\"text-align: center;\">4</td>\n<td style=\"text-align: right;\">5</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec(" a | b | c \n:------|:-------:| ----:\n 0 | 1 | 2 \n 3 | 4 | 5 ", "<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">a</th>\n<th style=\"text-align: center;\">b</th>\n<th style=\"text-align: right;\">c</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\">0</td>\n<td style=\"text-align: center;\">1</td>\n<td style=\"text-align: right;\">2</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">3</td>\n<td style=\"text-align: center;\">4</td>\n<td style=\"text-align: right;\">5</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 15\nSection Extensions / Gfm Pipe Table\n");
}
// Test alignment with starting and ending pipes:
@@ -558,8 +543,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 16\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("| abc | def | ghi |\n|:---:|-----|----:|\n| 1 | 2 | 3 |", "<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">abc</th>\n<th>def</th>\n<th style=\"text-align: right;\">ghi</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\">1</td>\n<td>2</td>\n<td style=\"text-align: right;\">3</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("| abc | def | ghi |\n|:---:|-----|----:|\n| 1 | 2 | 3 |", "<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">abc</th>\n<th>def</th>\n<th style=\"text-align: right;\">ghi</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\">1</td>\n<td>2</td>\n<td style=\"text-align: right;\">3</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 16\nSection Extensions / Gfm Pipe Table\n");
}
// The following example shows a non matching header column separator:
@@ -581,8 +565,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// 0 | 1
// 2 | 3</p>
Console.WriteLine("Example 17\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec(" a | b\n-------|---x---\n 0 | 1\n 2 | 3 ", "<p>a | b\n-------|---x---\n0 | 1\n2 | 3</p> ", "gfm-pipetables");
TestParser.TestSpec(" a | b\n-------|---x---\n 0 | 1\n 2 | 3 ", "<p>a | b\n-------|---x---\n0 | 1\n2 | 3</p> ", "gfm-pipetables", context: "Example 17\nSection Extensions / Gfm Pipe Table\n");
}
// **Rule #6**
@@ -620,8 +603,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 18\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec(" *a* | b\n----- |-----\n 0 | _1_\n _2 | 3* ", "<table>\n<thead>\n<tr>\n<th><em>a</em></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td><em>1</em></td>\n</tr>\n<tr>\n<td>_2</td>\n<td>3*</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec(" *a* | b\n----- |-----\n 0 | _1_\n _2 | 3* ", "<table>\n<thead>\n<tr>\n<th><em>a</em></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td><em>1</em></td>\n</tr>\n<tr>\n<td>_2</td>\n<td>3*</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 18\nSection Extensions / Gfm Pipe Table\n");
}
// **Rule #7**
@@ -640,8 +622,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// Should be rendered as:
// <p>a | b <code>0 |</code></p>
Console.WriteLine("Example 19\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a | b `\n0 | ` ", "<p>a | b <code>0 |</code></p> ", "gfm-pipetables");
TestParser.TestSpec("a | b `\n0 | ` ", "<p>a | b <code>0 |</code></p> ", "gfm-pipetables", context: "Example 19\nSection Extensions / Gfm Pipe Table\n");
}
// **Rule #8**
@@ -674,8 +655,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 20\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a <a href=\"\" title=\"|\"></a> | b\n-- | --\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a <a href=\"\" title=\"|\"></a></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("a <a href=\"\" title=\"|\"></a> | b\n-- | --\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a <a href=\"\" title=\"|\"></a></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 20\nSection Extensions / Gfm Pipe Table\n");
}
// **Rule #9**
@@ -708,8 +688,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 21\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a | b\n-- | --\n[This is a link with a | inside the label](http://google.com) | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"http://google.com\">This is a link with a | inside the label</a></td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("a | b\n-- | --\n[This is a link with a | inside the label](http://google.com) | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"http://google.com\">This is a link with a | inside the label</a></td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 21\nSection Extensions / Gfm Pipe Table\n");
}
// **Rule #10**
@@ -735,8 +714,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </thead>
// </table>
Console.WriteLine("Example 22\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "gfm-pipetables");
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "gfm-pipetables", context: "Example 22\nSection Extensions / Gfm Pipe Table\n");
}
[Test]
@@ -760,8 +738,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </thead>
// </table>
Console.WriteLine("Example 23\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("|a|b|c\n|---|---|---|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n<th>c</th>\n</tr>\n</thead>\n</table>", "gfm-pipetables");
TestParser.TestSpec("|a|b|c\n|---|---|---|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n<th>c</th>\n</tr>\n</thead>\n</table>", "gfm-pipetables", context: "Example 23\nSection Extensions / Gfm Pipe Table\n");
}
// **Tests**
@@ -809,8 +786,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 24\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("| abc | def | \n|---|---|\n| cde| ddd| \n| eee| fff|\n| fff | fffff | \n|gggg | ffff | ", "<table>\n<thead>\n<tr>\n<th>abc</th>\n<th>def</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>cde</td>\n<td>ddd</td>\n</tr>\n<tr>\n<td>eee</td>\n<td>fff</td>\n</tr>\n<tr>\n<td>fff</td>\n<td>fffff</td>\n</tr>\n<tr>\n<td>gggg</td>\n<td>ffff</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("| abc | def | \n|---|---|\n| cde| ddd| \n| eee| fff|\n| fff | fffff | \n|gggg | ffff | ", "<table>\n<thead>\n<tr>\n<th>abc</th>\n<th>def</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>cde</td>\n<td>ddd</td>\n</tr>\n<tr>\n<td>eee</td>\n<td>fff</td>\n</tr>\n<tr>\n<td>fff</td>\n<td>fffff</td>\n</tr>\n<tr>\n<td>gggg</td>\n<td>ffff</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 24\nSection Extensions / Gfm Pipe Table\n");
}
// **Normalized columns count**
@@ -849,8 +825,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
// </tbody>
// </table>
Console.WriteLine("Example 25\nSection Extensions / Gfm Pipe Table\n");
TestParser.TestSpec("a | b\n-- | - \n0 | 1 | 2\n3 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>3</td>\n<td></td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
TestParser.TestSpec("a | b\n-- | - \n0 | 1 | 2\n3 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>3</td>\n<td></td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 25\nSection Extensions / Gfm Pipe Table\n");
}
}
}

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