Compare commits

...

402 Commits

Author SHA1 Message Date
Alexandre Mutel
cc47d33c7d Bump version to 0.15.5 2018-12-11 13:40:22 +01:00
Alexandre Mutel
fedbe2adc5 Merge pull request #267 from redcrayonn/patch-1
AutoIdentifierOptions clarification
2018-12-11 13:34:02 +01:00
Alexandre Mutel
8efc082d1d Update typo in AutoIdentifierOptions 2018-12-04 10:02:56 +01:00
Alexandre Mutel
6c2702f9fd Merge pull request #270 from neilha/master
fixes #196 ensuring line breaks when renderer does not have html enabled
2018-11-22 15:46:33 +01:00
Neil Harrold
4824f460c0 fixes #196 ensuring line breaks when renderer does not have html enabled 2018-11-21 09:03:24 +00:00
Olaf de Mol
46fc840c35 AutoIdentifierOptions clarification
Clarified 'None' option in the options for the auto-identifiers extension.
2018-11-09 15:22:02 +01:00
Alexandre Mutel
ce3078dcf2 Merge pull request #264 from MihaZupan/autolink-prev-chars
Make AutoLink ValidPreviousCharacters configurable
2018-10-29 07:29:43 -07:00
Miha Zupan
b8255a87cf Make AutoLink ValidPreviousCharacters configurable 2018-10-29 15:10:11 +01:00
Alexandre Mutel
4f6ad47c6a Merge pull request #255 from dampee/patch-1
having three dots while it has to be three dashes
2018-10-27 07:27:49 -07:00
Alexandre Mutel
d6d6f9c5c1 Merge pull request #256 from SyntaxC4/patch-1
Fixed spelling mistake
2018-10-27 07:27:28 -07:00
Alexandre Mutel
92b06f0954 Merge pull request #260 from MihaZupan/fix-autolinks
Allow #, ? and : instead of only / as the domain separator for AutoLinks
2018-10-25 12:54:57 -07:00
Miha Zupan
ecd625d40b Test for links without a slash after the domain 2018-10-25 16:56:33 +02:00
Miha Zupan
7e81747662 AutoLink Match links without slash after domain 2018-10-25 16:54:51 +02:00
Cory Fowler
cffe5b2f4b Update FootnotesSpecs.md 2018-10-10 17:03:38 -07:00
Damiaan
c349ead32c having three dots while it has to be three dashes 2018-10-09 11:44:53 +02:00
Alexandre Mutel
07e6a13378 Merge pull request #254 from MihaZupan/issue_251
Empty image alt text for link reference definitions
2018-10-07 20:57:38 +02:00
Miha Zupan
1b54bb157c Added image alt text tests 2018-10-07 19:24:24 +02:00
Miha Zupan
e23fae26c8 Empty image alt text for link reference definitions 2018-10-07 16:27:50 +02:00
Alexandre Mutel
2501c6aaf6 Bump to 0.15.4 2018-10-07 09:23:53 +02:00
Alexandre Mutel
e390a870cc Merge branch 'pr/n253_MihaZupan' 2018-10-06 18:04:35 +02:00
Miha Zupan
e4892fc86d Update Specs.cs 2018-10-06 18:03:39 +02:00
Miha Zupan
dfc67021ee Add autolink domain validation tests 2018-10-06 17:29:14 +02:00
Miha Zupan
25db2774c3 Add autolink domain validation 2018-10-06 17:28:53 +02:00
Alexandre Mutel
22271db431 Bump to 0.15.3 2018-09-26 10:34:54 +02:00
Alexandre Mutel
446764873d Merge pull request #247 from MihaZupan/master
Expose line start indexes
2018-09-26 10:17:19 +02:00
Miha Zupan
bdb5119806 Update changelog 2018-09-25 01:03:06 +02:00
Miha Zupan
f3bb06531e Expose line start indexes 2018-09-25 00:56:36 +02:00
Alexandre Mutel
9a74f3708e Merge pull request #245 from dampee/patch-1
small typo
2018-09-18 13:58:27 +02:00
Damiaan
57033311dc small typo 2018-09-18 13:53:15 +02:00
Alexandre Mutel
bda2f8c9ad Merge pull request #244 from MihaZupan/list-item-order
Expose list item order
2018-09-17 20:13:20 +02:00
Miha Zupan
0d5d3ddb2a Expose List item order 2018-09-17 16:19:01 +02:00
Alexandre Mutel
3f8e50031b Update changelog 2018-09-15 20:58:49 +02:00
Alexandre Mutel
949d5c0c25 Merge pull request #243 from MihaZupan/all-link-definitions
Fix Link and Footnote reference definitions in syntax tree
2018-09-15 20:52:50 +02:00
Miha Zupan
31e163f9f7 Fix offset calculation 2018-09-15 15:58:35 +02:00
Miha Zupan
b1440ab788 Add a LinkReferenceDefinition source position test 2018-09-15 15:27:38 +02:00
Miha Zupan
6e55d84f01 Add a FootnoteDefinition source position test 2018-09-15 15:27:05 +02:00
Miha Zupan
595dbb9af4 Fix FootnoteLinkReferenceDefinition content and source positions 2018-09-15 15:25:33 +02:00
Miha Zupan
d1576fd2ee Fix LinkReferenceDefinition source positions 2018-09-15 15:25:16 +02:00
Alexandre Mutel
1fe05639c9 Merge pull request #241 from MihaZupan/document-lines
Expose the number of lines in a document
2018-09-04 12:01:16 +02:00
Miha Zupan
8afff09b3e Expose the number of lines in a document 2018-08-29 18:56:36 +02:00
Alexandre Mutel
694747471a Merge pull request #239 from encrypt0r/master
Globalization Extension
2018-08-28 16:06:57 +02:00
Muhammad Azeez
dfcd5cddfa Fix the specs to make sure that the requried extensions are added in the tests 2018-08-28 16:30:35 +03:00
Muhammad Azeez
a99b7f4572 update globalization specs, docs and tests 2018-08-28 16:18:25 +03:00
Muhammad Azeez
7d7598db54 fix copyright notices 2018-08-28 15:58:12 +03:00
Muhammad Azeez
8767a1d8ed take surrogate characters into account 2018-08-28 15:33:20 +03:00
Muhammad Azeez
16c0829d9e add globalization spec 2018-08-27 00:48:22 +03:00
Muhammad Azeez
fc5d832432 Add globalization case for Configure extension method 2018-08-27 00:34:50 +03:00
Muhammad Azeez
7292acc27a make sure TaskList's X doesn't affect whether a block is marked as RTL or not 2018-08-27 00:32:08 +03:00
Muhammad Azeez
d05bc1572d only take strong characters into consideration when deciding whether a block should be rtl or not 2018-08-27 00:30:47 +03:00
Muhammad Azeez
5aa138425f add globalization extension 2018-08-25 23:11:20 +03:00
Alexandre Mutel
c56041811b Bump to 0.15.2 2018-08-21 11:04:45 +02:00
Alexandre Mutel
790bff3baf Fix footnotes parsing when they are defined after a container that has been closed in the meantime (#223) 2018-08-21 11:01:07 +02:00
Alexandre Mutel
571e04fe28 Bump to 0.15.1 2018-07-10 11:02:38 +02:00
Alexandre Mutel
c847996146 Fix CRLF 2018-07-10 11:02:16 +02:00
Alexandre Mutel
2782d3b4c3 Remove usage of IDictionary 2018-07-10 10:31:34 +02:00
Alexandre Mutel
582a76f8f0 Change editorconfig to not force crlf 2018-07-10 10:31:34 +02:00
Alexandre Mutel
030d676497 Merge pull request #235 from yufeih/autolink
Make AutoIdentifierExtension thread safe
2018-07-10 10:09:17 +02:00
Yufei Huang
0794213eef Make AutoIdentifierExtension thread safe 2018-07-10 11:52:32 +08:00
Alexandre Mutel
427dc849f7 Merge pull request #234 from ForNeVeR/patch-1
Fix spelling in package description
2018-06-28 17:16:43 +02:00
Friedrich von Never
e598047964 Fix spelling in package description 2018-06-28 22:07:53 +07:00
Alexandre Mutel
15546732dd Merge pull request #228 from carbon/master
Cross target .NETSTANDARD 2.0
2018-05-22 18:40:29 +02:00
Jason Nelson
7bd238852d Fix formatting 2018-05-22 08:59:35 -07:00
Jason Nelson
0f406039a0 Enable FIXED_STRING & UNSAFE code for .NETSTANDARD 2.0 target 2018-05-22 08:55:03 -07:00
Jason Nelson
1cd9cdfca0 Cross target .NETSTANDARD 2.0 2018-05-22 08:45:46 -07:00
Alexandre Mutel
812c4fabe6 Merge pull request #222 from caioproiete/add-tests-for-ConfigureNewLine
Add tests for `ConfigureNewLineExtension`'s `ConfigureNewLine`
2018-04-13 10:27:15 +02:00
Caio Proiete
85f8f59786 Add tests for ConfigureNewLineExtension's ConfigureNewLine 2018-04-12 20:34:53 -03:00
Alexandre Mutel
b0839f114c Bump to 0.15.0 2018-04-04 11:37:41 +02:00
Alexandre Mutel
431fecb1c6 Add third party extensions (#206) 2018-04-04 11:37:24 +02:00
Alexandre Mutel
7620b2b760 Merge pull request #214 from caioproiete/add-configure-new-line-extension
Add `ConfigureNewLineExtension` markdown extension
2018-04-04 11:09:48 +02:00
Alexandre Mutel
ad18514824 Merge pull request #213 from caioproiete/add-new-use-extension-with-instance
Add alternative `Use` extension method to `MarkdownPipelineBuilder` that receives an object instance
2018-02-23 09:23:38 +01:00
Caio Proiete
c68a488717 Add ConfigureNewLine extension method to MarkdownPipelineBuilder 2018-02-20 19:53:36 -04:00
Caio Proiete
e2f0f00831 Add ConfigureNewLineExtension markdown extension 2018-02-20 19:46:14 -04:00
Caio Proiete
fea2ca5adf Add alternative Use extension method to MarkdownPipelineBuilder that receives an object instance 2018-02-20 19:32:47 -04:00
Alexandre Mutel
f77bd0b36d Merge pull request #208 from pascalberger/patch-1
Fix typo
2018-02-17 10:01:58 +01:00
Pascal Berger
20243a79a0 Fix typo 2018-02-08 22:03:06 +01:00
Alexandre Mutel
a097247272 Merge pull request #203 from matteofabbri/master
added class attribute to media link extension
2018-02-01 10:06:31 +01:00
Matteo Fabbri
f3cb0712ca added class attribute to media link extension 2018-01-31 20:23:30 +01:00
Alexandre Mutel
eedfc3cd9c Merge pull request #201 from markheath/relative-urls-2
Optional link rewriter func for HtmlRenderer
2018-01-25 07:16:10 +01:00
Mark Heath
5f4b049ce0 removing spurious nuget file 2018-01-24 14:17:01 +00:00
Mark Heath
20edf26c40 optional link rewriter func for HtmlRenderer #143 2018-01-24 14:00:28 +00:00
Alexandre Mutel
4192a00e20 Merge pull request #199 from leotsarev/fix-resharper-unit-tests
Upgrade NUnit3TestAdapter from 3.2 to 3.9
2018-01-20 17:21:59 +01:00
Leonid Tsarev
4346c52ef0 Upgrade NUnit3TestAdapter from 3.2 to 3.9 to address Resharper test runner problems 2018-01-20 18:46:34 +03:00
Alexandre Mutel
42683e043d Merge pull request #197 from markheath/relative-urls
Convert relative URLs to absolute
2018-01-17 16:23:51 +01:00
Mark Heath
96c469018e HTML renderer supports converting relative URLs on links and images to absolute #143 2018-01-17 14:59:02 +00:00
Alexandre Mutel
f64ac47841 Bump to 0.14.9 2018-01-15 10:12:42 +01:00
Alexandre Mutel
c29b7d2942 Merge pull request #195 from leotsarev/remove-mailto
AutoLinkParser should to remove mailto: in outputted text
2018-01-10 11:17:34 +01:00
Leonid Tsarev
0f7e3b8c52 AutoLinkParser should to remove mailto: in outputted text 2018-01-09 11:48:06 +03:00
Alexandre Mutel
6dff16a612 Merge pull request #191 from leotsarev/improve-test-names
Improve discoverability of test in VS test runner
2018-01-08 20:50:43 +01:00
Alexandre Mutel
8e15c8bc6a Merge pull request #193 from leotsarev/support-vk-and-yandex-music
Added yandex.music & ok.ru support to MediaLinkExtension
2018-01-08 18:06:32 +01:00
Leonid Tsarev
558db1fd70 Support odnoklassniki.ru (meaning: classmates, top 2 social network in Russia) 2018-01-08 19:30:23 +03:00
Leonid Tsarev
796b143316 Add support for yandex music 2018-01-08 19:14:01 +03:00
Leonid Tsarev
3402805ebb refactor MediaLinkExtension to make it pluggable 2018-01-08 18:48:09 +03:00
Leonid Tsarev
c3600c2ba5 Merge branch 'master' into improve-test-names 2018-01-08 18:09:54 +03:00
Leonid Tsarev
4ba98f594a Merge branch 'master' into improve-test-names 2018-01-08 18:08:47 +03:00
Leonid Tsarev
544a64e5c9 Improve discoverability of test in VS test runner 2018-01-08 18:05:19 +03:00
Alexandre Mutel
f17320702a Merge pull request #189 from markheath/patch-1
Media link extension to support MP3 by default
2018-01-03 09:08:51 +01:00
Alexandre Mutel
d883694ac4 Merge pull request #186 from tthiery/normalize-autolinks
Add Normalization Support for AutoLinks
2018-01-03 09:08:08 +01:00
Mark Heath
42fba26ea2 Media link extension to support MP3 by default
I presume MP3 wasn't intentionally left out this list?
2018-01-01 13:50:52 +00:00
T. Thiery
595dacf213 Add Normalization Support for AutoLinks (including Options) 2017-12-24 12:21:23 +01:00
T. Thiery
75adfa3fe8 Add Normalization Tests for AutoLinks 2017-12-24 12:20:26 +01:00
Alexandre Mutel
0bb8139450 Merge pull request #185 from tthiery/normalize-jiralinks
Add Normalization Support for JIRA Links
2017-12-17 20:25:59 +01:00
T. Thiery
60784901b2 Add Normalization Support for JIRA Links 2017-12-17 17:00:55 +01:00
Alexandre Mutel
5020fdd3b1 Bump to 0.14.8 2017-12-05 09:36:48 +01:00
Alexandre Mutel
bd4ea56d2a Fix potential StackOverflow exception when processing deep nested | delimiters (#179) 2017-12-05 09:34:59 +01:00
Alexandre Mutel
fbc8a4116c Bump to 0.14.7 2017-11-25 14:17:23 +01:00
Alexandre Mutel
9af318d334 Merge pull request #175 from terryjintry/fixautolink
fix autolink attribute
2017-11-24 09:29:44 +01:00
Terry Jin
88e561c3ae fix autolink attribute 2017-11-24 14:34:44 +08:00
Alexandre Mutel
1ca82eb058 Bump to 0.14.6 2017-11-21 07:36:14 +01:00
Alexandre Mutel
66007c6bbf Add TestLink example (#171) 2017-11-21 07:35:17 +01:00
Alexandre Mutel
14d4286fd7 Merge pull request #170 from yishengjin1413/master
fix yaml frontmatter issue when ending with a empty line
2017-11-20 08:26:47 +01:00
Terry Jin
58b1a48f5b fix yaml frontmatter issue when ending with a empty line 2017-11-20 15:11:16 +08:00
Alexandre Mutel
5e1eaf8590 Bump to 0.14.5 2017-11-18 17:08:21 +01:00
Alexandre Mutel
c946295d96 Fix link to changelog.md in Markdig.csproj 2017-11-18 17:08:11 +01:00
Alexandre Mutel
97cbf11f8b Bump to 0.14.4 2017-11-18 11:29:39 +01:00
Alexandre Mutel
8d4394e7c6 Fix link conflict between a link to an image definition and heading auto-identifiers (#159) 2017-11-18 11:17:06 +01:00
Alexandre Mutel
fbdb8cf063 Better handle YAML frontmatter in case the opening --- is never actually closed (#160) 2017-11-18 10:55:16 +01:00
Alexandre Mutel
964538ec79 Add support for GFM autolinks (#165, #169) 2017-11-17 21:28:27 +01:00
Alexandre Mutel
9a30883e2a Add support for compatible github auto-identifiers for headings 2017-11-10 20:42:53 +01:00
Alexandre Mutel
6408705f82 Return an empty string for / on markdig webapi 2017-11-10 10:59:27 +01:00
Alexandre Mutel
523582e588 Fix bug when a thematic break is inside a fenced code block inside a pending list (#164) 2017-11-09 09:08:51 +01:00
Alexandre Mutel
aaff022e7c Merge pull request #161 from tthiery/normalize-tasklists
Add Normalization Support for Task Lists
2017-11-04 23:09:14 +01:00
T. Thiery
37eb6aa529 Add Normalization Support for Task Lists 2017-11-04 23:01:01 +01:00
Alexandre Mutel
9b7356b05e Remove local project 2017-11-03 14:22:05 +01:00
Alexandre Mutel
33d3bc1330 Upgrade WebApp to .NET core app 2.0 and add ApplicationInsights 2017-11-03 14:19:32 +01:00
Alexandre Mutel
07a2980d5b Bump to 0.14.3 2017-11-01 07:44:41 +01:00
Alexandre Mutel
2d8872f2a1 Make EmojiExtension.EnableSmiley public 2017-11-01 07:44:30 +01:00
Alexandre Mutel
16a9bbc84e Bump to 0.14.2 2017-11-01 07:35:07 +01:00
Alexandre Mutel
0e5338a709 Add option to disable smiley parsing in EmojiAndSmiley extension 2017-11-01 07:34:57 +01:00
Alexandre Mutel
9139e0142b Fix issue with emphasis preceded/followed by an HTML entity (#157) 2017-11-01 07:26:28 +01:00
Alexandre Mutel
9a38312df0 Add support for link reference definitions for Normalize renderer (#155) 2017-10-28 09:26:14 +02:00
Alexandre Mutel
d808dcf6f8 Bump to 0.14.1 2017-10-27 22:45:53 +02:00
Alexandre Mutel
d5985fc94c Add missing HtmlBlockRenderer 2017-10-27 22:44:58 +02:00
Alexandre Mutel
994687d5ae Use original bullet character if NormalizeOptions.ListItemCharacter is not set 2017-10-27 22:44:37 +02:00
Alexandre Mutel
2f4e958ab2 Bump to 0.14.0 2017-10-27 22:13:49 +02:00
Alexandre Mutel
26beaa81da Add comments to NormalizeOptions (#155) 2017-10-27 22:08:34 +02:00
Alexandre Mutel
de5ed11963 Add support for NormalizeOptions. Add a few more tests. Better handling of loose lists. 2017-10-27 22:01:43 +02:00
Alexandre Mutel
b557d51276 Don't output a space after a blockquote 2017-10-27 18:41:09 +02:00
Alexandre Mutel
27a8345943 Use Assert.AreEqual instead of string.Compare for better error reporting 2017-10-27 18:34:38 +02:00
Alexandre Mutel
131163ff9a Add bigger sample for normalize (#155) 2017-10-27 18:34:14 +02:00
Alexandre Mutel
241f674b99 Rename test names for normalize (#155) 2017-10-27 18:33:55 +02:00
Alexandre Mutel
194edee243 Fix normalize, don't introduce non necessary new lines (#155) 2017-10-27 18:32:43 +02:00
Alexandre Mutel
0995fa0cec Add tests for backslash and hard lines 2017-10-27 18:32:01 +02:00
Alexandre Mutel
6717be5210 Add support for escape characters for normalize (#155) 2017-10-27 18:30:32 +02:00
Alexandre Mutel
e15745f346 Fix escape inline parser to use the plain string instead of only a single string char (to allow literal continuation) 2017-10-27 18:28:57 +02:00
Alexandre Mutel
a513b0c587 Merge pull request #156 from leotsarev/add-plain-text-helper
Add simple helper that enables conversion to plain text
2017-10-27 10:54:03 +02:00
Leonid Tsarev
72adb963e8 Add simple helper that enables conversion to plain text 2017-10-27 11:35:23 +03:00
Alexandre Mutel
aac7df6b87 Merge remote-tracking branch 'origin/normalize' 2017-10-25 17:11:15 +02:00
Alexandre Mutel
4cf4bfb58f Merge pull request #154 from tthiery/normalize
Add support for normalize (core CommonMark for now)
2017-10-24 17:38:42 +02:00
T. Thiery
59630aec8e Review Fix: Make IntLog10Fast method private static. 2017-10-24 17:27:42 +02:00
Alexandre Mutel
70c4f6deda Merge pull request #140 from peinearydevelopment/master
Added EnableHtmlForBlock flag on HtmlRenderer for issue #104
2017-10-24 17:02:42 +02:00
T. Thiery
e2b3f812cb Merge remote-tracking branch 'upstream/normalize' into normalize 2017-10-24 16:53:42 +02:00
Alexandre Mutel
fc8adc70e0 Merge tag 'v0.13.4' into normalize
# Conflicts:
#	src/Markdig.Tests/Markdig.Tests.csproj
2017-10-24 16:28:58 +02:00
T. Thiery
72c2c06fcb Add normalization tests for space between leaves. 2017-10-24 15:58:44 +02:00
T. Thiery
7174e32b7a Remove incomplete UnorderedList syntax test case 2017-10-24 15:51:13 +02:00
T. Thiery
600219529c Add test approach for AST based arrange. 2017-10-20 16:10:02 +02:00
Alexandre Mutel
d7fd04d14c Bump to 0.13.4 2017-10-17 23:01:26 +02:00
Alexandre Mutel
2147e434f7 Add support for single table header row without a table body rows (#141) 2017-10-17 22:58:25 +02:00
Alexandre Mutel
7bd00d115f Merge pull request #149 from maulwuff/nomnomlSupport
Nomnoml support
2017-10-17 15:44:17 +02:00
maulwuff
80ef9d2799 doc: added nomnoml specs 2017-10-17 14:54:43 +02:00
maulwuff
d6a705d76c new: support nomnoml diagrams 2017-10-17 14:54:43 +02:00
Alexandre Mutel
42472085a6 Add mdtoc simple tool to generate a markdown TOC from a local markdown file or github link to a markdown file 2017-10-15 23:31:14 +02:00
Alexandre Mutel
3897e875ee Add KeepOpeningDigits and DiscardDots options to AutoIdentifiers to match GitHub behavior 2017-10-15 23:29:02 +02:00
T. Thiery
c63392657d Add finish block method and adjusted naming in test suite. 2017-09-18 23:28:34 +02:00
T. Thiery
34579b51a1 Relabeled unit tests. 2017-09-18 23:04:08 +02:00
T. Thiery
1bb35c5fc1 Fixed issues found in straight forward normalization test cases
- Code fences with attributes
- Escaping inline code
- Escaping link titles
- Encode hard line breaks
- HTML Entity emitting
- List intend.
2017-09-18 22:16:50 +02:00
T. Thiery
0c408951b8 Add straight forward unit tests. 2017-09-18 20:42:50 +02:00
Alexandre Mutel
218a094f0d Try to workaround tight/loose lists and paragraphs (wip #17) 2017-09-17 11:08:08 +02:00
Alexandre Mutel
3cc405b05b Add normalize renderers for core CommonMark components (wip #17) 2017-09-17 11:08:08 +02:00
Alexandre Mutel
d58db530bb wip normalize (issue #17) 2017-09-17 11:07:44 +02:00
Alexandre Mutel
3628dc3b17 Merge pull request #142 from mlaily/patch-1
Fix typos in EmojiSpecs.md
2017-09-13 22:30:24 +02:00
Melvyn Laïly
89ff42805d Fix typos in EmojiSpecs.md 2017-09-13 22:26:10 +02:00
Peineary Development
48866a2609 Added EnableHtmlForBlock flag on HtmlRenderer for issue #104 2017-09-11 22:43:23 -04:00
Alexandre Mutel
9cc5856c1c Bump to 0.13.3 2017-08-30 15:47:54 +02:00
Alexandre Mutel
a4bb174a77 Add .editorconfig 2017-08-30 15:44:08 +02:00
Alexandre Mutel
82987fa879 Merge pull request #139 from kenmuse/enhance/yaml-frontmatter
Implement Pandoc compatible front matter parsing
2017-08-30 15:36:38 +02:00
Ken Muse
2df2ff17c5 Replace tabs with spaces 2017-08-30 09:29:27 -04:00
Ken Muse
4e4825cb3f Implement Pandoc compatible front matter parsing 2017-08-29 18:49:14 -04:00
Alexandre Mutel
ed2371beae Update MSBuild.Sdk.Extras to version 1.0.9 to fix compatibility with pack for uap10.0 2017-08-29 08:06:29 +02:00
Alexandre Mutel
2c2898769e Bump to 0.13.2 2017-08-29 07:48:27 +02:00
Alexandre Mutel
001a99ab77 Add support for UAP10.0 (#137) 2017-08-29 07:48:14 +02:00
Alexandre Mutel
9233ec220c Bump to 0.13.1 2017-08-21 16:23:31 +02:00
Alexandre Mutel
bb30dc21c5 Fix indenting issue after a double digit list block using a tab (#134) 2017-08-21 16:04:47 +02:00
Alexandre Mutel
c761fa2243 Bump to 0.13.0 2017-08-03 19:26:48 +02:00
Alexandre Mutel
9d172ffcc1 Update URL links to accept balanced parenthesis for CommonMark specs 0.28 2017-08-03 19:25:17 +02:00
Alexandre Mutel
1863e22328 Update to emphasis to CommonMark specs 0.28 (when *** prefer em, strong vs strong, em) 2017-08-03 19:20:58 +02:00
Alexandre Mutel
c591d1950f Update CommonMark test specs to 0.28 2017-08-03 19:20:23 +02:00
Alexandre Mutel
339fcc9152 Add signed package link to readme.md (#11) 2017-08-03 14:57:28 +02:00
Alexandre Mutel
3a54f06540 Fix appveyor.yml for multiline cmd 2017-08-03 14:47:07 +02:00
Alexandre Mutel
3f305a25a8 Add sign assembly to build 2017-08-03 14:41:52 +02:00
Alexandre Mutel
5a210223b6 Bump to 0.12.4 2017-07-31 11:17:13 +02:00
Alexandre Mutel
a7786d934d Make Profile328 (for sl50 compat) not using unsafe code to pass verifier (issue #131) 2017-07-31 11:17:01 +02:00
Alexandre Mutel
cb3c1f1505 Merge pull request #124 from joannaz/master
Fix abbreviation matching
2017-07-03 14:38:25 +02:00
Joanna Zhang
50b33b8512 Add more test cases for abbreviation 2017-07-03 13:12:23 +01:00
Joanna Zhang
80790b5038 Fix abbreviation parsing 2017-07-03 11:55:31 +01:00
Joanna Zhang
311c28ae60 Fix abbreviation matching 2017-06-29 15:00:56 +01:00
Alexandre Mutel
9906a0554f Merge pull request #123 from joannaz/master
Fix abbreviation at start of paragraphs
2017-06-28 13:59:11 +02:00
Joanna Zhang
7e92f1881d Fix Abbrevation at start 2017-06-28 12:25:14 +01:00
Alexandre Mutel
41911806a0 Bump to 0.12.3 2017-06-26 11:53:59 +02:00
Alexandre Mutel
610f1519b0 Fix issue with HTML blocks for heading h2,h3,h4,h5,h6 that were not correctly identified as HTML blocks as per CommonMark spec 2017-06-26 11:53:47 +02:00
Alexandre Mutel
29aa56b4f1 Bump to 0.12.2 2017-06-18 10:58:40 +02:00
Alexandre Mutel
5004ecedb7 Use TestParser.TestSpec instead to mitigate problem with \r 2017-06-18 10:56:44 +02:00
Alexandre Mutel
ba06a796dc Merge branch 'pr/n121_pearsonn' 2017-06-18 10:50:39 +02:00
Nick Pearson
b1d6f34976 Fix issue in GenericAttributesParser causing inline elements to be skipped 2017-06-14 15:20:16 -07:00
Alexandre Mutel
28f4236a57 Bump to 0.12.1 2017-05-29 12:26:17 +02:00
Alexandre Mutel
0739a82735 Merge pull request #119 from meziantou/master
MediaLinkExtension: Remove unexpected closing tag
2017-05-28 19:00:54 +02:00
Meziantou
3ac9f2e788 Fix issue 2017-05-28 12:18:59 +02:00
Meziantou
d9607cc687 Update tests 2017-05-28 12:18:50 +02:00
Alexandre Mutel
f0573ef9e2 Bump to 0.12.0 2017-05-28 08:24:50 +02:00
Alexandre Mutel
017a3bd7e2 Merge branch 'pr/n118_meziantou' 2017-05-28 08:17:46 +02:00
Alexandre Mutel
d6f9a1055c Remove unnecessary test in BlockProcessor (#115) 2017-05-28 08:14:58 +02:00
Alexandre Mutel
2612e9d565 Fix issue in TryParseAutolink in LinkHelper (#115) 2017-05-28 08:02:55 +02:00
Alexandre Mutel
1076d7af10 Fix issue in ScanEntity (#115) 2017-05-28 07:56:58 +02:00
Meziantou
850cecfa6c Fix issue 2017-05-27 16:16:46 +02:00
Meziantou
bfdb03bd78 Update tests 2017-05-27 16:16:33 +02:00
Alexandre Mutel
56b6e329d5 Add missing update to Tests.csproj 2017-05-18 11:31:51 +02:00
Alexandre Mutel
006e384d1a Update links 2017-05-18 11:31:24 +02:00
Alexandre Mutel
9cd9b683a5 Remove specialized renderer for JiraLinks now going through the standard LinkInline 2017-05-18 11:04:42 +02:00
Alexandre Mutel
e01e2c3d2b Add StringBuilder extensions to accept a StringSlice 2017-05-18 11:04:06 +02:00
Alexandre Mutel
97ce0da564 Update JiraLinks, allow ( and ), use StringSlice instead of strings for key/issue, add support for attributes, add tests 2017-05-18 10:33:16 +02:00
Alexandre Mutel
26c5ce59b5 Update Benchmarks to latest DotNetBenchmark. Remove nuget.targets 2017-05-18 09:33:26 +02:00
Alexandre Mutel
d2d94ecf39 Merge branch 'pr/n113_clarkd' 2017-05-18 09:18:50 +02:00
Dave Clarke
0ec82bb81d Fix comment 2017-05-17 11:55:30 +01:00
Dave Clarke
ad9e941cb0 Fix missing space before target attribute
Rename Key -> ProjectKey
2017-05-16 16:48:25 +01:00
Dave Clarke
27dab6ca6d Automatically link JIRA issue references 2017-05-16 12:02:44 +01:00
Alexandre Mutel
762196f03b Bump to 0.11.0 2017-05-08 12:05:15 +02:00
Alexandre Mutel
26a565fa45 Fix/improve parsing of math inline, improving rules for matching, disallow newline between $$/$ for inline parsing (#87) 2017-05-08 11:50:40 +02:00
Alexandre Mutel
4369db1a43 Fix issue with $$ block parsing that should be interpreted as inline parsing (#105) 2017-05-08 11:48:44 +02:00
Alexandre Mutel
d83f1f87cc Add custom arrows to emoji 2017-05-08 09:25:44 +02:00
Alexandre Mutel
14027f4be3 Migrate to VS2017 - dotnet new csproj 2017-05-06 15:41:07 +02:00
Alexandre Mutel
8bcfb53607 Merge pull request #109 from aKzenT/issue-108
Fix for issue #108
2017-04-29 11:11:05 +02:00
Thomas Krause
700bb21e03 Fix for issue #108 2017-04-28 21:47:41 +02:00
Alexandre Mutel
1bc9d9083c Merge pull request #105 from MatthewRichards/DotTerminatedUrls
Treat trailing full stop after a URL as not being part of the URL
2017-04-04 10:02:38 +02:00
Alexandre Mutel
1d7bb021a7 Bump version to 0.10.7 2017-04-01 10:20:18 +02:00
Matthew Richards
3305a74b06 Additional test case 2017-03-30 08:51:05 +01:00
Matthew Richards
6312bc0515 Treat trailing full stop after a URL as not being part of the URL 2017-03-29 16:47:41 +01:00
Alexandre Mutel
b595383281 Merge pull request #103 from Daniel15/replace
Add Replace method to replace an element in an OrderedList
2017-03-20 10:39:01 +01:00
Alexandre Mutel
bf85964543 Merge pull request #102 from Daniel15/lock-dotnet
Lock .NET Core SDK to 1.0.0-preview2
2017-03-20 10:37:16 +01:00
Daniel Lo Nigro
d5b020b784 Add Replace method to replace an element in an OrderedList 2017-03-19 14:39:24 -07:00
Daniel Lo Nigro
8ab0467e78 Lock .NET Core SDK to 1.0.0-preview2 2017-03-19 13:42:26 -07:00
Alexandre Mutel
74fe3be7c7 Bump version to 0.10.6 2017-02-24 07:52:29 +01:00
Alexandre Mutel
b47ae8d9ba Fix emphasis with HTML entities (#97) 2017-02-24 07:49:46 +01:00
Alexandre Mutel
a926e1a326 Bump version to 0.10.5 2017-02-14 17:36:54 +01:00
Alexandre Mutel
6048406f52 Merge branch 'pr/n89_johnsimons' 2017-01-31 13:01:17 +01:00
John Simons
1edf14c742 Remove requirement for space after emoji.
Fixes #88
2017-01-31 12:58:32 +01:00
Alexandre Mutel
d4bb7c0a51 Merge pull request #82 from christophano/bugfix/abbreviation_parser_fix
Fixes the IsValidAbbreviation method in AbbreviationParser
2016-12-19 14:37:32 +01:00
Chris Rodgers
ba4e463517 Adds the test I ought to have included to begin with. 2016-12-19 13:25:56 +00:00
Chris Rodgers
2d0dae4ebb Fixes the IsValidAbbreviation method in AbbreviationParser 2016-12-19 13:05:03 +00:00
Alexandre Mutel
1a9d923c44 Merge pull request #81 from volkanceylan/master
Use Char.ToUpperInvariant instead of Char.ToUpper
2016-12-18 22:20:20 +01:00
volkan.ceylan
013ec0bf80 Due to use of Char.ToUpper, ListExtraItemParser fails in cultures like Turkish where uppercase of 'i' is 'İ'. Char.ToUpperInvariant should be used instead. 2016-12-18 11:47:25 +03:00
Alexandre Mutel
f82e874cb7 Merge pull request #80 from christophano/feature/pipeline_builder_use_method
Adds fluent extension registration method to MarkdownPipelineBuilder.
2016-12-15 11:49:32 +01:00
Chris Rodgers
976fc569fa Moves new Use method to MarkdownExtensions and changes it to extension method. 2016-12-15 10:31:02 +00:00
Chris Rodgers
5aab21f0bf Adds fluent extension registration method to MarkdownPipelineBuilder. 2016-12-15 10:07:54 +00:00
Alexandre Mutel
1df8344576 Bump to 0.10.4 2016-12-12 11:03:48 +01:00
Alexandre Mutel
9eb84cf95e Normalize pipe tables so they contain the same number of columns for all rows 2016-12-11 19:01:49 +01:00
Alexandre Mutel
47d45b5577 Fix issue when parsing autolinks embraced by a pending emphasis (issue #78) 2016-12-11 18:08:51 +01:00
Alexandre Mutel
ebcc286df0 Bump version to 0.10.3 2016-11-29 18:02:39 +01:00
Alexandre Mutel
f11d8037ac Fix issue when the last character of a table is not a newline and there are spaces in-between (issue #73) 2016-11-29 17:43:28 +01:00
Alexandre Mutel
f668b3fe38 Bump version to 0.10.2 2016-11-26 11:20:49 +01:00
Alexandre Mutel
5ca4e31332 Fix exception when trying to Urilize an url with an unicode character outside the supported range by string.Normalize + NormD (issue #75) 2016-11-26 11:20:37 +01:00
Alexandre Mutel
a8fbe79e30 Bump to 0.10.1 2016-11-19 16:53:56 +01:00
Alexandre Mutel
bdb09b9820 Update to latest CommonMark specs 0.27 2016-11-19 16:53:44 +01:00
Alexandre Mutel
79348ebea8 Add span to LinkReferenceDefinition (issue #51) 2016-10-27 17:06:37 +02:00
Alexandre Mutel
b366c1a5e3 Bump to 0.10.0 2016-10-16 21:30:55 +02:00
Alexandre Mutel
a5e53b014f Add better support to discover active extensions when setup renderer (issue #66) 2016-10-16 21:30:38 +02:00
Alexandre Mutel
c2f21e21c8 Bump to 0.9.1 2016-10-11 14:45:03 +02:00
Alexandre Mutel
81b8dce7a8 Fix regression between autolink extension with html inline link a /regular or regular markdown links (issue #65) 2016-10-11 14:44:50 +02:00
Alexandre Mutel
e6223f86d8 Update readme with autolink extension 2016-10-11 13:15:10 +02:00
Alexandre Mutel
2054c16662 Bump to 0.9.0 2016-10-11 13:10:25 +02:00
Alexandre Mutel
95641e562f Add support for the autolink extension (issue #64) 2016-10-11 13:10:06 +02:00
Alexandre Mutel
e4953931c7 Update donation logo 2016-09-29 20:00:08 +02:00
Alexandre Mutel
93b5b7a091 Add donation button 2016-09-29 18:23:30 +02:00
Alexandre Mutel
4f52e893ee Disable warning for comments as it slows down build on appveyor 2016-09-23 14:01:01 +02:00
Alexandre Mutel
23ede077a1 Bump version to 0.8.5 2016-09-23 13:48:15 +02:00
Alexandre Mutel
d5e6f17683 Allow to force table alignment to left (issue #62) 2016-09-23 13:43:49 +02:00
Alexandre Mutel
4d5980a485 Bump version to 0.8.4 2016-09-22 21:48:58 +02:00
Alexandre Mutel
19dd902519 Fix issue when calculating the span of an indented code block within a list. Make sure to include first whitespace on the line 2016-09-22 21:48:15 +02:00
Alexandre Mutel
f046a46275 Bump version to 0.8.3 2016-09-22 16:26:54 +02:00
Alexandre Mutel
25e9eafa8b Allow to pass a manual pipeline to TestParser.TestSpec 2016-09-22 16:25:46 +02:00
Alexandre Mutel
f4ff981008 Fix NullReferenceException with GridTable extension when a single + is entered on a line 2016-09-22 16:25:13 +02:00
Alexandre Mutel
a1b48aff89 Bump version to 0.8.2 2016-09-22 12:44:52 +02:00
Alexandre Mutel
0aa34caa82 Merge pull request #61 from christophano/bugfix/literal-inline-parser
Fixes issue where LiteralInlineParser calls PostMatch while processor.Inline is type other than LiteralInline
2016-09-22 12:40:16 +02:00
Chris Rodgers
98ce9b1a06 Fixes issue where LiteralInlineParser calls PostMatch while processor.Inline is type other than LiteralInline 2016-09-22 09:28:52 +01:00
Alexandre Mutel
ab157b21ea Update the readme with recent extensions added 2016-09-22 09:31:43 +02:00
Alexandre Mutel
53c72d3031 Bump version to 0.8.1 2016-09-21 13:06:51 +02:00
Alexandre Mutel
165e2f97d0 Output attached html attributes for dd in definition lists (fix issue #59) 2016-09-21 13:06:37 +02:00
Alexandre Mutel
6c577059ad Add extension NonAsciiNoEscape for URI to workaround a bug/singular behavior of EDGE/IE for local file links with non US-ASCII characters 2016-09-21 12:57:16 +02:00
Alexandre Mutel
0ea4dc769b Merge pull request #60 from christophano/bugfix/abbreviation-preceded-by-punctuation
Fixes issue where punctuation forms part of word, so abbreviation is not valid
2016-09-20 16:37:21 +02:00
Chris Rodgers
a9b626e810 Fixes issue where punctuation forms part of word, so abbreviation is not valid. 2016-09-20 15:22:31 +01:00
Alexandre Mutel
b90d6f9769 Bump to 0.8.0 2016-09-19 10:36:35 +02:00
Alexandre Mutel
f0ea008c46 Add support for YAML frontmatter parsing/discard (issue #37) 2016-09-19 10:11:38 +02:00
Alexandre Mutel
c43d5ccd63 Don't create an empty LiteralInline when trimming spaces at the end of a line (issue #42) 2016-09-19 09:10:36 +02:00
Alexandre Mutel
d6d7b398e4 Update to latest CommonMark specs. Fix new corner cases when parsing inline links 2016-09-18 13:24:19 +02:00
Alexandre Mutel
e1032e5094 Bump version to 0.7.5 2016-09-18 11:21:17 +02:00
Alexandre Mutel
a32ac298c5 Merge branch 'pr/n58_christophano'
# Conflicts:
#	src/Markdig/Parsers/InlineProcessor.cs
2016-09-18 11:12:10 +02:00
Alexandre Mutel
2e6ab670cb Keep UrlSpan (and LabelSpan and TitleSpan) for a LinkReferenceDefinition (issue #51) 2016-09-18 11:07:56 +02:00
Alexandre Mutel
72b7fce48c Fix extension DisableHtml not working properly with inline HTML (issue #46) 2016-09-18 10:59:12 +02:00
Alexandre Mutel
fa281f1ca1 Fix pipetable parsing (issue #44) 2016-09-18 10:44:26 +02:00
Chris Rodgers
1b92311aeb Fixes issue when using colon (among others) on lines with abbreviations 2016-09-15 14:18:58 +01:00
Alexandre Mutel
a593212f03 Merge pull request #45 from christophano/feature/rowspan
Adds support for rowspans in grid tables
2016-09-15 09:03:44 +02:00
Alexandre Mutel
c4ec928953 Merge pull request #57 from christophano/bugfix/punctuation-exceptions
Adds exceptions to allow certain punctuation characters to behave as inline delimiters
2016-09-15 09:00:57 +02:00
Chris Rodgers
e7df7fabeb Adds exceptions to allow certain punctuation characters to behave as inline delimiters 2016-09-14 12:00:31 +01:00
Alexandre Mutel
1c187f2d81 Merge pull request #56 from christophano/bugfix/similar-abbreviations
Fixes problem with multiple, similar abbreviations not parsing correctly
2016-09-14 07:42:26 +02:00
Alexandre Mutel
da66cf90c3 Merge pull request #55 from christophano/bugfix/restart-list-after-paragraph
Checks if paragraph block is closed when deciding if continuation list is allowable with character other than '1.'
2016-09-14 07:40:33 +02:00
Alexandre Mutel
0e8bd7407f Merge pull request #49 from christophano/bugfix/nested-definition-lists
Fixes issue where multiple definition lists are created when they are nested in a list item.
2016-09-14 07:39:24 +02:00
Chris Rodgers
d70f14addb Fixes problem with multiple, similar abbreviations not parsing correctly. 2016-09-13 16:53:25 +01:00
Chris Rodgers
e7b9eea3a5 Checks if paragraph block is closed when deciding if continuation list is allowable with character other than '1.'
Fixes #54
2016-09-09 14:09:52 +01:00
Alexandre Mutel
97af9d822d Merge pull request #50 from christophano/bugfix/ordered-list-start-attribute
Changes List Extra to output valid integer for start attribute, instead of roman or latin character.
2016-08-27 09:07:46 +02:00
Alexandre Mutel
cf7a09ab76 Merge pull request #52 from christophano/bugfix/merge-auto-generated-id
fixes issue where using special attributes overwites element id, even when id is not specified
2016-08-27 09:05:22 +02:00
Chris Rodgers
e755627421 fixes issue where using special attributes overwites element id, even when id is not specified 2016-08-25 09:11:50 +01:00
Chris Rodgers
f9be64a988 Changes List Extra to output valid integer for start attribute, instead of roman or latin character. 2016-08-23 13:58:06 +01:00
Alexandre Mutel
d65431e6cc Merge pull request #47 from christophano/bugfix/inline-maths-with-trailing-text
Fixes issue where trailing text after an inline math block is both included in the math block and appended after the math block.
2016-08-23 08:19:01 +02:00
Chris Rodgers
86fb962fdb Removes BlankLineBlock if it is found after an active definition list. 2016-08-22 15:41:39 +01:00
Chris Rodgers
d003837b27 Fixes issue where multiple definition lists are created when they are nested in a list item. 2016-08-22 14:47:43 +01:00
Chris Rodgers
fd813e3c5a Fixes issue where trailing text after an inline math block is both included in the math block and appended after the math block. 2016-08-19 12:15:17 +01:00
Chris Rodgers
a3691c4423 Fixed issue when malformed tables can result in an unhandled exception being thrown. 2016-08-12 15:57:35 +01:00
Chris Rodgers
9506f22025 Adds support for rowspans in grid tables 2016-08-12 15:57:28 +01:00
Alexandre Mutel
06ae907949 Bump version to 0.7.4 2016-07-30 11:35:36 +02:00
Alexandre Mutel
555523b2af Add additional tests for emphasis (issue #43) 2016-07-30 11:33:45 +02:00
Alexandre Mutel
3b9772f772 Merge branch 'pr/n43_christophano' 2016-07-30 11:20:48 +02:00
Chris Rodgers
891c80f48c Fixes issue where sentences containing strong emphasis words did not correctly delimit the strong tag 2016-07-28 12:04:49 +01:00
Alexandre Mutel
105b09e1ec Bump version to 0.7.3 2016-07-23 12:47:04 +02:00
Alexandre Mutel
9fe7596a23 Update project.json for WebApp to .NET Core RTM 2016-07-23 12:46:48 +02:00
Alexandre Mutel
82af7cadc5 Fix issue with MarkdownPipeline that was not threadsafe and initialization was deferred at parsing time instead of pipeline build time (issue #40) 2016-07-23 12:46:26 +02:00
Alexandre Mutel
800c81bb9a Bump version to 0.7.2 2016-07-20 11:03:07 +02:00
Alexandre Mutel
5653a4f7ee Render table colspan with optional decimals for percentage 2016-07-20 10:51:05 +02:00
Alexandre Mutel
4f14ffe63b Merge commit 'fba96774f427ccdc08b5cb89a6dd02e84b2ef9d6' from capjan/markdig 2016-07-20 10:31:03 +02:00
Alexandre Mutel
d57acefe56 Merge pull request #39 from christophano/bugfix/cell-after-colspan
Bugfix colspan for grid tables
2016-07-19 10:06:25 +02:00
Alexandre Mutel
fb61e5a8da Update to latest CommonMark 0.26 specs. Two consecutive blank lines in a list no longer break it. Ordered list can break a paragraph if it starts at '1' 2016-07-19 09:41:59 +02:00
Chris Rodgers
264516bfdb Updated tests via md file and regenerated from t4 2016-07-19 08:40:34 +01:00
Chris Rodgers
6d8f8996d5 Fixes cell contents not displaying after colspan merged cell 2016-07-19 08:38:05 +01:00
Chris Rodgers
ba8557d3bf Corrects spec for TestExtensionsGridTable Example003 2016-07-19 08:38:05 +01:00
Alexandre Mutel
d18fd0b957 Link to a fixed version of the CommonMark specs to avoid unexpected upgrades to latest changes in the specs 2016-07-19 08:03:23 +02:00
Jan Ruhländer
fba96774f4 Fix: Table extension column width doesn't work on computers that use a comma as demimal point 2016-07-12 14:47:35 +02:00
Alexandre Mutel
0b2764ea62 Merge pull request #36 from synhershko/norel-links
Adding support for rel=nofollow in links
2016-07-10 08:14:52 +09:00
Itamar Syn-Hershko
64d81ed47b Removing config 2016-07-10 02:01:53 +03:00
Itamar Syn-Hershko
9a9742888b Fixing last bits 2016-07-10 01:47:38 +03:00
Itamar Syn-Hershko
5400b30a90 Fixing for renderer.EnableHtmlForInline case 2016-07-10 00:27:50 +03:00
Itamar Syn-Hershko
673f4a4beb Avoid adding rel to images 2016-07-10 00:13:51 +03:00
Itamar Syn-Hershko
05a27649aa Adding support for rel=nofollow in links 2016-07-09 23:56:58 +03:00
Alexandre Mutel
26c6b12dea Merge pull request #35 from ChrisMissal/readme-changes
Update readme.md, fix typos
2016-07-08 09:09:28 +09:00
Chris Missal
9a18ca222f Update readme.md
fixes typos
2016-07-07 18:30:09 -05:00
Alexandre Mutel
354f31b870 Fix calculation of span for HTML entities 2016-07-06 06:45:48 +09:00
Alexandre Mutel
c6c2f58ec0 Add support for diagrams extension. wip with mermaid (issue #34) 2016-07-05 23:24:37 +09:00
Alexandre Mutel
4a146f00f6 Bump to 0.7.1 2016-06-30 21:56:05 +09:00
Alexandre Mutel
2c10ac59d3 Update to latest specs 2016-06-30 21:54:18 +09:00
Alexandre Mutel
9a98fb9453 Fix issue InvalidCastException with SmartyPants extension when using both a mdash with quotes. 2016-06-30 21:53:53 +09:00
Alexandre Mutel
b20b111385 Try to workaround tight/loose lists and paragraphs (wip #17) 2016-06-28 23:55:01 +09:00
Alexandre Mutel
6ac2429e2a Add normalize renderers for core CommonMark components (wip #17) 2016-06-28 23:37:52 +09:00
Alexandre Mutel
9d52732f18 wip normalize (issue #17) 2016-06-28 10:53:08 +09:00
Alexandre Mutel
1c7f843a4c Update readme for building Markdig (issue #28) 2016-06-27 22:55:21 +09:00
Alexandre Mutel
fdaf301bd6 Update to latest dotnet Core RTM, NETStandard.Library 1.6.0. Bump to version 0.7.0 2016-06-27 21:44:31 +09:00
Alexandre Mutel
5a9f2f3afe Fix calculated span for indented code blocks that was starting after the first 4 chars and not before (issue https://github.com/madskristensen/MarkdownEditor/issues/8) 2016-06-27 20:32:24 +09:00
Alexandre Mutel
6a1b30761a Merge branch 'pr/n30_Jither' 2016-06-27 06:59:13 +09:00
Jimmi Thøgersen
8f8b08fad6 Include digits after first letter in Urilize (for Auto-Identifiers)
Added dependency: NUnit3TestAdapter (for VS Test Runner)
Disabled "Prefer 32-bit" on Markdig.Tests to accommodate NUnit.
2016-06-26 23:39:43 +02:00
Alexandre Mutel
312b63a4c8 Bump to 0.6.2 2016-06-25 18:27:06 +09:00
Alexandre Mutel
1598b538ab Update to latest CommonMark spec. Handle new corner cases for emphasis (see https://talk.commonmark.org/t/emphasis-strong-emphasis-corner-cases/2123/1) 2016-06-25 18:25:24 +09:00
Alexandre Mutel
16ce46a741 Bump to 0.6.1 2016-06-24 20:33:30 +09:00
Alexandre Mutel
4456598228 Double check all extensions with advanced profiles to make sure there are no conflicts between existing extensions 2016-06-24 17:44:57 +09:00
Alexandre Mutel
2a937c63b9 Fix issue with auto identifiers extension overriding manual ids for headings 2016-06-24 17:43:46 +09:00
Alexandre Mutel
191bc940c7 Bump version to 0.6.0 2016-06-24 16:10:58 +09:00
Alexandre Mutel
05673758e3 Add new SelfPipeline extension 2016-06-24 15:05:37 +09:00
Alexandre Mutel
cff2b9a8ca Fix conflict between SmartyPants extension and pipetables (issue #13) 2016-06-24 12:04:29 +09:00
Alexandre Mutel
586095a475 Refactor StringSlice.Search to StringSlice.IndexOf to make it more familiar and consistent 2016-06-24 12:03:16 +09:00
Alexandre Mutel
eae2082a1e Rename IDelimiterProcessor to IPostInlineProcessor as it can be used outside of a delimiter processing (for example SmartyPants mdash) 2016-06-24 12:02:25 +09:00
Alexandre Mutel
4576548df3 Add comment to Descendants() method that should be implemented without recursive 2016-06-24 12:01:03 +09:00
Alexandre Mutel
266e0c8bfd Fix consecutive footnotes without blanklines between (issue #26) 2016-06-24 06:01:53 +09:00
Alexandre Mutel
f5c07dbab5 Bump to 0.5.12 2016-06-24 05:43:17 +09:00
Alexandre Mutel
c8a28a1ad7 Fix an issue where a consecutive footnote is parsed as a paragraph (issue #26) 2016-06-24 05:42:36 +09:00
Alexandre Mutel
72cc454314 Remove backsticks in SmartyPantsInlineParser. Rename SmaryPantsInlineParser to SmartyPantsInlineParser 2016-06-24 05:27:12 +09:00
Alexandre Mutel
7fe2c1f939 Remove OrderedList.ReplacyBy method, as it is not used and might be not predictable (issue #25) 2016-06-24 05:25:54 +09:00
Alexandre Mutel
087e7a68b6 Fix emoji parsing and allow them only if they are surrounded by spaces (issue #15) 2016-06-23 17:28:33 +09:00
Alexandre Mutel
abeabf15a1 Remove non necessary check in HtmlAttributes.CopyTo 2016-06-23 11:49:01 +09:00
Alexandre Mutel
f9bfcaab7b Bump version to 0.5.11 2016-06-23 11:46:22 +09:00
Alexandre Mutel
afa0182f02 Fix ArgumentNullReference exception in HtmlAttributes.CopyTo (issue #23) 2016-06-23 11:46:07 +09:00
Alexandre Mutel
b83de5934d Bump version to 0.5.10 2016-06-23 05:52:06 +09:00
Alexandre Mutel
c294d3bfb4 Improve compact method for tests taking into account \r\n and \r 2016-06-23 05:52:06 +09:00
Alexandre Mutel
a7cdb2351a Fix regression for html block parsing (issue #21) 2016-06-23 05:52:05 +09:00
Alexandre Mutel
46ef21a3ed Merge pull request #19 from jorrit/patch-1
Fix typo in MarkdownExtensions.cs
2016-06-22 20:55:51 +09:00
Jorrit Schippers
18c8d0178c Fix typo in MarkdownExtensions.cs 2016-06-22 13:55:09 +02:00
Alexandre Mutel
ebc79dafbd Bump version to 0.5.9 2016-06-22 08:37:37 +09:00
Alexandre Mutel
a1d2467643 Fix bug for deep nested list items that were incorrectly detected (not CommonMark compliant. Check with CommonMark specs to add a test case) 2016-06-22 08:35:42 +09:00
Alexandre Mutel
8220f0fa56 Fix ArgumentOutOfRangeException in Block.FindClosestLine. Add tests for Pragmalines 2016-06-22 08:35:08 +09:00
Alexandre Mutel
ec385acc7f Bump version to 0.5.8 2016-06-21 23:45:48 +09:00
Alexandre Mutel
04c1cc62d4 Add block extension methods for looking for a block from a position or for the closest line (typically used for syntax highlighting by MarkdownEditor) 2016-06-21 23:45:40 +09:00
Alexandre Mutel
abdbd65f60 Fix calculation of spans for nested blocks 2016-06-21 23:45:28 +09:00
Alexandre Mutel
1f32a060da Update readme and link to MarkdownEditor 2016-06-20 15:05:39 +09:00
Alexandre Mutel
699d80c150 Add missing test for issue #16 2016-06-20 13:48:22 +09:00
Alexandre Mutel
56bcac7600 Bump version to 0.5.7 2016-06-20 13:42:32 +09:00
Alexandre Mutel
cab3365104 Fix issue while detecting generic attributes that was breaking parsing (issue #16) 2016-06-20 13:42:15 +09:00
Alexandre Mutel
3821bd00fe Bump version to 0.5.6 2016-06-20 12:30:25 +09:00
Alexandre Mutel
62701fd0f1 Improve pragma line output 2016-06-20 12:29:13 +09:00
Alexandre Mutel
1be5e60506 Make to MarkdownPipeline.Setup public. Make Markdown.ToHtml returning a MarkdownDocument 2016-06-20 12:27:50 +09:00
Alexandre Mutel
c9f1512358 Bump to 0.5.5 2016-06-20 09:06:29 +09:00
Alexandre Mutel
8f23aed6af Add pragma line extension 2016-06-20 09:04:33 +09:00
Alexandre Mutel
6a62ae9c69 Add github like class for taslk lists (issue #14) 2016-06-20 09:04:13 +09:00
Alexandre Mutel
2c3de5688b Don't add a class an HtmlAttributes that is already in the list 2016-06-20 09:00:54 +09:00
Alexandre Mutel
f3c08b4ec4 Output HtmlAttributes for unordered list 2016-06-20 09:00:31 +09:00
Alexandre Mutel
69e3baafe5 Add support for callbacks to RendererBase, IMarkdownRenderer 2016-06-20 09:00:10 +09:00
Alexandre Mutel
5844ccc395 Bump to 0.5.4 2016-06-20 06:58:55 +09:00
Alexandre Mutel
be9c6fa54b Fix bug for html block parsing in StringSlice.Search (issue #12) 2016-06-20 06:56:13 +09:00
Alexandre Mutel
d14f277c7b Bump version to 0.5.3 2016-06-19 11:37:26 +09:00
Alexandre Mutel
593bf08b92 Fix bug in pipetables with a trailing | on the header row separator (issue #10) 2016-06-19 11:37:12 +09:00
231 changed files with 32673 additions and 21149 deletions

37
.editorconfig Normal file
View File

@@ -0,0 +1,37 @@
# EditorConfig is awesome:http://EditorConfig.org
# top-most EditorConfig file
root = true
# All Files
[*]
charset = utf-8
indent_style = space
indent_size = 4
insert_final_newline = false
trim_trailing_whitespace = true
# Solution Files
[*.sln]
indent_style = tab
# XML Project Files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
indent_size = 2
# Configuration Files
[*.{json,xml,yml,config,props,targets,nuspec,resx,ruleset}]
indent_size = 2
# Markdown Files
[*.md]
trim_trailing_whitespace = false
# Web Files
[*.{htm,html,js,ts,css,scss,less}]
indent_size = 2
insert_final_newline = true
# Bash Files
[*.sh]
end_of_line = lf

46
appveyor.yml Normal file
View File

@@ -0,0 +1,46 @@
version: 10.0.{build}
image: Visual Studio 2017
configuration: Release
install:
- ps: >-
cd src
nuget restore Markdig.sln
$env:MARKDIG_BUILD_NUMBER = ([int]$env:APPVEYOR_BUILD_NUMBER).ToString("000")
$env:MARKDIG_VERSION_SUFFIX = ""
$env:appveyor_nuget_push = 'false'
if(-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) {
if($env:appveyor_repo_tag -eq 'True') {
if($env:appveyor_repo_tag_name -match '^v[0-9]') {
$env:appveyor_nuget_push = 'true'
$env:MARKDIG_VERSION_SUFFIX = ""
}
if($env:appveyor_repo_tag_name -eq 'latest') {
$env:appveyor_nuget_push = 'true'
$env:MARKDIG_VERSION_SUFFIX = "pre$env:MARKDIG_BUILD_NUMBER"
}
}
}
build:
project: src/Markdig.sln
verbosity: minimal
before_package:
- cmd: >-
msbuild /t:pack /p:VersionSuffix="%MARKDIG_VERSION_SUFFIX%" /p:Configuration=Release Markdig/Markdig.csproj
msbuild /t:Clean Markdig/Markdig.csproj
msbuild /t:pack /p:VersionSuffix="%MARKDIG_VERSION_SUFFIX%" /p:Configuration=Release;SignAssembly=true Markdig/Markdig.csproj
artifacts:
- path: src\Markdig\Bin\Release\*.nupkg
name: Markdig Nugets
deploy:
- provider: NuGet
api_key:
secure: 7cthHh+wYWZjhqxaxR6QObRaRnstvFkQOY7MkxIsC5kpQEBlKZXuinf0IybbYxJt
on:
appveyor_nuget_push: true

149
changelog.md Normal file
View File

@@ -0,0 +1,149 @@
# Changelog
## 0.15.5 (11 Dec 2018)
- Empty image alt text for link reference definitions ([(PR #254)](https://github.com/lunet-io/markdig/pull/254))
- Fix AutoLink Match links without slash after domain ([(PR #260)](https://github.com/lunet-io/markdig/pull/260))
- Make AutoLink ValidPreviousCharacters configurable ([(PR #264)](https://github.com/lunet-io/markdig/pull/264))
- Ensuring line breaks when renderer does not have html enabled ([(PR #270)](https://github.com/lunet-io/markdig/pull/270))
## 0.15.4 (07 Oct 2018)
- Add autolink domain GFM validation ([(PR #239)](https://github.com/lunet-io/markdig/pull/253))
## 0.15.3 (15 Sep 2018)
- Add support for RTL ([(PR #239)](https://github.com/lunet-io/markdig/pull/239))
- Add MarkdownDocument.LineCount ([(PR #241)](https://github.com/lunet-io/markdig/pull/241))
- Fix source positions for link definitions ([(PR #243)](https://github.com/lunet-io/markdig/pull/243))
- Add ListItemBlock.Order ([(PR #244)](https://github.com/lunet-io/markdig/pull/244))
- Add MarkdownDocument.LineStartIndexes ([(PR #247)](https://github.com/lunet-io/markdig/pull/247))
## 0.15.2 (21 Aug 2018)
- Fix footnotes parsing when they are defined after a container that has been closed in the meantime (#223)
## 0.15.1 (10 July 2018)
- Add support for `netstandard2.0`
- Make AutoIdentifierExtension thread safe
## 0.15.0 (4 Apr 2018)
- Add `ConfigureNewLine` extension method to `MarkdownPipelineBuilder` ([(PR #214)](https://github.com/lunet-io/markdig/pull/214))
- Add alternative `Use` extension method to `MarkdownPipelineBuilder` that receives an object instance ([(PR #213)](https://github.com/lunet-io/markdig/pull/213))
- Added class attribute to media link extension ([(PR #203)](https://github.com/lunet-io/markdig/pull/203))
- Optional link rewriter func for HtmlRenderer #143 ([(PR #201)](https://github.com/lunet-io/markdig/pull/201))
- Upgrade NUnit3TestAdapter from 3.2 to 3.9 to address Resharper test runner problems ([(PR #199)](https://github.com/lunet-io/markdig/pull/199))
- HTML renderer supports converting relative URLs on links and images to absolute #143 ([(PR #197)](https://github.com/lunet-io/markdig/pull/197))
## 0.14.9 (15 Jan 2018)
- AutoLinkParser should to remove mailto: in outputted text ([(PR #195)](https://github.com/lunet-io/markdig/pull/195))
- Add support for `music.yandex.ru` and `ok.ru` for MediaLinks extension ([(PR #193)](https://github.com/lunet-io/markdig/pull/193))
## 0.14.8 (05 Dec 2017)
- Fix potential StackOverflow exception when processing deep nested `|` delimiters (#179)
## 0.14.7 (25 Nov 2017)
- Fix autolink attached attributes not being displayed properly (#175)
## 0.14.6 (21 Nov 2017)
- Fix yaml frontmatter issue when ending with a empty line (#170)
## 0.14.5 (18 Nov 2017)
- Fix changelog link from nuget package
## 0.14.4 (18 Nov 2017)
- Add changelog.md
- Fix bug when a thematic break is inside a fenced code block inside a pending list (#164)
- Add support for GFM autolinks (#165, #169)
- Better handle YAML frontmatter in case the opening `---` is never actually closed (#160)
- Fix link conflict between a link to an image definition and heading auto-identifiers (#159)
## 0.14.3
- Make EmojiExtension.EnableSmiley public
## 0.14.2
- Fix issue with emphasis preceded/followed by an HTML entity (#157)
- Add support for link reference definitions for Normalize renderer (#155)
- Add option to disable smiley parsing in EmojiAndSmiley extension
## 0.14.1
- Fix crash in Markdown.Normalize to handle HtmlBlock correctly
- Add better handling of bullet character for lists in Markdown.Normalize
## 0.14.0
- Add Markdown.ToPlainText, Add option HtmlRenderer.EnableHtmlForBlock.
- Add Markdown.Normalize, to allow to normalize a markdown document. Add NormalizeRenderer, to render a MarkdownDocument back to markdown.
## 0.13.4
- Add support for single table header row without a table body rows (#141)
- ADd support for `nomnoml` diagrams
## 0.13.3
- Add support for Pandoc YAML frontmatter (#138)
## 0.13.2
- Add support for UAP10.0 (#137)
## 0.13.1
- Fix indenting issue after a double digit list block using a tab (#134)
## 0.13.0
- Update to latest CommonMark specs 0.28
## 0.12.3
- Fix issue with HTML blocks for heading h2,h3,h4,h5,h6 that were not correctly identified as HTML blocks as per CommonMark spec
## 0.12.2
- Fix issue with generic attributes used just before a pipe table (issue #121)
## 0.12.1
- Fix issue with media links extension when a URL to video is used, an unexpected closing `</iframe>` was inserted (issue #119)
## 0.12.0
- Add new extension JiraLink support (thanks to @clarkd)
- Fix issue in html attributes not parsing correctly properties (thanks to @meziantou)
- Fix issues detected by an automatic static code analysis tool
## 0.11.0
- Fix issue with math extension and $$ block parsing not handling correctly beginning of a $$ as a inline math instead (issue #107)
- Fix issue with custom attributes for emphasis
- Add support for new special custom arrows emoji (`->` `<-` `<->` `<=` `=>` `<==>`)
## 0.10.7
- Fix issue when an url ends by a dot `.`
## 0.10.6
- Fix emphasis with HTML entities
## 0.10.5
- Several minor fixes
## 0.10.4
- Fix issue with autolinks
- Normalize number of columns for tables
## 0.10.3
- Fix issue with pipetables shifting a cell to a new column (issue #73)
## 0.10.2
- Fix exception when trying to urlize an url with an unicode character outside the supported range by NormD (issue #75)
## 0.10.1
- Update to latest CommonMark specs
- Fix source span for LinkReferenceDefinition
## 0.10.0
- Breaking change of the IMarkdownExtension to allow to receive the MarkdownPipeline for the renderers setup
## 0.9.1
- Fix regression bug with conflicts between autolink extension and html inline/regular links
## 0.9.0
- Add new Autolink extension
## 0.8.5
- Allow to force table column alignment to left
## 0.8.4
- Fix issue when calculating the span of an indented code block within a list. Make sure to include first whitespace on the line
## 0.8.3
- fix NullReferenceException with Gridtables extension when a single `+` is entered on a line
## 0.8.2
- fix potential cast exception with Abreviation extension and empty literals
## 0.8.1
- new extension to disable URI escaping for non-US-ASCII characters to workaround a bug in Edge/IE
- Fix an issue with abbreviations with left/right multiple non-punctuation/space characters
## 0.8.0
- Update to latest CommonMark specs
- Fix empty literal
- Add YAML frontmatter extension
## 0.7.5
- several bug fixes (pipe tables, disable HTML, special attributes, inline maths, abbreviations...)
- add support for rowspan in grid tables
## 0.7.4
- Fix bug with strong emphasis starting at the beginning of a line
## 0.7.3
- Fix threading issue with pipeline
## 0.7.2
- Fix rendering of table colspan with non english locale
- Fix grid table colspan parsing
- Add nofollow extension for links
## 0.7.1
- Fix issue in smarty pants which could lead to an InvalidCastException
- Update parsers to latest CommonMark specs
## 0.7.0
- Update to latest NETStandard.Library 1.6.0
- Fix issue with digits in auto-identifier extension
- Fix incorrect start of span calculated for code indented blocks
## 0.6.2
- Handle latest CommonMark specs for corner cases for emphasis (See https://talk.commonmark.org/t/emphasis-strong-emphasis-corner-cases/2123/1 )
## 0.6.1:
- Fix issue with autoidentifier extension overriding manual HTML attributes id on headings
## 0.6.0
- Fix conflicts between PipeTables and SmartyPants extensions
- Add SelfPipeline extension

View File

@@ -1,4 +1,4 @@
Copyright (c) 2016, Alexandre Mutel
Copyright (c) 2018, Alexandre Mutel
All rights reserved.
Redistribution and use in source and binary forms, with or without modification

View File

@@ -1,8 +1,8 @@
# Markdig [![Build status](https://ci.appveyor.com/api/projects/status/hk391x8jcskxt1u8?svg=true)](https://ci.appveyor.com/project/xoofx/markdig) [![NuGet](https://img.shields.io/nuget/v/Markdig.svg)](https://www.nuget.org/packages/Markdig/)
# Markdig [![Build status](https://ci.appveyor.com/api/projects/status/hk391x8jcskxt1u8?svg=true)](https://ci.appveyor.com/project/xoofx/markdig) [![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, powerfull, [CommonMark](http://commonmark.org/) compliant, extensible Markdown processor for .NET.
Markdig is a fast, powerful, [CommonMark](http://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!
@@ -10,54 +10,68 @@ You can **try Markdig online** and compare it to other implementations on [babel
## Features
- **Very fast parser** (no-regexp), very lightweight in terms of GC pressure. See benchmarks
- **Abstract Syntax Tree**
- **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!
- Converter to **HTML**
- Passing more than **600+ tests** from the latest [CommonMark specs](http://spec.commonmark.org/)
- Passing more than **600+ tests** from the latest [CommonMark specs (0.28)](http://spec.commonmark.org/)
- Includes all the core elements of CommonMark:
- including GFM fenced code blocks.
- 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 `@`)
- Built-in with **20+ extensions**, including:
- 2 kind of tables:
- **Pipe tables** (inspired from Github tables and [PanDoc - Pipe Tables](http://pandoc.org/README.html#extension-pipe_tables))
- **Grid tables** (inspired from [Pandoc - Grid Tables](http://pandoc.org/README.html#extension-grid_tables))
- **Extra emphasis** (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](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/))
- strike through `~~`,
- Subscript `~`
- Superscript `^`
- Inserted `++`
- Marked `==`
- **Special attributes** or attached HTML attributes (inspired from [PHP Markdown Extra - Special Attributes](https://michelf.ca/projects/php-markdown/extra/#spe-attr))
- **Definition lists** (inspired from [PHP Markdown Extra - Definitions Lists](https://michelf.ca/projects/php-markdown/extra/#def-list))
- **Footnotes** (inspired from [PHP Markdown Extra - Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes))
- **Auto-identifiers** for headings (similar to [Pandoc - Auto Identifiers](http://pandoc.org/README.html#extension-auto_identifiers))
- **Task Lists** inspired from [Github Task lists](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments).
- **Extra bullet lists**, supporting alpha bullet `a.` `b.` and roman bullet (`i`, `ii`...etc.)
- **Media support** for media url (youtube, vimeo, mp4...etc.) (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/embedded-audio-and-video/441))
- **Abbreviations** (inspired from [PHP Markdown Extra - Abbreviations](https://michelf.ca/projects/php-markdown/extra/#abbr))
- **Citation** text by enclosing `""...""` (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/referencing-creative-works-with-cite/892))
- **Custom containers** similar to fenced code block `:::` for generating a proper `<div>...</div>` instead (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/custom-container-for-block-and-inline/2051))
- **Figures** (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/image-tag-should-expand-to-figure-when-used-with-title/265/5))
- **Footers** (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/syntax-for-footer/2070))
- **Mathematics**/Latex extension by enclosing `$$` for block and `$` for inline math (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/mathematics-extension/457/31))
- **Soft lines as hard lines**
- **Emoji** support (inspired from [Markdown-it](https://markdown-it.github.io/))
- **SmartyPants** (inspired from [Daring Fireball - SmartyPants](https://daringfireball.net/projects/smartypants/))
- **Bootstrap** class (to output bootstrap class)
- [**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-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.)
- [**Media support**](src/Markdig.Tests/Specs/MediaSpecs.md) for media url (youtube, vimeo, mp4...etc.) (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/embedded-audio-and-video/441))
- [**Abbreviations**](src/Markdig.Tests/Specs/AbbreviationSpecs.md) (inspired from [PHP Markdown Extra - Abbreviations](https://michelf.ca/projects/php-markdown/extra/#abbr))
- [**Citation**](src/Markdig.Tests/Specs/FigureFooterAndCiteSpecs.md) text by enclosing `""...""` (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/referencing-creative-works-with-cite/892))
- [**Custom containers**](src/Markdig.Tests/Specs/CustomContainerSpecs.md) similar to fenced code block `:::` for generating a proper `<div>...</div>` instead (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/custom-container-for-block-and-inline/2051))
- [**Figures**](src/Markdig.Tests/Specs/FigureFooterAndCiteSpecs.md) (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/image-tag-should-expand-to-figure-when-used-with-title/265/5))
- [**Footers**](src/Markdig.Tests/Specs/FigureFooterAndCiteSpecs.md) (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/syntax-for-footer/2070))
- [**Mathematics**](src/Markdig.Tests/Specs/MathSpecs.md)/Latex extension by enclosing `$$` for block and `$` for inline math (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/mathematics-extension/457/31))
- [**Soft lines as hard lines**](src/Markdig.Tests/Specs/HardlineBreakSpecs.md)
- [**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)
- [**YAML frontmatter**](src/Markdig.Tests/Specs/YamlSpecs.md) to parse without evaluating the frontmatter and to discard it from the HTML output (typically used for previewing without the frontmatter 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)
- Compatible with .NET 3.5, 4.0+ and .NET Core (`netstandard1.1+`)
### Third Party Extensions
- [**WPF/XAML Markdown Renderer**: `markdig.wpf`](https://github.com/Kryptos-FR/markdig.wpf)
- [**Syntax highlighting**: `Markdig.SyntaxHighlighting`](https://github.com/RichardSlater/Markdig.SyntaxHighlighting)
- [**Embedded C# scripting**: `Markdig.Extensions.ScriptCs`](https://github.com/macaba/Markdig.Extensions.ScriptCs)
## Documentation
> The repository is under construction. There will be a dedicated website and proper documentation at some point!
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/)
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/)
## Download
Markdig is available as a NuGet package: [![NuGet](https://img.shields.io/nuget/v/Markdig.svg)](https://www.nuget.org/packages/Markdig/)
Also [Markdig.Signed](https://www.nuget.org/packages/Markdig.Signed/) NuGet package provides signed assemblies.
## Usage
The main entry point for the API is the `Markdig.Markdown` class:
@@ -69,7 +83,7 @@ var result = Markdown.ToHtml("This is a text with some *emphasis*");
Console.WriteLine(result); // prints: <p>This is a text with some <em>emphasis</em></p>
```
In order to activate most of all advanced extensions (except Emoji, SoftLine as HarLine and SmartyPants)
In order to activate most of all advanced extensions (except Emoji, SoftLine as HardLine, JiraLinks and SmartyPants)
```csharp
// Configure the pipeline with all advanced extensions active
@@ -79,6 +93,10 @@ 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)
## Build
In order to build Markdig, you need to install [.NET Core RTM](https://www.microsoft.com/net/core)
## License
This software is released under the [BSD-Clause 2 license](https://github.com/lunet-io/markdig/blob/master/license.txt).
@@ -110,7 +128,7 @@ This is an early preview of the benchmarking against various implementations:
### Analysis of the results:
- Markdig is roughly **x100 times faster than MarkdownSharp**, **30x times faster than docfx**
- **Among the best in CPU**, Extremelly competitive and often faster than other implementations (not feature wise equivalent)
- **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:
@@ -168,6 +186,12 @@ WarmupCount=2 TargetCount=10
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)
## 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!
@@ -178,6 +202,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!
## Author
Alexandre MUTEL aka [xoofx](http://xoofx.com)

View File

@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Markdig.Benchmarks</RootNamespace>
<AssemblyName>Markdig.Benchmarks</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(NuGetPackageRoot)' == ''">
<NuGetPackageRoot>$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
</PropertyGroup>
<ImportGroup>
<Import Project="$(NuGetPackageRoot)\Microsoft.Diagnostics.Tracing.TraceEvent\1.0.41\build\Microsoft.Diagnostics.Tracing.TraceEvent.targets" Condition="Exists('$(NuGetPackageRoot)\Microsoft.Diagnostics.Tracing.TraceEvent\1.0.41\build\Microsoft.Diagnostics.Tracing.TraceEvent.targets')" />
</ImportGroup>
</Project>

View File

@@ -131,7 +131,7 @@ namespace Testamina.Markdig.Benchmarks
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(new Job { Mode = Mode.Throughput, LaunchCount = 2, WarmupCount = 2, TargetCount = 10 });
//config.Add(gcDiagnoser);
//var config = DefaultConfig.Instance;

View File

@@ -1,3 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/></startup></configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>

View File

@@ -4,7 +4,7 @@
"win-x64": {}
},
"frameworks": {
"net451": {
"net46": {
"compilationOptions": {
"define": [
"CLASSIC"
@@ -19,9 +19,9 @@
}
},
"dependencies": {
"BenchmarkDotNet": "0.9.6",
"BenchmarkDotNet.Diagnostics.Windows": "0.9.6",
"CommonMark.NET": "0.11.0",
"BenchmarkDotNet": "0.10.6",
"BenchmarkDotNet.Diagnostics.Windows": "0.10.6",
"CommonMark.NET": "0.15.1",
"MarkdownSharp": "1.13.0.0",
"Microsoft.Diagnostics.Runtime": "0.8.31-beta",
"Microsoft.Diagnostics.Tracing.TraceEvent": "1.0.41.0"

View File

@@ -24,6 +24,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -38,10 +39,6 @@
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="Markdig">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Markdig\Bin\$(Configuration)\net40\Markdig.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
@@ -59,9 +56,18 @@
<DesignTime>True</DesignTime>
<DependentUpon>Specs.tt</DependentUpon>
</Compile>
<Compile Include="Specs\TestEmphasisPlus.cs" />
<Compile Include="TestConfigureNewLine.cs" />
<Compile Include="TestHtmlAttributes.cs" />
<Compile Include="TestHtmlHelper.cs" />
<Compile Include="TestLineReader.cs" />
<Compile Include="TestLinkHelper.cs" />
<Compile Include="TestNormalize.cs" />
<Compile Include="TestOrderedList.cs" />
<Compile Include="TestPlainText.cs" />
<Compile Include="TestPragmaLines.cs" />
<Compile Include="TestLinkRewriter.cs" />
<Compile Include="TestRelativeUrlReplacement.cs" />
<Compile Include="TestSourcePosition.cs" />
<Compile Include="TestStringSliceList.cs" />
<Compile Include="TestPlayParser.cs" />
@@ -71,6 +77,9 @@
<ItemGroup>
<None Include="App.config" />
<None Include="project.json" />
<None Include="Specs\GlobalizationSpecs.md" />
<None Include="Specs\JiraLinks.md" />
<None Include="Specs\AutoLinks.md" />
<None Include="Specs\AutoIdentifierSpecs.md" />
<None Include="Specs\AbbreviationSpecs.md" />
<None Include="Specs\FigureFooterAndCiteSpecs.md" />
@@ -83,6 +92,10 @@
<None Include="Specs\GridTableSpecs.md" />
<None Include="Specs\HardlineBreakSpecs.md" />
<None Include="Specs\BootstrapSpecs.md" />
<None Include="Specs\DiagramsSpecs.md" />
<None Include="Specs\NoHtmlSpecs.md" />
<None Include="Specs\readme.md" />
<None Include="Specs\YamlSpecs.md" />
<None Include="Specs\TaskListSpecs.md" />
<None Include="Specs\SmartyPantsSpecs.md" />
<None Include="Specs\MediaSpecs.md" />
@@ -98,6 +111,13 @@
</ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Markdig\Markdig.csproj">
<Project>{8a58a7e2-627c-4f41-933f-5ac92adfab48}</Project>
<Name>Markdig</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@@ -45,3 +45,56 @@ This is a 😃 HTML document
.
<p>This is a <abbr title="Hypertext Markup Language">😃 HTML</abbr> document</p>
````````````````````````````````
Abbreviations may be similar:
```````````````````````````````` example
*[1A]: First
*[1A1]: Second
*[1A2]: Third
We 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 should match whole word only:
```````````````````````````````` example
*[1A]: First
We should not abbreviate 1.1A or 11A!
.
<p>We should not abbreviate 1.1A or 11A!</p>
````````````````````````````````
Abbreviations should match whole word only, even if the word is the entire content:
```````````````````````````````` example
*[1A]: First
1.1A
.
<p>1.1A</p>
````````````````````````````````
Abbreviations should match whole word only, even if there is another glossary term:
```````````````````````````````` example
*[SCO]: First
*[SCOM]: Second
SCOM
.
<p><abbr title="Second">SCOM</abbr></p>
````````````````````````````````
Abbreviations should only match when surrounded by whitespace:
```````````````````````````````` example
*[PR]: Pull Request
PRAA
.
<p>PRAA</p>
````````````````````````````````

View File

@@ -12,7 +12,7 @@ Allows to automatically creates an identifier for a heading:
<h1 id="this-is-a-heading">This is a heading</h1>
````````````````````````````````
Only punctuation `-`, `_` and `.` is kept, all over non letter characters are discarded.
Only punctuation `-`, `_` and `.` is kept, all other non letter characters are discarded.
Consecutive same character `-`, `_` or `.` are rendered into a single one
Characters `-`, `_` and `.` at the end of the string are also discarded.
@@ -96,3 +96,14 @@ The text of the link can be changed:
<p><a href="#this-is-a-heading">With a new text</a></p>
<h1 id="this-is-a-heading">This is a heading</h1>
````````````````````````````````
An autoidentifier should not conflict with an existing link:
```````````````````````````````` example
![scenario image][scenario]
## Scenario
[scenario]: ./scenario.png
.
<p><img src="./scenario.png" alt="scenario image" /></p>
<h2 id="scenario">Scenario</h2>
````````````````````````````````

View File

@@ -0,0 +1,242 @@
# Extensions
This section describes the different extensions supported:
## AutoLinks
Autolinks will format as a HTML link any string that starts by:
- `http://` or `https://`
- `ftp://`
- `mailto:`
- `www.`
```````````````````````````````` example
This is a http://www.google.com URL and https://www.google.com
This is a ftp://test.com
And a mailto:email@toto.com
And 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>
This is a <a href="ftp://test.com">ftp://test.com</a>
And a <a href="mailto:email@toto.com">email@toto.com</a>
And a plain <a href="http://www.google.com">www.google.com</a></p>
````````````````````````````````
But incomplete links will not be matched:
```````````````````````````````` example
This is not a http:/www.google.com URL and https:/www.google.com
This is not a ftp:/test.com
And not a mailto:emailtoto.com
And not a plain www. or a www.x
.
<p>This is not a http:/www.google.com URL and https:/www.google.com
This is not a ftp:/test.com
And not a mailto:emailtoto.com
And not a plain www. or a www.x</p>
````````````````````````````````
Previous character must be a punctuation or a valid space (tab, space, new line):
```````````````````````````````` example
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>
````````````````````````````````
An autolink should not interfere with an `<a>` HTML inline:
```````````````````````````````` example
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>
````````````````````````````````
or even within emphasis:
```````````````````````````````` example
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>
````````````````````````````````
An autolink should not interfere with a markdown link:
```````````````````````````````` example
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>
````````````````````````````````
A link embraced by pending emphasis should let the emphasis takes precedence if characters are placed at the end of the matched link:
```````````````````````````````` example
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>
````````````````````````````````
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):
```````````````````````````````` example
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>
````````````````````````````````
### GFM Support
Extract from [GFM Autolinks extensions specs](https://github.github.com/gfm/#autolinks-extension-)
```````````````````````````````` example
www.commonmark.org
.
<p><a href="http://www.commonmark.org">www.commonmark.org</a></p>
````````````````````````````````
```````````````````````````````` example
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>
````````````````````````````````
```````````````````````````````` example
Visit www.commonmark.org.
Visit www.commonmark.org/a.b.
.
<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>
````````````````````````````````
```````````````````````````````` example
www.google.com/search?q=Markup+(business)
(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>
<p>(<a href="http://www.google.com/search?q=Markup+(business)">www.google.com/search?q=Markup+(business)</a>)</p>
````````````````````````````````
```````````````````````````````` example
www.google.com/search?q=commonmark&hl=en
www.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>
<p><a href="http://www.google.com/search?q=commonmark">www.google.com/search?q=commonmark</a>&amp;hl;</p>
````````````````````````````````
```````````````````````````````` example
www.commonmark.org/he<lp
.
<p><a href="http://www.commonmark.org/he">www.commonmark.org/he</a>&lt;lp</p>
````````````````````````````````
```````````````````````````````` example
http://commonmark.org
(Visit https://encrypted.google.com/search?q=Markup+(business))
Anonymous FTP is available at ftp://foo.bar.baz.
.
<p><a href="http://commonmark.org">http://commonmark.org</a></p>
<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>
````````````````````````````````
### Valid Domain Tests
Domain names that have empty segments won't be matched
```````````````````````````````` example
www..
www..com
http://test.
http://.test
http://.
http://..
ftp://test.
ftp://.test
mailto:email@test.
mailto:email@.test
.
<p>www..
www..com
http://test.
http://.test
http://.
http://..
ftp://test.
ftp://.test
mailto:email@test.
mailto:email@.test</p>
````````````````````````````````
Domain names with too few segments won't be matched
```````````````````````````````` example
www
www.com
http://test
ftp://test
mailto:email@test
.
<p>www
www.com
http://test
ftp://test
mailto:email@test</p>
````````````````````````````````
Domain names that contain an underscores in the last two segments won't be matched
```````````````````````````````` example
www._test.foo.bar is okay, but www._test.foo is not
http://te_st.foo.bar is okay, as is http://test.foo_.bar.foo
But http://te_st.foo, http://test.foo_.bar and http://test._foo are not
ftp://test_.foo.bar is okay, but ftp://test.fo_o is not
mailto: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>
<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>
<p>But http://te_st.foo, http://test.foo_.bar and http://test._foo are not</p>
<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>
````````````````````````````````
Domain names that contain invalid characters (not AlphaNumberic, -, _ or .) won't be matched
```````````````````````````````` example
https://[your-domain]/api
.
<p>https://[your-domain]/api</p>
````````````````````````````````
Domain names followed by ?, : or # instead of / are matched
```````````````````````````````` example
https://github.com?
https://github.com?a
https://github.com#a
https://github.com:
https://github.com:443
.
<p><a href="https://github.com">https://github.com</a>?</p>
<p><a href="https://github.com?a">https://github.com?a</a></p>
<p><a href="https://github.com#a">https://github.com#a</a></p>
<p><a href="https://github.com">https://github.com</a>:</p>
<p><a href="https://github.com:443">https://github.com:443</a></p>
````````````````````````````````

View File

@@ -4,7 +4,7 @@ This section describes the different extensions supported:
## Custom Container
A custom container is similar to a fenced code block, but it is using the character `:` to declare a block (with at least 3 characters), and instead of generating a `<pre><code>...</code></pre>` it will generate a `<div>...</dib>` block.
A custom container is similar to a fenced code block, but it is using the character `:` to declare a block (with at least 3 characters), and instead of generating a `<pre><code>...</code></pre>` it will generate a `<div>...</div>` block.
```````````````````````````````` example
:::spoiler
@@ -109,4 +109,4 @@ Attributes can be attached to a inline custom container:
This is a text ::with special emphasis::{#myId .myemphasis}
.
<p>This is a text <span id="myId" class="myemphasis">with special emphasis</span></p>
````````````````````````````````
````````````````````````````````

View File

@@ -105,3 +105,28 @@ Term 1
<pre><code>: Not valid
</code></pre>
````````````````````````````````
Definition lists can be nested inside list items
```````````````````````````````` example
1. First
2. Second
Term 1
: Definition
Term 2
: Second Definition
.
<ol>
<li><p>First</p></li>
<li><p>Second</p>
<dl>
<dt>Term 1</dt>
<dd>Definition</dd>
<dt>Term 2</dt>
<dd>Second Definition</dd>
</dl></li>
</ol>
````````````````````````````````

View File

@@ -0,0 +1,57 @@
# Extensions
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:
```````````````````````````````` example
```mermaid
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```
.
<div class="mermaid">graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
</div>
````````````````````````````````
## nomnoml diagrams
Using a fenced code block with the `nomnoml` language info will output a `<div class='nomnoml'>` instead of a `pre/code` block:
```````````````````````````````` example
```nomnoml
[example|
propertyA: Int
propertyB: string
|
methodA()
methodB()
|
[subA]--[subB]
[subA]-:>[sub C]
]
```
.
<div class="nomnoml">[example|
propertyA: Int
propertyB: string
|
methodA()
methodB()
|
[subA]--[subB]
[subA]-:>[sub C]
]
</div>
````````````````````````````````
TODO: Add other text diagram languages

View File

@@ -11,3 +11,29 @@ This is a test with a :) and a :angry: smiley
.
<p>This is a test with a 😃 and a 😠 smiley</p>
````````````````````````````````
An emoji needs to be preceded by a space:
```````````````````````````````` example
These are not:) an emoji with a:) x:angry:x
.
<p>These are not:) an emoji with a:) x:angry:x</p>
````````````````````````````````
Emoji can be followed by close ponctuation (or any other characters):
```````````````````````````````` example
We all need :), it makes us :muscle:. (and :ok_hand:).
.
<p>We all need 😃, it makes us 💪. (and 👌).</p>
````````````````````````````````
Sentences can end with Emoji:
```````````````````````````````` example
This is a sentence :ok_hand:
and keeps going to the next line :)
.
<p>This is a sentence 👌
and keeps going to the next line 😃</p>
````````````````````````````````

View File

@@ -21,6 +21,17 @@ 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>
````````````````````````````````
Certain punctuation characters are exempted from the rule forbidding them within inline delimiters
```````````````````````````````` example
One quintillionth can be expressed as 10^-18^
Daggers^†^ and double-daggers^‡^ can be used to denote notes.
.
<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>
````````````````````````````````
## Inserted
@@ -41,3 +52,16 @@ 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
```````````````````````````````` 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;^
.
<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>
````````````````````````````````

View File

@@ -2,7 +2,7 @@
This section describes the different extensions supported:
## Footontes
## Footnotes
Allows footnotes using the following syntax (taken from pandoc example):
@@ -61,3 +61,81 @@ multi-paragraph list items.<a href="#fnref:3" class="footnote-back-ref">&#8617;<
</div>
````````````````````````````````
Check with mulitple consecutive footnotes:
```````````````````````````````` example
Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].
[^1]: Footnote 1 text
[^2]: Footnote 2 text
a
[^3]: Footnote 3 text
[^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>
<p>a</p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:1">
<p>Footnote 1 text<a href="#fnref:1" class="footnote-back-ref">&#8617;</a></p></li>
<li id="fn:2">
<p>Footnote 2 text<a href="#fnref:2" class="footnote-back-ref">&#8617;</a></p></li>
<li id="fn:3">
<p>Footnote 3 text<a href="#fnref:3" class="footnote-back-ref">&#8617;</a></p></li>
<li id="fn:4">
<p>Footnote 4 text<a href="#fnref:4" class="footnote-back-ref">&#8617;</a></p></li>
</ol>
</div>
````````````````````````````````
Another test with consecutive footnotes without a blank line separator:
```````````````````````````````` example
Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].
[^1]: Footnote 1 text
[^2]: Footnote 2 text
[^3]: Footnote 3 text
[^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>
<div class="footnotes">
<hr />
<ol>
<li id="fn:1">
<p>Footnote 1 text<a href="#fnref:1" class="footnote-back-ref">&#8617;</a></p></li>
<li id="fn:2">
<p>Footnote 2 text<a href="#fnref:2" class="footnote-back-ref">&#8617;</a></p></li>
<li id="fn:3">
<p>Footnote 3 text<a href="#fnref:3" class="footnote-back-ref">&#8617;</a></p></li>
<li id="fn:4">
<p>Footnote 4 text<a href="#fnref:4" class="footnote-back-ref">&#8617;</a></p></li>
</ol>
</div>
````````````````````````````````
A footnote link inside a list should work as well:
```````````````````````````````` example
- abc
- def[^1]
[^1]: Here is the footnote.
.
<ul>
<li>abc</li>
<li>def<a id="fnref:1" href="#fn:1" class="footnote-ref"><sup>1</sup></a></li>
</ul>
<div class="footnotes">
<hr />
<ol>
<li id="fn:1">
<p>Here is the footnote.<a href="#fnref:1" class="footnote-back-ref">&#8617;</a></p></li>
</ol>
</div>
````````````````````````````````

View File

@@ -27,13 +27,13 @@ The following shows that attributes is attached to the current block or the prev
This is a heading{#heading-link2}
-----------------
This is a paragraph with an attached attributes {#myparagraph attached-bool-property}
This 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>
<h1 id="heading-link2">This is a heading</h1>
<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>
<h2 id="heading-link2">This is a heading</h2>
<p id="myparagraph" attached-bool-property>This is a paragraph with an attached attributes </p>
<p id="myparagraph" attached-bool-property attached-bool-property2>This is a paragraph with an attached attributes </p>
````````````````````````````````
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):

View File

@@ -0,0 +1,189 @@
# Extensions
This section describes the different extensions supported:
## Globalization
Adds support for RTL content by adding `dir="rtl"` and `align="right` attributes to the appropriate html elements. Left to right text is not affected by this extension.
Whether a markdown block is marked as RTL or not is determined by the [first strong character](https://en.wikipedia.org/wiki/Bi-directional_text#Strong_characters) of the block.
**Note**: You might need to add `<meta charset="UTF-8">` to the head of the html file to be able to see the result correctly.
Headings and block quotes:
```````````````````````````````` example
# Fruits
In 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.
> Fruits are good for health
-- Anonymous
# میوە
[میوە](https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95) یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو
> میوە بۆ تەندروستی باشە
-- نەزانراو
.
<h1 id="fruits">Fruits</h1>
<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>
<blockquote>
<p>Fruits are good for health
-- Anonymous</p>
</blockquote>
<h1 id="section" dir="rtl">میوە</h1>
<p dir="rtl"><a href="https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95" dir="rtl">میوە</a> یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو</p>
<blockquote dir="rtl">
<p dir="rtl">میوە بۆ تەندروستی باشە
-- نەزانراو</p>
</blockquote>
````````````````````````````````
Lists:
```````````````````````````````` example
## Types of fruits
- Berries
- Strawberry
- kiwifruit
- Citrus
- Orange
- Lemon
## Examples of fruits :yum:
1. Apple
2. Banana
3. Orange
## Grocery List
- [X] 􏿽 Watermelon
- [X] Apricot
- [ ] Fig
## نموونەی میوە :yum:
1. ? سێو
2. 5 مۆز
3. 􏿽 پرتەقاڵ
## جۆرەکانی میوە
- توو
- فڕاولە
- کیوی
- مزرەمەنی
- پڕتەقاڵ
- لیمۆ
## لیستی کڕین
- [X] شووتی
- [X] قەیسی
- [ ] هەنجیر
.
<h2 id="types-of-fruits">Types of fruits</h2>
<ul>
<li>Berries
<ul>
<li>Strawberry</li>
<li>kiwifruit</li>
</ul>
</li>
<li>Citrus
<ul>
<li>Orange</li>
<li>Lemon</li>
</ul>
</li>
</ul>
<h2 id="examples-of-fruits">Examples of fruits 😋</h2>
<ol>
<li>Apple</li>
<li>Banana</li>
<li>Orange</li>
</ol>
<h2 id="grocery-list">Grocery List</h2>
<ul class="contains-task-list">
<li class="task-list-item"><input disabled="disabled" type="checkbox" checked="checked" /> 􏿽 Watermelon</li>
<li class="task-list-item"><input disabled="disabled" type="checkbox" checked="checked" /> Apricot</li>
<li class="task-list-item"><input disabled="disabled" type="checkbox" /> Fig</li>
</ul>
<h2 id="section" dir="rtl">نموونەی میوە 😋</h2>
<ol dir="rtl">
<li>? سێو</li>
<li>5 مۆز</li>
<li>􏿽 پرتەقاڵ</li>
</ol>
<h2 id="section-1" dir="rtl">جۆرەکانی میوە</h2>
<ul dir="rtl">
<li>توو
<ul dir="rtl">
<li>فڕاولە</li>
<li>کیوی</li>
</ul>
</li>
<li>مزرەمەنی
<ul dir="rtl">
<li>پڕتەقاڵ</li>
<li>لیمۆ</li>
</ul>
</li>
</ul>
<h2 id="section-2" dir="rtl">لیستی کڕین</h2>
<ul class="contains-task-list" dir="rtl">
<li class="task-list-item"><input disabled="disabled" type="checkbox" checked="checked" /> شووتی</li>
<li class="task-list-item"><input disabled="disabled" type="checkbox" checked="checked" /> قەیسی</li>
<li class="task-list-item"><input disabled="disabled" type="checkbox" /> هەنجیر</li>
</ul>
````````````````````````````````
Tables:
```````````````````````````````` example
Nuitrion |Apple | Oranges
--|-- | --
Calories|52|47
Sugar|10g|9g
پێکهاتە |سێو | پڕتەقاڵ
--|-- | --
کالۆری|٥٢|٤٧
شەکر| ١٠گ|٩گ
.
<table>
<thead>
<tr>
<th>Nuitrion</th>
<th>Apple</th>
<th>Oranges</th>
</tr>
</thead>
<tbody>
<tr>
<td>Calories</td>
<td>52</td>
<td>47</td>
</tr>
<tr>
<td>Sugar</td>
<td>10g</td>
<td>9g</td>
</tr>
</tbody>
</table>
<table dir="rtl" align="right">
<thead>
<tr>
<th>پێکهاتە</th>
<th>سێو</th>
<th>پڕتەقاڵ</th>
</tr>
</thead>
<tbody>
<tr>
<td>کالۆری</td>
<td>٥٢</td>
<td>٤٧</td>
</tr>
<tr>
<td>شەکر</td>
<td>١٠گ</td>
<td>٩گ</td>
</tr>
</tbody>
</table>
````````````````````````````````

View File

@@ -70,8 +70,8 @@ A regular row can continue a previous regular row when column separator `|` are
+---------+---------+---------+
| Col1 | Col2 | Col3 |
| Col1a | Col2a | Col3a |
| Col12 | Col3b |
| Col123 |
| Col1b | Col3b |
| Col1c |
.
<table>
<col style="width:33.33%">
@@ -87,11 +87,11 @@ Col2a</td>
Col3a</td>
</tr>
<tr>
<td colspan="2">Col12</td>
<td></td>
<td colspan="2">Col1b</td>
<td>Col3b</td>
</tr>
<tr>
<td colspan="3">Col123</td>
<td colspan="3">Col1c</td>
</tr>
</tbody>
</table>
@@ -184,3 +184,103 @@ Alignment might be specified on the first row using the character `:`:
</tbody>
</table>
````````````````````````````````
A grid table may have cells spanning both columns and rows:
```````````````````````````````` example
+---+---+---+
| AAAAA | B |
+---+---+ B +
| D | E | B |
+ D +---+---+
| D | CCCCC |
+---+---+---+
.
<table>
<col style="width:33.33%">
<col style="width:33.33%">
<col style="width:33.33%">
<tbody>
<tr>
<td colspan="2">AAAAA</td>
<td rowspan="2">B
B
B</td>
</tr>
<tr>
<td rowspan="2">D
D
D</td>
<td>E</td>
</tr>
<tr>
<td colspan="2">CCCCC</td>
</tr>
</tbody>
</table>
````````````````````````````````
A grid table may have cells with both colspan and rowspan:
```````````````````````````````` example
+---+---+---+
| AAAAA | B |
+ AAAAA +---+
| AAAAA | C |
+---+---+---+
| D | E | F |
+---+---+---+
.
<table>
<col style="width:33.33%">
<col style="width:33.33%">
<col style="width:33.33%">
<tbody>
<tr>
<td colspan="2" rowspan="2">AAAAA
AAAAA
AAAAA</td>
<td>B</td>
</tr>
<tr>
<td>C</td>
</tr>
<tr>
<td>D</td>
<td>E</td>
<td>F</td>
</tr>
</tbody>
</table>
````````````````````````````````
A grid table may not have irregularly shaped cells:
```````````````````````````````` example
+---+---+---+
| AAAAA | B |
+ A +---+ B +
| A | C | B |
+---+---+---+
| DDDDD | E |
+---+---+---+
.
<p>+---+---+---+
| AAAAA | B |
+ A +---+ B +
| A | C | B |
+---+---+---+
| DDDDD | E |
+---+---+---+</p>
````````````````````````````````
An empty `+` on a line should result in a simple empty list output:
```````````````````````````````` example
+
.
<ul>
<li></li>
</ul>
````````````````````````````````

View File

@@ -0,0 +1,77 @@
## Jira Links
The JiraLinks extension will automatically add links to JIRA issue items within your markdown, e.g. XX-1234. For this to happen, you must configure the extension when adding to the pipeline, e.g.
```
var pipeline = new MarkdownPipelineBuilder()
.UseJiraLinks(new JiraLinkOptions("http://your.company.abc"))
.Build();
```
The rules for detecting a link are:
- The project key must be composed of onre or more capitalised ASCII letter `[A-Z]+`
- A single hypen `-` must separate the project key and issue number.
- The issue number is composed of 1 or more digits `[0, 9]+`
- The reference must be preceeded by either `(` or whitespace or EOF.
- The reference must be followed by either `)` or whitespace or EOF.
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>
````````````````````````````````
```````````````````````````````` 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>
````````````````````````````````
```````````````````````````````` 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>
````````````````````````````````
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>
````````````````````````````````
```````````````````````````````` 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>
````````````````````````````````
```````````````````````````````` 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>
````````````````````````````````
These are not valid links:
```````````````````````````````` example
This is not aJIRA-123 issue
.
<p>This is not aJIRA-123 issue</p>
````````````````````````````````
```````````````````````````````` example
This is not JIRA-123a issue
.
<p>This is not JIRA-123a issue</p>
````````````````````````````````
```````````````````````````````` example
This is not JIRA- issue
.
<p>This is not JIRA- issue</p>
````````````````````````````````

View File

@@ -38,7 +38,7 @@ Like for numbered list, a list can start with a different letter
b. First item
c. Second item
.
<ol type="a" start="b">
<ol type="a" start="2">
<li>First item</li>
<li>Second item</li>
</ol>
@@ -100,8 +100,26 @@ Like for numbered list, a list can start with a different letter
ii. First item
iii. Second item
.
<ol type="i" start="ii">
<ol type="i" start="2">
<li>First item</li>
<li>Second item</li>
</ol>
````````````````````````````````
Lists can be restarted, specifying the start point.
```````````````````````````````` example
1. First item
Some text
2. Second item
.
<ol>
<li>First item</li>
</ol>
<p>Some text</p>
<ol start="2">
<li>Second item</li>
</ol>
````````````````````````````````

View File

@@ -20,12 +20,47 @@ This is a $$math block$$
<p>This is a <span class="math">math block</span></p>
````````````````````````````````
The opening `$` and closing `$` is following the rules of the emphasis delimiter `_`:
Newlines inside an inline math are not allowed:
```````````````````````````````` example
This is not a $ math block $
This is not a $$math
block$$ and? this is a $$math block$$
.
<p>This is not a $ math block $</p>
<p>This is not a $$math
block$$ and? this is a <span class="math">math block</span></p>
````````````````````````````````
```````````````````````````````` example
This is not a $math
block$ and? this is a $math block$
.
<p>This is not a $math
block$ and? this is a <span class="math">math block</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 $
.
<p>This is a <span class="math">math block</span></p>
````````````````````````````````
```````````````````````````````` example
This is a $ math block $ after
.
<p>This is a <span class="math">math block</span> after</p>
````````````````````````````````
```````````````````````````````` example
This is a $$ math block $$ after
.
<p>This is a <span class="math">math block</span> after</p>
````````````````````````````````
```````````````````````````````` example
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>
````````````````````````````````
For the opening `$` it requires a space or a punctuation before (but cannot be used within a word):
@@ -68,6 +103,13 @@ This is a $$$math block$$$
<p>This is a <span class="math">$math block$</span></p>
````````````````````````````````
Regular text can come both before and after the math inline
```````````````````````````````` example
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>
````````````````````````````````
A mathematic block takes precedence over standard emphasis `*` `_`:
```````````````````````````````` example
@@ -75,6 +117,13 @@ This is *a $math* block$
.
<p>This is *a <span class="math">math* block</span></p>
````````````````````````````````
An opening $$ at the beginning of a line should not be interpreted as a Math block:
```````````````````````````````` example
$$ math $$ starting at a line
.
<p><span class="math">math</span> starting at a line</p>
````````````````````````````````
## Math Block

View File

@@ -10,7 +10,16 @@ Allows to embed audio/video links to popular website:
![Video1](https://www.youtube.com/watch?v=mswPy5bt3TQ)
![Video2](https://vimeo.com/8607834)
![Video3](https://sample.com/video.mp4)
![Audio4](https://music.yandex.ru/album/411845/track/4402274)
![Video5](https://ok.ru/video/26870090463)
.
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" width="500" height="281" frameborder="0" allowfullscreen></iframe></p>
<p><iframe src="https://player.vimeo.com/video/8607834" width="500" height="281" frameborder="0" allowfullscreen></iframe></p>
<p><video width="500" height="281" controls><source type="video/mp4" src="https://sample.com/video.mp4"></source></video></p>
<p><iframe src="https://music.yandex.ru/iframe/#track/4402274/411845/" width="500" height="281" frameborder="0"></iframe></p>
<p><iframe src="https://ok.ru/videoembed/26870090463" width="500" height="281" frameborder="0" allowfullscreen></iframe></p>
````````````````````````````````

View File

@@ -0,0 +1,27 @@
# Extensions
## NoHTML
The extension DisableHtml allows to disable the parsing of HTML:
For inline HTML:
```````````````````````````````` example
this is some text</td></tr>
.
<p>this is some text&lt;/td&gt;&lt;/tr&gt;</p>
````````````````````````````````
For Block HTML:
```````````````````````````````` example
<div>
this is some text
</div>
.
<p>&lt;div&gt;
this is some text
&lt;/div&gt;</p>
````````````````````````````````

View File

@@ -122,7 +122,7 @@ c no d
c no d</p>
````````````````````````````````
The number of columns in the first row determine the number of columns for the whole table. Any extra columns delimiter `|` for sub-sequent lines are converted to literal strings instead:
If a row contains more column than the header row, it will still be added as a column:
```````````````````````````````` example
a | b
@@ -136,19 +136,24 @@ a | b
<tr>
<th>a</th>
<th>b</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>1 | 2</td>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>4</td>
<td></td>
</tr>
<tr>
<td>5</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
@@ -214,6 +219,76 @@ Column delimiters `|` at the very beginning of a line or just before a line endi
</tbody>
</table>
````````````````````````````````
A pipe may be present at both the beginning/ending of each line:
```````````````````````````````` example
|a|b|
|-|-|
|0|1|
.
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>1</td>
</tr>
</tbody>
</table>
````````````````````````````````
Or may be ommitted on one side:
```````````````````````````````` example
a|b|
-|-|
0|1|
.
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>1</td>
</tr>
</tbody>
</table>
````````````````````````````````
```````````````````````````````` example
|a|b
|-|-
|0|1
.
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>1</td>
</tr>
</tbody>
</table>
````````````````````````````````
Single column table can be declared with lines starting only by a column delimiter:
```````````````````````````````` example
@@ -275,7 +350,8 @@ The first row is considered as a **header row** if it is separated from the regu
</table>
````````````````````````````````
The text alignment is defined by default to be left.
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.
There is no way to define a different alignment for heading and cells (apart from the default).
The text alignment can be changed by using the character `:` with the header column separator:
```````````````````````````````` example
@@ -287,19 +363,19 @@ The text alignment can be changed by using the character `:` with the header col
<table>
<thead>
<tr>
<th>a</th>
<th style="text-align: left;">a</th>
<th style="text-align: center;">b</th>
<th style="text-align: right;">c</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td style="text-align: left;">0</td>
<td style="text-align: center;">1</td>
<td style="text-align: right;">2</td>
</tr>
<tr>
<td>3</td>
<td style="text-align: left;">3</td>
<td style="text-align: center;">4</td>
<td style="text-align: right;">5</td>
</tr>
@@ -307,6 +383,31 @@ The text alignment can be changed by using the character `:` with the header col
</table>
````````````````````````````````
Test alignment with starting and ending pipes:
```````````````````````````````` example
| abc | def | ghi |
|:---:|-----|----:|
| 1 | 2 | 3 |
.
<table>
<thead>
<tr>
<th style="text-align: center;">abc</th>
<th>def</th>
<th style="text-align: right;">ghi</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">1</td>
<td>2</td>
<td style="text-align: right;">3</td>
</tr>
</tbody>
</table>
````````````````````````````````
The following example shows a non matching header column separator:
```````````````````````````````` example
@@ -412,3 +513,103 @@ a | b
</table>
````````````````````````````````
** Rule #9**
It is possible to have a single row header only:
```````````````````````````````` example
a | b
-- | --
.
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
</table>
````````````````````````````````
```````````````````````````````` example
|a|b|c
|---|---|---|
.
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
<th>c</th>
</tr>
</thead>
</table>
````````````````````````````````
** Tests **
Tests trailing spaces after pipes
```````````````````````````````` example
| abc | def |
|---|---|
| cde| ddd|
| eee| fff|
| fff | fffff |
|gggg | ffff |
.
<table>
<thead>
<tr>
<th>abc</th>
<th>def</th>
</tr>
</thead>
<tbody>
<tr>
<td>cde</td>
<td>ddd</td>
</tr>
<tr>
<td>eee</td>
<td>fff</td>
</tr>
<tr>
<td>fff</td>
<td>fffff</td>
</tr>
<tr>
<td>gggg</td>
<td>ffff</td>
</tr>
</tbody>
</table>
````````````````````````````````
** Normalized columns count **
The tables are normalized to the maximum number of columns found in a table
```````````````````````````````` example
a | b
-- | -
0 | 1 | 2
.
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>1</td>
<td>2</td>
</tr>
</tbody>
</table>
````````````````````````````````

View File

@@ -83,29 +83,6 @@ They are' not matching 'quotes
.
<p>They are' not matching 'quotes</p>
````````````````````````````````
Double quotes using ``` `` ``` are working if they match another `''` pair, and there is no other double quotes on the line (otherwise they would be parsed as a code span):
```````````````````````````````` example
This is ``a double quote''
.
<p>This is &ldquo;a double quote&rdquo;</p>
````````````````````````````````
```````````````````````````````` example
This is ``a code span''``
.
<p>This is <code>a code span''</code></p>
````````````````````````````````
```````````````````````````````` example
hello ``there```
test
.
<p>hello &ldquo;there&rdquo;`
test</p>
````````````````````````````````
An emphasis starting inside left/right quotes will span over the right quote:
```````````````````````````````` example
@@ -133,3 +110,36 @@ This is a en ellipsis...
.
<p>This is a en ellipsis&hellip;</p>
````````````````````````````````
Check that a smartypants are not breaking pipetable parsing:
```````````````````````````````` example
a | b
-- | --
0 | 1
.
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>1</td>
</tr>
</tbody>
</table>
````````````````````````````````
Check quotes and dash:
```````````````````````````````` example
A "quote" with a ---
.
<p>A &ldquo;quote&rdquo; with a &mdash;</p>
````````````````````````````````

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
<#/*
<#/*
Copyright (c) Alexandre Mutel. All rights reserved.
This file is licensed under the BSD-Clause 2 license.
This file is licensed under the BSD-Clause 2 license.
See the license.txt file in the project root for more information.The MIT License (MIT)
This is a modified version of code released previously with the following license:
@@ -36,31 +36,39 @@ SOFTWARE.
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.CodeDom" #>
<#@ import namespace="System.CodeDom.Compiler" #>
<#@ output extension=".cs" #><#
<#@ output extension=".cs" encoding="utf-8"#>
<#
var specFiles = new KeyValuePair<string, string>[] {
new KeyValuePair<string, string>("https://raw.githubusercontent.com/jgm/CommonMark/master/spec.txt", string.Empty),
new KeyValuePair<string, string>(Host.ResolvePath("PipeTableSpecs.md"), "pipetables"),
new KeyValuePair<string, string>(Host.ResolvePath("FootnotesSpecs.md"), "footnotes"),
new KeyValuePair<string, string>(Host.ResolvePath("GenericAttributesSpecs.md"), "attributes"),
new KeyValuePair<string, string>(Host.ResolvePath("EmphasisExtraSpecs.md"), "emphasisextras"),
new KeyValuePair<string, string>(Host.ResolvePath("HardlineBreakSpecs.md"), "hardlinebreak"),
new KeyValuePair<string, string>(Host.ResolvePath("GridTableSpecs.md"), "gridtables"),
new KeyValuePair<string, string>(Host.ResolvePath("CustomContainerSpecs.md"), "customcontainers+attributes"),
new KeyValuePair<string, string>(Host.ResolvePath("DefinitionListSpecs.md"), "definitionlists+attributes"),
new KeyValuePair<string, string>(Host.ResolvePath("EmojiSpecs.md"), "emojis"),
new KeyValuePair<string, string>(Host.ResolvePath("AbbreviationSpecs.md"), "abbreviations"),
new KeyValuePair<string, string>(Host.ResolvePath("ListExtraSpecs.md"), "listextras"),
new KeyValuePair<string, string>(Host.ResolvePath("FigureFooterAndCiteSpecs.md"), "figures+footers+citations"),
new KeyValuePair<string, string>(Host.ResolvePath("MathSpecs.md"), "mathematics"),
new KeyValuePair<string, string>("https://raw.githubusercontent.com/jgm/CommonMark/4ec06917c3a3632be4a935ffa0973092bd2621be/spec.txt", string.Empty), // 0.28
new KeyValuePair<string, string>(Host.ResolvePath("PipeTableSpecs.md"), "pipetables|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("FootnotesSpecs.md"), "footnotes|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("GenericAttributesSpecs.md"), "attributes|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("EmphasisExtraSpecs.md"), "emphasisextras|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("HardlineBreakSpecs.md"), "hardlinebreak|advanced+hardlinebreak"),
new KeyValuePair<string, string>(Host.ResolvePath("GridTableSpecs.md"), "gridtables|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("CustomContainerSpecs.md"), "customcontainers+attributes|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("DefinitionListSpecs.md"), "definitionlists+attributes|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("EmojiSpecs.md"), "emojis|advanced+emojis"),
new KeyValuePair<string, string>(Host.ResolvePath("AbbreviationSpecs.md"), "abbreviations|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("ListExtraSpecs.md"), "listextras|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("FigureFooterAndCiteSpecs.md"), "figures+footers+citations|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("MathSpecs.md"), "mathematics|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("BootstrapSpecs.md"), "bootstrap+pipetables+figures+attributes"),
new KeyValuePair<string, string>(Host.ResolvePath("MediaSpecs.md"), "medialinks"),
new KeyValuePair<string, string>(Host.ResolvePath("SmartyPantsSpecs.md"), "smartypants"),
new KeyValuePair<string, string>(Host.ResolvePath("AutoIdentifierSpecs.md"), "autoidentifiers"),
new KeyValuePair<string, string>(Host.ResolvePath("TaskListSpecs.md"), "tasklists"),
new KeyValuePair<string, string>(Host.ResolvePath("MediaSpecs.md"), "medialinks|advanced+medialinks"),
new KeyValuePair<string, string>(Host.ResolvePath("SmartyPantsSpecs.md"), "pipetables+smartypants|advanced+smartypants"), // Check with smartypants to make sure that it doesn't break pipetables
new KeyValuePair<string, string>(Host.ResolvePath("AutoIdentifierSpecs.md"), "autoidentifiers|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("TaskListSpecs.md"), "tasklists|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("DiagramsSpecs.md"), "diagrams|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("NoHtmlSpecs.md"), "nohtml"),
new KeyValuePair<string, string>(Host.ResolvePath("YamlSpecs.md"), "yaml"),
new KeyValuePair<string, string>(Host.ResolvePath("AutoLinks.md"), "autolinks|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("JiraLinks.md"), "jiralinks"),
new KeyValuePair<string, string>(Host.ResolvePath("GlobalizationSpecs.md"), "globalization+advanced+emojis"),
};
var emptyLines = false;
var displayEmptyLines = false;
#>
// Generated the <#= DateTime.Now #>
using System;
using NUnit.Framework;
@@ -142,7 +150,7 @@ private class Spec
public string Name
{
get { return string.Format("Example{0:000}", Example); }
get { return string.Format("{0}_Example{1:000}", SecHeadingCompact, Example); }
}
}

View File

@@ -12,10 +12,10 @@ A task list item consist of `[ ]` or `[x]` or `[X]` inside a list item (ordered
- [ ] Item3
- Item4
.
<ul>
<li><input disabled="disabled" type="checkbox" /> Item1</li>
<li><input disabled="disabled" type="checkbox" checked="checked" /> Item2</li>
<li><input disabled="disabled" type="checkbox" /> Item3</li>
<ul class="contains-task-list">
<li class="task-list-item"><input disabled="disabled" type="checkbox" /> Item1</li>
<li class="task-list-item"><input disabled="disabled" type="checkbox" checked="checked" /> Item2</li>
<li class="task-list-item"><input disabled="disabled" type="checkbox" /> Item3</li>
<li>Item4</li>
</ul>
````````````````````````````````

View File

@@ -0,0 +1,25 @@
// 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 NUnit.Framework;
namespace Markdig.Tests
{
[TestFixture]
public partial class TestEmphasisPlus
{
[Test]
public void StrongNormal()
{
TestParser.TestSpec("***Strong emphasis*** normal", "<p><em><strong>Strong emphasis</strong></em> normal</p>", "");
}
[Test]
public void NormalStrongNormal()
{
TestParser.TestSpec("normal ***Strong emphasis*** normal", "<p>normal <em><strong>Strong emphasis</strong></em> normal</p>", "");
}
}
}

View File

@@ -0,0 +1,120 @@
# Extensions
Adds support for YAML frontmatter parsing:
## YAML frontmatter discard
If a frontmatter is present, it will not be rendered:
```````````````````````````````` example
---
this: is a frontmatter
---
This is a text
.
<p>This is a text</p>
````````````````````````````````
But if a frontmatter doesn't happen on the first line, it will be parse as regular Markdown content
```````````````````````````````` example
This is a text1
---
this: is a frontmatter
---
This is a text2
.
<h2>This is a text1</h2>
<h2>this: is a frontmatter</h2>
<p>This is a text2</p>
````````````````````````````````
It expects an exact 3 dashes `---`:
```````````````````````````````` example
----
this: is a frontmatter
----
This is a text
.
<hr />
<h2>this: is a frontmatter</h2>
<p>This is a text</p>
````````````````````````````````
It can end with three dots `...`:
```````````````````````````````` example
---
this: is a frontmatter
...
This is a text
.
<p>This is a text</p>
````````````````````````````````
If the end front matter marker (`...` or `---`) is not present, it will render the `---` has a `<hr>`:
```````````````````````````````` example
---
this: is a frontmatter
This is a text
.
<hr />
<p>this: is a frontmatter
This is a text</p>
````````````````````````````````
It expects exactly three dots `...`:
```````````````````````````````` example
---
this: is a frontmatter
....
This is a text
.
<hr />
<p>this: is a frontmatter
....
This is a text</p>
````````````````````````````````
Front matter ends with the first line containing three dots `...` or three dashes `---`:
```````````````````````````````` example
---
this: is a frontmatter
....
Hello
---
This is a text
.
<p>This is a text</p>
````````````````````````````````
It expects whitespace can exist after the leading characters
```````````````````````````````` example
---
this: is a frontmatter
...
This is a text
.
<p>This is a text</p>
````````````````````````````````
It expects whitespace can exist after the trailing characters
```````````````````````````````` example
---
this: is a frontmatter
...
This is a text
.
<p>This is a text</p>
````````````````````````````````

View File

@@ -0,0 +1,36 @@
# Documentation Tests/Specs
You will find from the following links the supported extensions in markdig and their usage:
- 2 kind of tables:
- [**Pipe tables**](PipeTableSpecs.md)
- [**Grid tables**](GridTableSpecs.md)
- [**Extra emphasis**](EmphasisExtraSpecs.md)
- strike through `~~`,
- Subscript `~`
- Superscript `^`
- Inserted `++`
- Marked `==`
- [**Special attributes**](GenericAttributesSpecs.md)
- [**Definition lists**](DefinitionListSpecs.md)
- [**Footnotes**](FootnotesSpecs.md)
- [**Auto-identifiers**](AutoIdentifierSpecs.md)
- [**Auto-links**](AutoLinks.md)
- [**Task Lists**](TaskListSpecs.md)
- [**Extra bullet lists**](ListExtraSpecs.md)
- [**Media support**](MediaSpecs.md)
- [**Abbreviations**](AbbreviationSpecs.md)
- [**Citation**](FigureFooterAndCiteSpecs.md)
- [**Custom containers**](CustomContainerSpecs.md)
- [**Figures**](FigureFooterAndCiteSpecs.md)
- [**Footers**](FigureFooterAndCiteSpecs.md)
- [**Mathematics**](MathSpecs.md)/Latex extension by enclosing `$$` for block and `$` for inline math (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/mathematics-extension/457/31))
- [**Soft lines as hard lines**](HardlineBreakSpecs.md)
- [**Emoji**](EmojiSpecs.md)
- [**SmartyPants**](SmartyPantsSpecs.md)
- [**Bootstrap**](BootstrapSpecs.md)
- [**Diagrams**](DiagramsSpecs.md)
- [**YAML frontmatter**](YamlSpecs.md)
- [**JIRA links**](JiraLinks.md)
> Notice that the links above are not yet the final documentation but are "specification" files used for testing the correctness of markdig for each extension

View File

@@ -0,0 +1,42 @@
using NUnit.Framework;
namespace Markdig.Tests
{
[TestFixture]
public class TestConfigureNewLine
{
[Test]
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "<p><em>1</em>\n<em>2</em></p>\n")]
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "<p><em>1</em>\n<em>2</em></p>\n")]
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "<p><em>1</em>\r\n<em>2</em></p>\r\n")]
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "<p><em>1</em>\r\n<em>2</em></p>\r\n")]
[TestCase(/* newLineForWriting: */ "!!!" , /* markdownText: */ "*1*\n*2*\n", /* expected: */ "<p><em>1</em>!!!<em>2</em></p>!!!")]
[TestCase(/* newLineForWriting: */ "!!!" , /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "<p><em>1</em>!!!<em>2</em></p>!!!")]
public void TestHtmlOutputWhenConfiguringNewLine(string newLineForWriting, string markdownText, string expected)
{
var pipeline = new MarkdownPipelineBuilder()
.ConfigureNewLine(newLineForWriting)
.Build();
var actual = Markdown.ToHtml(markdownText, pipeline);
Assert.AreEqual(expected, actual);
}
[Test]
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "1\n2\n")]
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "1\n2\n")]
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "1\r\n2\r\n")]
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "1\r\n2\r\n")]
[TestCase(/* newLineForWriting: */ "!!!", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "1!!!2!!!")]
[TestCase(/* newLineForWriting: */ "!!!", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "1!!!2!!!")]
public void TestPlainOutputWhenConfiguringNewLine(string newLineForWriting, string markdownText, string expected)
{
var pipeline = new MarkdownPipelineBuilder()
.ConfigureNewLine(newLineForWriting)
.Build();
var actual = Markdown.ToPlainText(markdownText, pipeline);
Assert.AreEqual(expected, actual);
}
}
}

View File

@@ -0,0 +1,96 @@
// 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 Markdig.Renderers.Html;
using NUnit.Framework;
using System.Collections.Generic;
namespace Markdig.Tests
{
[TestFixture()]
public class TestHtmlAttributes
{
[Test]
public void TestAddClass()
{
var attributes = new HtmlAttributes();
attributes.AddClass("test");
Assert.NotNull(attributes.Classes);
Assert.AreEqual(new List<string>() { "test" }, attributes.Classes);
attributes.AddClass("test");
Assert.AreEqual(1, attributes.Classes.Count);
attributes.AddClass("test1");
Assert.AreEqual(new List<string>() { "test", "test1" }, attributes.Classes);
}
[Test]
public void TestAddProperty()
{
var attributes = new HtmlAttributes();
attributes.AddProperty("key1", "1");
Assert.NotNull(attributes.Properties);
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1") }, attributes.Properties);
attributes.AddPropertyIfNotExist("key1", "1");
Assert.NotNull(attributes.Properties);
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1") }, attributes.Properties);
attributes.AddPropertyIfNotExist("key2", "2");
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1"), new KeyValuePair<string, string>("key2", "2") }, attributes.Properties);
}
[Test]
public void TestCopyTo()
{
var from = new HtmlAttributes();
from.AddClass("test");
from.AddProperty("key1", "1");
var to = new HtmlAttributes();
from.CopyTo(to);
Assert.True(ReferenceEquals(from.Classes, to.Classes));
Assert.True(ReferenceEquals(from.Properties, to.Properties));
// From: Classes From: Properties To: Classes To: Properties
// test1: null null null null
from = new HtmlAttributes();
to = new HtmlAttributes();
from.CopyTo(to, false, false);
Assert.Null(to.Classes);
Assert.Null(to.Properties);
// test2: ["test"] ["key1", "1"] null null
from = new HtmlAttributes();
to = new HtmlAttributes();
from.AddClass("test");
from.AddProperty("key1", "1");
from.CopyTo(to, false, false);
Assert.AreEqual(new List<string>() { "test" }, to.Classes);
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1")}, to.Properties);
// test3: null null ["test"] ["key1", "1"]
from = new HtmlAttributes();
to = new HtmlAttributes();
to.AddClass("test");
to.AddProperty("key1", "1");
from.CopyTo(to, false, false);
Assert.AreEqual(new List<string>() { "test" }, to.Classes);
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1") }, to.Properties);
// test4: ["test1"] ["key2", "2"] ["test"] ["key1", "1"]
from = new HtmlAttributes();
to = new HtmlAttributes();
from.AddClass("test1");
from.AddProperty("key2", "2");
to.AddClass("test");
to.AddProperty("key1", "1");
from.CopyTo(to, false, false);
Assert.AreEqual(new List<string>() { "test", "test1" }, to.Classes);
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1"), new KeyValuePair<string, string>("key2", "2") }, to.Properties);
}
}
}

View File

@@ -0,0 +1,25 @@
using NUnit.Framework;
using System.Text.RegularExpressions;
namespace Markdig.Tests
{
[TestFixture]
public class TestImageAltText
{
[Test]
[TestCase("![](image.jpg)", "")]
[TestCase("![foo](image.jpg)", "foo")]
[TestCase("![][1]\n\n[1]: image.jpg", "")]
[TestCase("![bar][1]\n\n[1]: image.jpg", "bar")]
[TestCase("![](image.jpg 'title')", "")]
[TestCase("![foo](image.jpg 'title')", "foo")]
[TestCase("![][1]\n\n[1]: image.jpg 'title'", "")]
[TestCase("![bar][1]\n\n[1]: image.jpg 'title'", "bar")]
public void TestImageHtmlAltText(string markdown, string expectedAltText)
{
string html = Markdown.ToHtml(markdown);
string actualAltText = Regex.Match(html, "alt=\"(.*?)\"").Groups[1].Value;
Assert.AreEqual(expectedAltText, actualAltText);
}
}
}

View File

@@ -1,6 +1,8 @@
// 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.Security;
using NUnit.Framework;
using Markdig.Helpers;
using Markdig.Syntax;
@@ -30,6 +32,18 @@ namespace Markdig.Tests
Assert.AreEqual(')', text.CurrentChar);
}
[Test]
[TestCase("http://google.com.")]
[TestCase("http://google.com. ")]
public void TestUrlTrailingFullStop(string uri)
{
var text = new StringSlice(uri);
string link;
Assert.True(LinkHelper.TryParseUrl(ref text, out link, true));
Assert.AreEqual("http://google.com", link);
Assert.AreEqual('.', text.CurrentChar);
}
[Test]
public void TestUrlNestedParenthesis()
{
@@ -297,5 +311,103 @@ namespace Markdig.Tests
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@"<ab"), out text, out isEmail));
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@"<user@>"), out text, out isEmail));
}
[TestCase("Header identifiers in HTML", "header-identifiers-in-html")]
[TestCase("* Dogs*?--in *my* house?", "dogs-in-my-house")] // Not Pandoc equivalent: dogs--in...
[TestCase("[HTML], [S5], or [RTF]?", "html-s5-or-rtf")]
[TestCase("3. Applications", "applications")]
[TestCase("33", "")]
public void TestUrilizeNonAscii_Pandoc(string input, string expectedResult)
{
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
}
[TestCase("abc", "abc")]
[TestCase("a-c", "a-c")]
[TestCase("a c", "a-c")]
[TestCase("a_c", "a_c")]
[TestCase("a.c", "a.c")]
[TestCase("a,c", "ac")]
[TestCase("a--", "a")] // Not Pandoc-equivalent: a--
[TestCase("a__", "a")] // Not Pandoc-equivalent: a__
[TestCase("a..", "a")] // Not Pandoc-equivalent: a..
[TestCase("a??", "a")]
[TestCase("a ", "a")]
[TestCase("a--d", "a-d")]
[TestCase("a__d", "a_d")]
[TestCase("a??d", "ad")]
[TestCase("a d", "a-d")]
[TestCase("a..d", "a.d")]
[TestCase("-bc", "bc")]
[TestCase("_bc", "bc")]
[TestCase(" bc", "bc")]
[TestCase("?bc", "bc")]
[TestCase(".bc", "bc")]
[TestCase("a-.-", "a")] // Not Pandoc equivalent: a-.-
public void TestUrilizeOnlyAscii_Simple(string input, string expectedResult)
{
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, true));
}
[TestCase("bær", "br")]
[TestCase("bør", "br")]
[TestCase("bΘr", "br")]
[TestCase("四五", "")]
public void TestUrilizeOnlyAscii_NonAscii(string input, string expectedResult)
{
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, true));
}
[TestCase("bár", "bar")]
[TestCase("àrrivé", "arrive")]
public void TestUrilizeOnlyAscii_Normalization(string input, string expectedResult)
{
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, true));
}
[TestCase("123", "")]
[TestCase("1,-b", "b")]
[TestCase("b1,-", "b1")] // Not Pandoc equivalent: b1-
[TestCase("ab3", "ab3")]
[TestCase("ab3de", "ab3de")]
public void TestUrilizeOnlyAscii_Numeric(string input, string expectedResult)
{
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, true));
}
[TestCase("一二三四五", "一二三四五")]
[TestCase("一,-b", "一-b")]
public void TestUrilizeNonAscii_NonAsciiNumeric(string input, string expectedResult)
{
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
}
[TestCase("bær", "bær")]
[TestCase("æ5el", "æ5el")]
[TestCase("-æ5el", "æ5el")]
[TestCase("-frø-", "frø")]
[TestCase("-fr-ø", "fr-ø")]
public void TestUrilizeNonAscii_Simple(string input, string expectedResult)
{
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
}
// Just to be sure, test for characters expressly forbidden in URI fragments:
[TestCase("b#r", "br")]
[TestCase("b%r", "br")] // Invalid except as an escape character
[TestCase("b^r", "br")]
[TestCase("b[r", "br")]
[TestCase("b]r", "br")]
[TestCase("b{r", "br")]
[TestCase("b}r", "br")]
[TestCase("b<r", "br")]
[TestCase("b>r", "br")]
[TestCase(@"b\r", "br")]
[TestCase(@"b""r", "br")]
[TestCase(@"Requirement 😀", "requirement")]
public void TestUrilizeNonAscii_NonValidCharactersForFragments(string input, string expectedResult)
{
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.IO;
using Markdig.Parsers;
using Markdig.Renderers;
using NUnit.Framework;
namespace Markdig.Tests
{
public class TestLinkRewriter
{
[Test]
public void ReplacesRelativeLinks()
{
TestSpec(s => "abc" + s, "Link: [hello](/relative.jpg)", "abc/relative.jpg");
TestSpec(s => s + "xyz", "Link: [hello](relative.jpg)", "relative.jpgxyz");
TestSpec(null, "Link: [hello](relative.jpg)", "relative.jpg");
TestSpec(null, "Link: [hello](/relative.jpg)", "/relative.jpg");
}
[Test]
public void ReplacesRelativeImageSources()
{
TestSpec(s => "abc" + s, "Image: ![alt text](/image.jpg)", "abc/image.jpg");
TestSpec(s => "abc" + s, "Image: ![alt text](image.jpg \"title\")", "abcimage.jpg");
TestSpec(null, "Image: ![alt text](/image.jpg)", "/image.jpg");
}
public static void TestSpec(Func<string,string> linkRewriter, string markdown, string expectedLink)
{
var pipeline = new MarkdownPipelineBuilder().Build();
var writer = new StringWriter();
var renderer = new HtmlRenderer(writer);
renderer.LinkRewriter = linkRewriter;
pipeline.Setup(renderer);
var document = MarkdownParser.Parse(markdown, pipeline);
renderer.Render(document);
writer.Flush();
Assert.That(writer.ToString(), Contains.Substring("=\"" + expectedLink + "\""));
}
}
}

View File

@@ -0,0 +1,495 @@
// 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 NUnit.Framework;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using System.IO;
using Markdig.Renderers.Normalize;
using Markdig.Helpers;
namespace Markdig.Tests
{
[TestFixture]
public class TestNormalize
{
[Test]
public void SyntaxCodeBlock()
{
AssertSyntax("````csharp\npublic void HelloWorld()\n{\n}\n````", new FencedCodeBlock(null)
{
FencedChar = '`',
FencedCharCount = 4,
Info = "csharp",
Lines = new StringLineGroup(4)
{
new StringSlice("public void HelloWorld()"),
new StringSlice("{"),
new StringSlice("}"),
}
});
AssertSyntax(" public void HelloWorld()\n {\n }", new CodeBlock(null)
{
Lines = new StringLineGroup(4)
{
new StringSlice("public void HelloWorld()"),
new StringSlice("{"),
new StringSlice("}"),
}
});
}
[Test]
public void SyntaxHeadline()
{
AssertSyntax("## Headline", new HeadingBlock(null)
{
HeaderChar = '#',
Level = 2,
Inline = new ContainerInline().AppendChild(new LiteralInline("Headline")),
});
}
[Test]
public void SyntaxParagraph()
{
AssertSyntax("This is a normal paragraph", new ParagraphBlock()
{
Inline = new ContainerInline()
.AppendChild(new LiteralInline("This is a normal paragraph")),
});
AssertSyntax("This is a\nnormal\nparagraph", new ParagraphBlock()
{
Inline = new ContainerInline()
.AppendChild(new LiteralInline("This is a"))
.AppendChild(new LineBreakInline())
.AppendChild(new LiteralInline("normal"))
.AppendChild(new LineBreakInline())
.AppendChild(new LiteralInline("paragraph")),
});
}
[Test]
public void CodeBlock()
{
AssertNormalizeNoTrim(" public void HelloWorld();\n {\n }");
AssertNormalizeNoTrim(" public void HelloWorld();\n {\n }\n\ntext after two newlines");
AssertNormalizeNoTrim("````\npublic void HelloWorld();\n{\n}\n````\n\ntext after two newlines");
AssertNormalizeNoTrim("````csharp\npublic void HelloWorld();\n{\n}\n````");
AssertNormalizeNoTrim("````csharp hideNewKeyword=true\npublic void HelloWorld();\n{\n}\n````");
}
[Test]
public void Heading()
{
AssertNormalizeNoTrim("# Heading");
AssertNormalizeNoTrim("## Heading");
AssertNormalizeNoTrim("### Heading");
AssertNormalizeNoTrim("#### Heading");
AssertNormalizeNoTrim("##### Heading");
AssertNormalizeNoTrim("###### Heading");
AssertNormalizeNoTrim("###### Heading\n\ntext after two newlines");
AssertNormalizeNoTrim("# Heading\nAnd Text1\n\nAndText2", options: new NormalizeOptions() { EmptyLineAfterHeading = false });
AssertNormalizeNoTrim("Heading\n=======\n\ntext after two newlines", "# Heading\n\ntext after two newlines");
}
[Test]
public void Backslash()
{
AssertNormalizeNoTrim("This is a hardline \nAnd this is another hardline\\\nThis is standard newline");
AssertNormalizeNoTrim("This is a line\nWith another line\nAnd a last line");
}
[Test]
public void HtmlBlock()
{
/*AssertNormalizeNoTrim(@"<div id=""foo"" class=""bar
baz"">
</ div >");*/ // TODO: Bug: Throws Exception during emit
}
[Test]
public void Paragraph()
{
AssertNormalizeNoTrim("This is a plain paragraph");
AssertNormalizeNoTrim(@"This
is
a
plain
paragraph");
}
[Test]
public void ParagraphMulti()
{
AssertNormalizeNoTrim(@"line1
line2
line3");
}
[Test]
public void ListUnordered()
{
AssertNormalizeNoTrim(@"- a
- b
- c");
}
[Test]
public void ListUnorderedLoose()
{
AssertNormalizeNoTrim(@"- a
- b
- c");
}
[Test]
public void ListOrderedLooseAndCodeBlock()
{
AssertNormalizeNoTrim(@"1. ```
foo
```
bar");
}
[Test, Ignore("Not sure this is the correct normalize for this one. Need to check the specs")]
public void ListUnorderedLooseTop()
{
AssertNormalizeNoTrim(@"* foo
* bar
baz", options: new NormalizeOptions() { ListItemCharacter = '*' });
}
[Test]
public void ListUnorderedLooseMultiParagraph()
{
AssertNormalizeNoTrim(
@"- a
And another paragraph a
- b
And another paragraph b
- c");
}
[Test]
public void ListOrdered()
{
AssertNormalizeNoTrim(@"1. a
2. b
3. c");
}
[Test]
public void ListOrderedAndIntended()
{
AssertNormalizeNoTrim(@"1. a
2. b
- foo
- bar
a) 1234
b) 1324
3. c
4. c
5. c
6. c
7. c
8. c
9. c
10. c
- Foo
- Bar
11. c
12. c");
}
[Test]
public void HeaderAndParagraph()
{
AssertNormalizeNoTrim(@"# heading
paragraph
paragraph2 without newlines");
}
[Test]
public void QuoteBlock()
{
AssertNormalizeNoTrim(@"> test1
>
> test2");
AssertNormalizeNoTrim(@"> test1
This is a continuation
> test2",
@"> test1
> This is a continuation
> test2"
);
AssertNormalizeNoTrim(@"> test1
> -foobar
asdf
> test2
> -foobar sen.");
}
[Test]
public void ThematicBreak()
{
AssertNormalizeNoTrim("***\n");
AssertNormalizeNoTrim("* * *\n", "***\n");
}
[Test]
public void AutolinkInline()
{
AssertNormalizeNoTrim("This has a <auto.link.com>");
}
[Test]
public void CodeInline()
{
AssertNormalizeNoTrim("This has a `HelloWorld()` in it");
AssertNormalizeNoTrim(@"This has a ``Hello`World()`` in it");
}
[Test]
public void EmphasisInline()
{
AssertNormalizeNoTrim("This is a plain **paragraph**");
AssertNormalizeNoTrim("This is a plain *paragraph*");
AssertNormalizeNoTrim("This is a plain _paragraph_");
AssertNormalizeNoTrim("This is a plain __paragraph__");
AssertNormalizeNoTrim("This is a pl*ai*n **paragraph**");
}
[Test]
public void LineBreakInline()
{
AssertNormalizeNoTrim("normal\nline break");
AssertNormalizeNoTrim("hard \nline break");
AssertNormalizeNoTrim("This is a hardline \nAnd this is another hardline\\\nThis is standard newline");
AssertNormalizeNoTrim("This is a line\nWith another line\nAnd a last line");
}
[Test]
public void LinkInline()
{
AssertNormalizeNoTrim("This is a [link](http://company.com)");
AssertNormalizeNoTrim("This is an ![image](http://company.com)");
AssertNormalizeNoTrim(@"This is a [link](http://company.com ""Crazy Company"")");
AssertNormalizeNoTrim(@"This is a [link](http://company.com ""Crazy \"" Company"")");
}
[Test]
public void LinkReferenceDefinition()
{
// Full link
AssertNormalizeNoTrim("This is a [link][MyLink]\n\n[MyLink]: http://company.com");
AssertNormalizeNoTrim("[MyLink]: http://company.com\nThis is a [link][MyLink]",
"This is a [link][MyLink]\n\n[MyLink]: http://company.com");
AssertNormalizeNoTrim("This is a [link][MyLink] a normal link [link](http://google.com) and another def link [link2][MyLink2]\n\n[MyLink]: http://company.com\n[MyLink2]: http://company2.com");
// Collapsed link
AssertNormalizeNoTrim("This is a [link][]\n\n[link]: http://company.com");
// Shortcut link
AssertNormalizeNoTrim("This is a [link]\n\n[link]: http://company.com");
}
[Test]
public void EscapeInline()
{
AssertNormalizeNoTrim("This is an escape \\* with another \\[");
}
[Test]
public void HtmlEntityInline()
{
AssertNormalizeNoTrim("This is a &auml; blank");
}
[Test]
public void HtmlInline()
{
AssertNormalizeNoTrim("foo <hr/> bar");
AssertNormalizeNoTrim(@"foo <hr foo=""bar""/> bar");
}
[Test]
public void SpaceBetweenNodes()
{
AssertNormalizeNoTrim("# Hello World\nFoobar is a better bar.",
"# Hello World\n\nFoobar is a better bar.");
}
[Test]
public void SpaceBetweenNodesEvenForHeadlines()
{
AssertNormalizeNoTrim("# Hello World\n## Chapter 1\nFoobar is a better bar.",
"# Hello World\n\n## Chapter 1\n\nFoobar is a better bar.");
}
[Test]
public void SpaceRemoveAtStartAndEnd()
{
AssertNormalizeNoTrim("\n\n# Hello World\n## Chapter 1\nFoobar is a better bar.\n\n",
"# Hello World\n\n## Chapter 1\n\nFoobar is a better bar.");
}
[Test]
public void SpaceShortenBetweenNodes()
{
AssertNormalizeNoTrim("# Hello World\n\n\n\nFoobar is a better bar.",
"# Hello World\n\nFoobar is a better bar.");
}
[Test]
public void BiggerSample()
{
var input = @"# Heading 1
This is a paragraph
This is another paragraph
- This is a list item 1
- This is a list item 2
- This is a list item 3
```C#
This is a code block
```
> This is a quote block
This is an indented code block
line 2 of indented
This is a last line";
AssertNormalizeNoTrim(input);
}
[Test]
public void TaskLists()
{
AssertNormalizeNoTrim("- [X] This is done");
AssertNormalizeNoTrim("- [x] This is done",
"- [X] This is done");
AssertNormalizeNoTrim("- [ ] This is not done");
// ignore
AssertNormalizeNoTrim("[x] This is not a task list");
AssertNormalizeNoTrim("[ ] This is not a task list");
}
[Test]
public void JiraLinks()
{
AssertNormalizeNoTrim("FOO-1234");
AssertNormalizeNoTrim("AB-1");
AssertNormalizeNoTrim("**Hello World AB-1**");
}
[Test]
public void AutoLinks()
{
AssertNormalizeNoTrim("Hello from http://example.com/foo", "Hello from [http://example.com/foo](http://example.com/foo)", new NormalizeOptions() { ExpandAutoLinks = true, });
AssertNormalizeNoTrim("Hello from www.example.com/foo", "Hello from [www.example.com/foo](http://www.example.com/foo)", new NormalizeOptions() { ExpandAutoLinks = true, });
AssertNormalizeNoTrim("Hello from ftp://example.com", "Hello from [ftp://example.com](ftp://example.com)", new NormalizeOptions() { ExpandAutoLinks = true, });
AssertNormalizeNoTrim("Hello from mailto:hello@example.com", "Hello from [hello@example.com](mailto:hello@example.com)", new NormalizeOptions() { ExpandAutoLinks = true, });
AssertNormalizeNoTrim("Hello from http://example.com/foo", "Hello from http://example.com/foo", new NormalizeOptions() { ExpandAutoLinks = false, });
AssertNormalizeNoTrim("Hello from www.example.com/foo", "Hello from http://www.example.com/foo", new NormalizeOptions() { ExpandAutoLinks = false, });
AssertNormalizeNoTrim("Hello from mailto:hello@example.com", "Hello from mailto:hello@example.com", new NormalizeOptions() { ExpandAutoLinks = false, });
}
private static void AssertSyntax(string expected, MarkdownObject syntax)
{
var writer = new StringWriter();
var normalizer = new NormalizeRenderer(writer);
var document = new MarkdownDocument();
if (syntax is Block)
{
document.Add(syntax as Block);
}
else
{
throw new InvalidOperationException();
}
normalizer.Render(document);
var actual = writer.ToString();
Assert.AreEqual(expected, actual);
}
public void AssertNormalizeNoTrim(string input, string expected = null, NormalizeOptions options = null)
=> AssertNormalize(input, expected, false, options);
public void AssertNormalize(string input, string expected = null, bool trim = true, NormalizeOptions options = null)
{
expected = expected ?? input;
input = NormText(input, trim);
expected = NormText(expected, trim);
var pipeline = new MarkdownPipelineBuilder()
.UseAutoLinks()
.UseJiraLinks(new Extensions.JiraLinks.JiraLinkOptions("https://jira.example.com"))
.UseTaskLists()
.Build();
var result = Markdown.Normalize(input, options, pipeline: pipeline);
result = NormText(result, trim);
Console.WriteLine("```````````````````Source");
Console.WriteLine(TestParser.DisplaySpaceAndTabs(input));
Console.WriteLine("```````````````````Result");
Console.WriteLine(TestParser.DisplaySpaceAndTabs(result));
Console.WriteLine("```````````````````Expected");
Console.WriteLine(TestParser.DisplaySpaceAndTabs(expected));
Console.WriteLine("```````````````````");
Console.WriteLine();
TextAssert.AreEqual(expected, result);
}
private static string NormText(string text, bool trim)
{
if (trim)
{
text = text.Trim();
}
return text.Replace("\r\n", "\n").Replace('\r', '\n');
}
}
}

View File

@@ -0,0 +1,43 @@
// 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 Markdig.Helpers;
using NUnit.Framework;
namespace Markdig.Tests
{
[TestFixture]
public class TestOrderedList
{
[Test]
public void TestReplace()
{
var list = new OrderedList<ITest>
{
new A(),
new B(),
new C(),
};
// Replacing B with D. Order should now be A, D, B.
var result = list.Replace<B>(new D());
Assert.That(result, Is.True);
Assert.That(list.Count, Is.EqualTo(3));
Assert.That(list[0], Is.InstanceOf<A>());
Assert.That(list[1], Is.InstanceOf<D>());
Assert.That(list[2], Is.InstanceOf<C>());
// Replacing B again should fail, as it's no longer in the list.
Assert.That(list.Replace<B>(new D()), Is.False);
}
#region Test fixtures
private interface ITest { }
private class A : ITest { }
private class B : ITest { }
private class C : ITest { }
private class D : ITest { }
#endregion
}
}

View File

@@ -1,39 +1,72 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// 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.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Markdig.Extensions.JiraLinks;
using NUnit.Framework;
namespace Markdig.Tests
{
public class TestParser
{
[Test]
public void TestEmphasisAndHtmlEntity()
{
var markdownText = "*Unlimited-Fun&#174;*&#174;";
TestSpec(markdownText, "<p><em>Unlimited-Fun®</em>®</p>");
}
[Test]
public void TestThematicInsideCodeBlockInsideList()
{
var input = @"1. In the :
```
Id DisplayName Description
-- ----------- -----------
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
```";
TestSpec(input, @"<ol>
<li><p>In the :</p>
<pre><code>Id DisplayName Description
-- ----------- -----------
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
</code></pre></li>
</ol>");
}
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null)
{
foreach (var pipeline in GetPipeline(extensions))
{
Console.WriteLine($"Pipeline configured with extensions: {pipeline.Key}");
// Uncomment this line to get more debug information for process inlines.
//pipeline.DebugLog = Console.Out;
var result = Markdown.ToHtml(inputText, pipeline.Value);
result = Compact(result);
expectedOutputText = Compact(expectedOutputText);
Console.WriteLine("```````````````````Source");
Console.WriteLine(DisplaySpaceAndTabs(inputText));
Console.WriteLine("```````````````````Result");
Console.WriteLine(DisplaySpaceAndTabs(result));
Console.WriteLine("```````````````````Expected");
Console.WriteLine(DisplaySpaceAndTabs(expectedOutputText));
Console.WriteLine("```````````````````");
Console.WriteLine();
TextAssert.AreEqual(expectedOutputText, result);
TestSpec(inputText, expectedOutputText, pipeline.Value);
}
}
public static void TestSpec(string inputText, string expectedOutputText, MarkdownPipeline pipeline)
{
// Uncomment this line to get more debug information for process inlines.
//pipeline.DebugLog = Console.Out;
var result = Markdown.ToHtml(inputText, pipeline);
result = Compact(result);
expectedOutputText = Compact(expectedOutputText);
Console.WriteLine("```````````````````Source");
Console.WriteLine(DisplaySpaceAndTabs(inputText));
Console.WriteLine("```````````````````Result");
Console.WriteLine(DisplaySpaceAndTabs(result));
Console.WriteLine("```````````````````Expected");
Console.WriteLine(DisplaySpaceAndTabs(expectedOutputText));
Console.WriteLine("```````````````````");
Console.WriteLine();
TextAssert.AreEqual(expectedOutputText, result);
}
private static IEnumerable<KeyValuePair<string, MarkdownPipeline>> GetPipeline(string extensionsGroupText)
{
// For the standard case, we make sure that both the CommmonMark core and Extra/Advanced are CommonMark compliant!
@@ -64,8 +97,17 @@ namespace Markdig.Tests
var extensionGroups = extensionsGroupText.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var extensionsText in extensionGroups)
{
var pipeline = new MarkdownPipelineBuilder().Configure(extensionsText);
yield return new KeyValuePair<string, MarkdownPipeline>(extensionsText, pipeline.Build());
var builder = new MarkdownPipelineBuilder();
builder.DebugLog = Console.Out;
if (extensionsText == "jiralinks")
{
builder.UseJiraLinks(new JiraLinkOptions("http://your.company.abc"));
}
else
{
builder = extensionsText == "self" ? builder.UseSelfPipeline() : builder.Configure(extensionsText);
}
yield return new KeyValuePair<string, MarkdownPipeline>(extensionsText, builder.Build());
}
}
@@ -78,7 +120,7 @@ namespace Markdig.Tests
private static string Compact(string html)
{
// Normalize the output to make it compatible with CommonMark specs
html = html.Replace("\r", "").Trim();
html = html.Replace("\r\n", "\n").Replace(@"\r", @"\n").Trim();
html = Regex.Replace(html, @"\s+</li>", "</li>");
html = Regex.Replace(html, @"<li>\s+", "<li>");
html = html.Normalize(NormalizationForm.FormKD);

View File

@@ -0,0 +1,38 @@
using NUnit.Framework;
namespace Markdig.Tests
{
[TestFixture]
public class TestPlainText
{
[Test]
public void TestPlain()
{
var markdownText = "*Hello*, [world](http://example.com)!";
var expected = "Hello, world!\n";
var actual = Markdown.ToPlainText(markdownText);
Assert.AreEqual(expected, actual);
}
[Test]
[TestCase(/* markdownText: */ "foo bar", /* expected: */ "foo bar\n")]
[TestCase(/* markdownText: */ "foo\nbar", /* expected: */ "foo\nbar\n")]
[TestCase(/* markdownText: */ "*foo\nbar*", /* expected: */ "foo\nbar\n")]
[TestCase(/* markdownText: */ "[foo\nbar](http://example.com)", /* expected: */ "foo\nbar\n")]
[TestCase(/* markdownText: */ "<http://foo.bar.baz>", /* expected: */ "http://foo.bar.baz\n")]
[TestCase(/* markdownText: */ "# foo bar", /* expected: */ "foo bar\n")]
[TestCase(/* markdownText: */ "# foo\nbar", /* expected: */ "foo\nbar\n")]
[TestCase(/* markdownText: */ "> foo", /* expected: */ "foo\n")]
[TestCase(/* markdownText: */ "> foo\nbar\n> baz", /* expected: */ "foo\nbar\nbaz\n")]
[TestCase(/* markdownText: */ "`foo`", /* expected: */ "foo\n")]
[TestCase(/* markdownText: */ "`foo\nbar`", /* expected: */ "foo bar\n")] // new line within codespan is treated as whitespace (Example317)
[TestCase(/* markdownText: */ "```\nfoo bar\n```", /* expected: */ "foo bar\n")]
[TestCase(/* markdownText: */ "- foo\n- bar\n- baz", /* expected: */ "foo\nbar\nbaz\n")]
public void TestPlainEnsureNewLine(string markdownText, string expected)
{
var actual = Markdown.ToPlainText(markdownText);
Assert.AreEqual(expected, actual);
}
}
}

View File

@@ -1,7 +1,10 @@
// 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.Linq;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using NUnit.Framework;
namespace Markdig.Tests
@@ -9,6 +12,26 @@ namespace Markdig.Tests
[TestFixture]
public class TestPlayParser
{
[Test]
public void TestLink()
{
var doc = Markdown.Parse("There is a ![link](/yoyo)");
var link = doc.Descendants<ParagraphBlock>().SelectMany(x => x.Inline.Descendants<LinkInline>()).FirstOrDefault(l => l.IsImage);
Assert.AreEqual("/yoyo", link?.Url);
}
[Test]
public void TestListBug2()
{
TestParser.TestSpec("10.\t*test* test\n\n11.\t__test__ test\n\n", @"<ol start=""10"">
<li><p><em>test</em> test</p>
</li>
<li><p><strong>test</strong> test</p>
</li>
</ol>
");
}
[Test]
public void TestSimple()
{
@@ -27,6 +50,170 @@ Later in a text we are using HTML and it becomes an abbr tag HTML
Console.WriteLine(result);
}
[Test]
public void TestEmptyLiteral()
{
var text = @"> *some text*
> some other text";
var doc = Markdown.Parse(text);
Assert.True(doc.Descendants().OfType<LiteralInline>().All(x => !x.Content.IsEmpty),
"There should not have any empty literals");
}
[Test]
public void TestSelfPipeline1()
{
var text = @" <!--markdig:pipetables-->
a | b
- | -
0 | 1
";
TestParser.TestSpec(text, @"<!--markdig:pipetables-->
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>1</td>
</tr>
</tbody>
</table>
", "self");
}
[Test]
public void TestListBug()
{
// TODO: Add this test back to the CommonMark specs
var text = @"- item1
- item2
- item3
- item4";
TestParser.TestSpec(text, @"<ul>
<li>item1
<ul>
<li>item2
<ul>
<li>item3
<ul>
<li>item4</li>
</ul></li>
</ul></li>
</ul></li>
</ul>
");
}
[Test]
public void TestHtmlBug()
{
TestParser.TestSpec(@" # header1
<pre class='copy'>
blabla
</pre>
# header2
", @"<h1>header1</h1>
<pre class='copy'>
blabla
</pre>
<h1>header2</h1>");
}
[Test]
public void TestHtmlh4Bug()
{
TestParser.TestSpec(@"<h4>foobar</h4>", @"<h4>foobar</h4>");
}
[Test]
public void TestStandardUriEscape()
{
TestParser.TestSpec(@"![你好](你好.png)", "<p><img src=\"你好.png\" alt=\"你好\" /></p>", "nonascii-noescape");
}
[Test]
public void TestBugAdvanced()
{
TestParser.TestSpec(@"`https://{domain}/callbacks`
#### HEADING
Paragraph
", "<p><code>https://{domain}/callbacks</code></p>\n<h4 id=\"heading\">HEADING</h4>\n<p>Paragraph</p>", "advanced");
}
[Test]
public void TestBugEmphAttribute()
{
// https://github.com/lunet-io/markdig/issues/108
TestParser.TestSpec(@"*test*{name=value}", "<p><em name=\"value\">test</em></p>", "advanced");
}
[Test]
public void TestBugPipeTables()
{
// https://github.com/lunet-io/markdig/issues/73
TestParser.TestSpec(@"| abc | def |
| --- | --- |
| 1 | ~3 |
", @"<table>
<thead>
<tr>
<th>abc</th>
<th>def</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>~3</td>
</tr>
</tbody>
</table>", "advanced");
}
[Test]
public void TestGridTableWithCustomAttributes() {
var input = @"
{.table}
+---+---+
| a | b |
+===+===+
| 1 | 2 |
+---+---+
";
var expected = @"<table class=""table"">
<col style=""width:50%"">
<col style=""width:50%"">
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>2</td>
</tr>
</tbody>
</table>
";
TestParser.TestSpec(input, expected, "advanced");
}
[Test]
public void TestSamePipelineAllExtensions()
{

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 Markdig.Renderers;
using Markdig.Syntax;
using NUnit.Framework;
namespace Markdig.Tests
{
[TestFixture]
public class TestPragmaLines
{
[Test]
public void TestFindClosest()
{
var doc = Markdown.Parse(
"test1\n" + // 0
"\n" + // 1
"test2\n" + // 2
"\n" + // 3
"test3\n" + // 4
"\n" + // 5
"test4\n" + // 6
"\n" + // 7
"# Heading\n" + // 8
"\n" + // 9
"Long para\n" + // 10
"on multiple\n" + // 11
"lines\n" + // 12
"to check that\n" + // 13
"lines are\n" + // 14
"correctly \n" + // 15
"found\n" + // 16
"\n" + // 17
"- item1\n" + // 18
"- item2\n" + // 19
"- item3\n" + // 20
"\n" + // 21
"This is a last paragraph\n" // 22
, new MarkdownPipelineBuilder().UsePragmaLines().Build());
foreach (var exact in new int[] {0, 2, 4, 6, 8, 10, 18, 19, 20, 22})
{
Assert.AreEqual(exact, doc.FindClosestLine(exact));
}
Assert.AreEqual(22, doc.FindClosestLine(23));
Assert.AreEqual(10, doc.FindClosestLine(11));
Assert.AreEqual(10, doc.FindClosestLine(12));
Assert.AreEqual(10, doc.FindClosestLine(13));
Assert.AreEqual(18, doc.FindClosestLine(14)); // > 50% of the paragraph, we switch to next
Assert.AreEqual(18, doc.FindClosestLine(15));
Assert.AreEqual(18, doc.FindClosestLine(16));
}
[Test]
public void TestFindClosest1()
{
var text =
"- item1\n" + // 0
" - item11\n" + // 1
" - item12\n" + // 2
" - item121\n" + // 3
" - item13\n" + // 4
" - item131\n" + // 5
" - item1311\n"; // 6
var pipeline = new MarkdownPipelineBuilder().UsePragmaLines().Build();
var doc = Markdown.Parse(text, pipeline);
for (int exact = 0; exact < 7; exact++)
{
Assert.AreEqual(exact, doc.FindClosestLine(exact));
}
Assert.AreEqual(6, doc.FindClosestLine(50));
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.IO;
using Markdig.Parsers;
using Markdig.Renderers;
using NUnit.Framework;
namespace Markdig.Tests
{
public class TestRelativeUrlReplacement
{
[Test]
public void ReplacesRelativeLinks()
{
TestSpec("https://example.com", "Link: [hello](/relative.jpg)", "https://example.com/relative.jpg");
TestSpec("https://example.com", "Link: [hello](relative.jpg)", "https://example.com/relative.jpg");
TestSpec("https://example.com/", "Link: [hello](/relative.jpg?a=b)", "https://example.com/relative.jpg?a=b");
TestSpec("https://example.com/", "Link: [hello](relative.jpg#x)", "https://example.com/relative.jpg#x");
TestSpec(null, "Link: [hello](relative.jpg)", "relative.jpg");
TestSpec(null, "Link: [hello](/relative.jpg)", "/relative.jpg");
TestSpec("https://example.com", "Link: [hello](/relative.jpg)", "https://example.com/relative.jpg");
}
[Test]
public void ReplacesRelativeImageSources()
{
TestSpec("https://example.com", "Image: ![alt text](/image.jpg)", "https://example.com/image.jpg");
TestSpec("https://example.com", "Image: ![alt text](image.jpg \"title\")", "https://example.com/image.jpg");
TestSpec(null, "Image: ![alt text](/image.jpg)", "/image.jpg");
}
public static void TestSpec(string baseUrl, string markdown, string expectedLink)
{
var pipeline = new MarkdownPipelineBuilder().Build();
var writer = new StringWriter();
var renderer = new HtmlRenderer(writer);
if (baseUrl != null)
renderer.BaseUrl = new Uri(baseUrl);
pipeline.Setup(renderer);
var document = MarkdownParser.Parse(markdown, pipeline);
renderer.Render(document);
writer.Flush();
Assert.That(writer.ToString(), Contains.Substring("=\"" + expectedLink + "\""));
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Markdig.Extensions.Footnotes;
using Markdig.Helpers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
@@ -88,9 +89,9 @@ literal ( 0, 5) 5-11
Check("01*2**3*", @"
paragraph ( 0, 0) 0-7
literal ( 0, 0) 0-1
emphasis ( 0, 2) 2-4
emphasis ( 0, 2) 2-7
literal ( 0, 3) 3-3
emphasis ( 0, 5) 5-7
literal ( 0, 4) 4-5
literal ( 0, 6) 6-6
");
}
@@ -140,6 +141,48 @@ literal ( 0, 2) 2-3
emphasis ( 0, 4) 4-9
literal ( 0, 6) 6-7
");
}
[Test]
public void TestFootnoteLinkReferenceDefinition()
{
// 01 2 345678
var footnote = Markdown.Parse("0\n\n [^1]:", new MarkdownPipelineBuilder().UsePreciseSourceLocation().UseFootnotes().Build()).Descendants().OfType<FootnoteLinkReferenceDefinition>().FirstOrDefault();
Assert.NotNull(footnote);
Assert.AreEqual(2, footnote.Line);
Assert.AreEqual(new SourceSpan(4, 7), footnote.Span);
Assert.AreEqual(new SourceSpan(5, 6), footnote.LabelSpan);
}
[Test]
public void TestLinkReferenceDefinition1()
{
// 0 1
// 0123456789012345
var link = Markdown.Parse("[234]: /56 'yo' ", new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants().OfType<LinkReferenceDefinition>().FirstOrDefault();
Assert.NotNull(link);
Assert.AreEqual(0, link.Line);
Assert.AreEqual(new SourceSpan(0, 14), link.Span);
Assert.AreEqual(new SourceSpan(1, 3), link.LabelSpan);
Assert.AreEqual(new SourceSpan(7, 9), link.UrlSpan);
Assert.AreEqual(new SourceSpan(11, 14), link.TitleSpan);
}
[Test]
public void TestLinkReferenceDefinition2()
{
// 0 1
// 01 2 34567890123456789
var link = Markdown.Parse("0\n\n [234]: /56 'yo' ", new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants().OfType<LinkReferenceDefinition>().FirstOrDefault();
Assert.NotNull(link);
Assert.AreEqual(2, link.Line);
Assert.AreEqual(new SourceSpan(4, 18), link.Span);
Assert.AreEqual(new SourceSpan(5, 7), link.LabelSpan);
Assert.AreEqual(new SourceSpan(11, 13), link.UrlSpan);
Assert.AreEqual(new SourceSpan(15, 18), link.TitleSpan);
}
[Test]
@@ -206,19 +249,6 @@ literal ( 0, 4) 4-5
Assert.AreEqual(SourceSpan.Empty, link.TitleSpan);
}
[Test]
public void TestHtmlInline()
{
// 0123456789
Check("01<b>4</b>", @"
paragraph ( 0, 0) 0-9
literal ( 0, 0) 0-1
html ( 0, 2) 2-4
literal ( 0, 5) 5-5
html ( 0, 6) 6-9
");
}
[Test]
public void TestAutolinkInline()
{
@@ -252,6 +282,60 @@ literal ( 4, 0) 16-16
");
}
[Test]
public void TestHtmlBlock1()
{
// 0 1
// 01 2 345678901 23
Check("0\n\n<!--A-->\n1\n", @"
paragraph ( 0, 0) 0-0
literal ( 0, 0) 0-0
html ( 2, 0) 3-10
paragraph ( 3, 0) 12-12
literal ( 3, 0) 12-12
");
}
[Test]
public void TestHtmlComment()
{
// 0 1 2
// 012345678901 234567890 1234
Check("# 012345678\n<!--0-->\n123\n", @"
heading ( 0, 0) 0-10
literal ( 0, 2) 2-10
html ( 1, 0) 12-19
paragraph ( 2, 0) 21-23
literal ( 2, 0) 21-23
");
}
[Test]
public void TestHtmlInline()
{
// 0123456789
Check("01<b>4</b>", @"
paragraph ( 0, 0) 0-9
literal ( 0, 0) 0-1
html ( 0, 2) 2-4
literal ( 0, 5) 5-5
html ( 0, 6) 6-9
");
}
[Test]
public void TestHtmlInline1()
{
// 0
// 0123456789
Check("0<!--A-->1", @"
paragraph ( 0, 0) 0-9
literal ( 0, 0) 0-0
html ( 0, 1) 1-8
literal ( 0, 9) 9-9
");
}
[Test]
public void TestThematicBreak()
{
@@ -312,6 +396,42 @@ listitem ( 1, 0) 4-6
paragraph ( 1, 2) 6-6
literal ( 1, 2) 6-6
");
}
[Test]
public void TestListBlock2()
{
string test = @"
1. Foo
9. Bar
5. Foo
6. Bar
987123. FooBar";
test = test.Replace("\r\n", "\n");
var list = Markdown.Parse(test, new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants().OfType<ListBlock>().FirstOrDefault();
Assert.NotNull(list);
Assert.AreEqual(1, list.Line);
Assert.True(list.IsOrdered);
List<ListItemBlock> items = list.Cast<ListItemBlock>().ToList();
Assert.AreEqual(5, items.Count);
// Test orders
Assert.AreEqual(1, items[0].Order);
Assert.AreEqual(9, items[1].Order);
Assert.AreEqual(5, items[2].Order);
Assert.AreEqual(6, items[3].Order);
Assert.AreEqual(987123, items[4].Order);
// Test positions
for (int i = 0; i < 4; i++)
{
Assert.AreEqual(i + 1, items[i].Line);
Assert.AreEqual(1 + (i * 7), items[i].Span.Start);
Assert.AreEqual(6, items[i].Span.Length);
}
Assert.AreEqual(5, items[4].Line);
Assert.AreEqual(new SourceSpan(29, 42), items[4].Span);
}
[Test]
@@ -328,12 +448,13 @@ literal ( 0, 2) 2-3
[Test]
public void TestHtmlEntityInline()
{
// 01234567
Check("0&nbsp;1", @"
paragraph ( 0, 0) 0-7
// 01 23456789
Check("0\n&nbsp; 1", @"
paragraph ( 0, 0) 0-9
literal ( 0, 0) 0-0
htmlentity ( 0, 1) 1-6
literal ( 0, 7) 7-7
linebreak ( 0, 1) 1-1
htmlentity ( 1, 0) 2-7
literal ( 1, 6) 8-9
");
}
@@ -613,7 +734,21 @@ literal ( 4, 2) 13-13
Check("0\n\n 0\n 1\n", @"
paragraph ( 0, 0) 0-0
literal ( 0, 0) 0-0
code ( 2, 4) 7-13
code ( 2, 0) 3-13
");
}
[Test]
public void TestIndentedCodeAfterList()
{
// 0 1 2 3 4 5
// 012345678901234567 8 901234567890123456 789012345678901234 56789
Check("1) Some list item\n\n some code\n more code\n", @"
list ( 0, 0) 0-53
listitem ( 0, 0) 0-53
paragraph ( 0, 3) 3-16
literal ( 0, 3) 3-16
code ( 2, 0) 19-53
");
}
@@ -624,7 +759,7 @@ code ( 2, 4) 7-13
Check("0\n\n\t0\n\t1\n", @"
paragraph ( 0, 0) 0-0
literal ( 0, 0) 0-0
code ( 2, 4) 4-7
code ( 2, 0) 3-7
");
}
@@ -635,7 +770,7 @@ code ( 2, 4) 4-7
Check("0\n\n \t0\n \t1\n", @"
paragraph ( 0, 0) 0-0
literal ( 0, 0) 0-0
code ( 2, 4) 5-9
code ( 2, 0) 3-9
");
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
@@ -86,7 +86,7 @@ namespace Markdig.Tests
//output.WriteLine();
}
Assert.True(string.CompareOrdinal(expectedValue, actualValue) == 0, "strings are differing");
Assert.AreEqual(expectedValue, actualValue);
}
private static string ToSafeString(this char c)

View File

@@ -18,6 +18,7 @@
}
},
"dependencies": {
"NUnit": "3.2.0"
"NUnit": "3.2.0",
"NUnit3TestAdapter": "3.9.0"
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Text;
using Microsoft.AspNetCore.Mvc;
@@ -6,6 +6,13 @@ namespace Markdig.WebApp
{
public class ApiController : Controller
{
[HttpGet()]
[Route("")]
public string Empty()
{
return string.Empty;
}
// GET api/to_html?text=xxx&extensions=advanced
[Route("api/to_html")]
[HttpGet()]

View File

@@ -0,0 +1,7 @@
{
"ProviderId": "Microsoft.ApplicationInsights.ConnectedService.ConnectedServiceProvider",
"Version": "8.9.809.2",
"GettingStartedDocument": {
"Uri": "https://go.microsoft.com/fwlink/?LinkID=798432"
}
}

View File

@@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
<AssemblyName>Markdig.WebApp</AssemblyName>
<OutputType>Exe</OutputType>
<PackageId>Markdig.WebApp</PackageId>
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
<ApplicationInsightsResourceId>/subscriptions/b6745039-70e7-4641-994b-5457cb220e2a/resourcegroups/Default-ApplicationInsights-EastUS/providers/microsoft.insights/components/Markdig.WebApp</ApplicationInsightsResourceId>
<ApplicationInsightsAnnotationResourceId>/subscriptions/b6745039-70e7-4641-994b-5457cb220e2a/resourcegroups/Default-ApplicationInsights-EastUS/providers/microsoft.insights/components/Markdig.WebApp</ApplicationInsightsAnnotationResourceId>
</PropertyGroup>
<ItemGroup>
<None Update="Views">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Markdig\Markdig.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Connected Services" />
</ItemGroup>
</Project>

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>3cad9801-9976-46be-baca-f6d0d21fdc00</ProjectGuid>
<RootNamespace>Markdig.WebApp</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -13,6 +13,7 @@ namespace Markdig.WebApp
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseApplicationInsights()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -46,10 +46,6 @@ namespace Markdig.WebApp
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseMvc();
}
}

View File

@@ -1,4 +1,4 @@
{
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
@@ -6,5 +6,8 @@
"System": "Information",
"Microsoft": "Information"
}
},
"ApplicationInsights": {
"InstrumentationKey": "5d12f113-76b2-41fe-a35a-db454b104bf9"
}
}
}

View File

@@ -1,58 +0,0 @@
{
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0-rc2-3002702",
"type": "platform"
},
"Microsoft.ApplicationInsights.AspNetCore": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-rc2-final",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final",
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-rc2-final",
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",
"Microsoft.Extensions.Logging": "1.0.0-rc2-final",
"Microsoft.Extensions.Logging.Console": "1.0.0-rc2-final",
"Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-final",
"Markdig": "0.2.1"
},
"tools": {
"Microsoft.AspNetCore.Server.IISIntegration.Tools": {
"version": "1.0.0-preview1-final",
"imports": "portable-net45+win8+dnxcore50"
}
},
"frameworks": {
"netcoreapp1.0": {
"imports": [
"dotnet5.6",
"dnxcore50",
"portable-net45+win8"
]
}
},
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
},
"runtimeOptions": {
"gcServer": true
},
"publishOptions": {
"include": [
"wwwroot",
"Views",
"appsettings.json",
"web.config"
]
},
"scripts": {
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
}
}

View File

@@ -0,0 +1,9 @@
<Project>
<PropertyGroup>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-*" PrivateAssets="All"/>
</ItemGroup>
</Project>

View File

@@ -17,7 +17,7 @@ namespace Markdig.Extensions.Abbreviations
pipeline.BlockParsers.AddIfNotAlready<AbbreviationParser>();
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null && !htmlRenderer.ObjectRenderers.Contains<HtmlAbbreviationRenderer>())

View File

@@ -20,7 +20,7 @@ namespace Markdig.Extensions.Abbreviations
/// </summary>
public AbbreviationParser()
{
OpeningCharacters = new[] {'*'};
OpeningCharacters = new[] { '*' };
}
public override BlockState TryOpen(BlockProcessor processor)
@@ -90,7 +90,7 @@ namespace Markdig.Extensions.Abbreviations
inlineProcessor.LiteralInlineParser.PostMatch += (InlineProcessor processor, ref StringSlice slice) =>
{
var literal = (LiteralInline) processor.Inline;
var literal = (LiteralInline)processor.Inline;
var originalLiteral = literal;
ContainerInline container = null;
@@ -101,20 +101,9 @@ namespace Markdig.Extensions.Abbreviations
for (int i = content.Start; i < content.End; i++)
{
string match;
if (matcher.TryMatch(text, i, content.End - i + 1, out match))
if (matcher.TryMatch(text, i, content.End - i + 1, out match) && IsValidAbbreviation(match, content, i))
{
// The word matched must be embraced by punctuation or whitespace or \0.
var c = content.PeekCharAbsolute(i - 1);
if (!(c == '\0' || c.IsAsciiPunctuation() || c.IsWhitespace()))
{
continue;
}
var indexAfterMatch = i + match.Length;
c = content.PeekCharAbsolute(indexAfterMatch);
if (!(c == '\0' || c.IsAsciiPunctuation() || c.IsWhitespace()))
{
continue;
}
// We should have a match, but in case...
Abbreviation abbr;
@@ -126,12 +115,13 @@ namespace Markdig.Extensions.Abbreviations
// If we don't have a container, create a new one
if (container == null)
{
container = new ContainerInline()
{
Span = originalLiteral.Span,
Line = originalLiteral.Line,
Column = originalLiteral.Column,
};
container = literal.Parent ??
new ContainerInline
{
Span = originalLiteral.Span,
Line = originalLiteral.Line,
Column = originalLiteral.Column,
};
}
int line;
@@ -150,13 +140,18 @@ namespace Markdig.Extensions.Abbreviations
// Append the previous literal
if (i > content.Start)
{
container.AppendChild(literal);
if (literal.Parent == null)
{
container.AppendChild(literal);
}
literal.Span.End = abbrInline.Span.Start - 1;
// Truncate it before the abbreviation
literal.Content.End = i - 1;
}
literal.Span.End = abbrInline.Span.Start - 1;
// Truncate it before the abbreviation
literal.Content.End = i - 1;
// Appned the abbreviation
container.AppendChild(abbrInline);
@@ -192,5 +187,55 @@ namespace Markdig.Extensions.Abbreviations
}
};
}
private static bool IsValidAbbreviation(string match, StringSlice content, int matchIndex)
{
// The word matched must be embraced by punctuation or whitespace or \0.
var index = matchIndex - 1;
while (index >= content.Start)
{
var c = content.PeekCharAbsolute(index);
if (!(c == '\0' || c.IsWhitespace() || c.IsAsciiPunctuation()))
{
return false;
}
if (c.IsAlphaNumeric())
{
return false;
}
if (!c.IsAsciiPunctuation() || c.IsWhitespace())
{
break;
}
index--;
}
// This will check if the next char at the end of the StringSlice is whitespace, punctuation or \0.
var contentNew = content;
contentNew.End = content.End + 1;
index = matchIndex + match.Length;
while (index <= contentNew.End)
{
var c = contentNew.PeekCharAbsolute(index);
if (!(c == '\0' || c.IsWhitespace() || c.IsAsciiPunctuation()))
{
return false;
}
if (c.IsAlphaNumeric())
{
return false;
}
if (c.IsWhitespace())
{
break;
}
index++;
}
return true;
}
}
}

View File

@@ -1,158 +1,199 @@
// 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.Collections.Generic;
using System.IO;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Renderers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
namespace Markdig.Extensions.AutoIdentifiers
{
/// <summary>
/// The auto-identifier extension
/// </summary>
/// <seealso cref="Markdig.IMarkdownExtension" />
public class AutoIdentifierExtension : IMarkdownExtension
{
private const string AutoIdentifierKey = "AutoIdentifier";
private readonly HtmlRenderer stripRenderer;
private readonly StringWriter headingWriter;
private readonly AutoIdentifierOptions options;
/// <summary>
/// Initializes a new instance of the <see cref="AutoIdentifierExtension"/> class.
/// </summary>
/// <param name="options">The options.</param>
public AutoIdentifierExtension(AutoIdentifierOptions options)
{
this.options = options;
headingWriter = new StringWriter();
// Use internally a HtmlRenderer to strip links from a heading
stripRenderer = new HtmlRenderer(headingWriter)
{
// Set to false both to avoid having any HTML tags in the output
EnableHtmlForInline = false,
EnableHtmlEscape = false
};
}
public void Setup(MarkdownPipelineBuilder pipeline)
{
var headingBlockParser = pipeline.BlockParsers.Find<HeadingBlockParser>();
if (headingBlockParser != null)
{
// Install a hook on the HeadingBlockParser when a HeadingBlock is actually processed
headingBlockParser.Closed -= HeadingBlockParser_Closed;
headingBlockParser.Closed += HeadingBlockParser_Closed;
}
var paragraphBlockParser = pipeline.BlockParsers.FindExact<ParagraphBlockParser>();
if (paragraphBlockParser != null)
{
// Install a hook on the ParagraphBlockParser when a HeadingBlock is actually processed as a Setex heading
paragraphBlockParser.Closed -= HeadingBlockParser_Closed;
paragraphBlockParser.Closed += HeadingBlockParser_Closed;
}
}
public void Setup(IMarkdownRenderer renderer)
{
}
/// <summary>
/// Process on a new <see cref="HeadingBlock"/>
/// </summary>
/// <param name="processor">The processor.</param>
/// <param name="block">The heading block.</param>
private void HeadingBlockParser_Closed(BlockProcessor processor, Block block)
{
// We may have a ParagraphBlock here as we have a hook on the ParagraphBlockParser
var headingBlock = block as HeadingBlock;
if (headingBlock == null)
{
return;
}
// If the AutoLink options is set, we register a LinkReferenceDefinition at the document level
if ((options & AutoIdentifierOptions.AutoLink) != 0)
{
var headingLine = headingBlock.Lines.Lines[0];
var text = headingLine.ToString();
var linkRef = new HeadingLinkReferenceDefinition()
{
Heading = headingBlock,
CreateLinkInline = CreateLinkInlineForHeading
};
processor.Document.SetLinkReferenceDefinition(text, linkRef);
}
// Then we register after inline have been processed to actually generate the proper #id
headingBlock.ProcessInlinesEnd += HeadingBlock_ProcessInlinesEnd;
}
/// <summary>
/// Callback when there is a reference to found to a heading.
/// Note that reference are only working if they are declared after.
/// </summary>
private Inline CreateLinkInlineForHeading(InlineProcessor inlineState, LinkReferenceDefinition linkRef, Inline child)
{
var headingRef = (HeadingLinkReferenceDefinition) linkRef;
return new LinkInline()
{
// Use GetDynamicUrl to allow late binding of the Url (as a link may occur before the heading is declared and
// the inlines of the heading are actually processed by HeadingBlock_ProcessInlinesEnd)
GetDynamicUrl = () => HtmlHelper.Unescape("#" + headingRef.Heading.GetAttributes().Id),
Title = HtmlHelper.Unescape(linkRef.Title),
};
}
/// <summary>
/// Process the inlines of the heading to create a unique identifier
/// </summary>
/// <param name="processor">The processor.</param>
/// <param name="inline">The inline.</param>
private void HeadingBlock_ProcessInlinesEnd(InlineProcessor processor, Inline inline)
{
var identifiers = processor.Document.GetData(AutoIdentifierKey) as HashSet<string>;
if (identifiers == null)
{
identifiers = new HashSet<string>();
processor.Document.SetData(AutoIdentifierKey, identifiers);
}
var headingBlock = (HeadingBlock) processor.Block;
if (headingBlock.Inline == null)
{
return;
}
// Use a HtmlRenderer with
stripRenderer.Render(headingBlock.Inline);
var headingText = headingWriter.ToString();
headingWriter.GetStringBuilder().Length = 0;
headingText = LinkHelper.Urilize(headingText, (options & AutoIdentifierOptions.AllowOnlyAscii) != 0);
var baseHeadingId = string.IsNullOrEmpty(headingText) ? "section" : headingText;
int index = 0;
var headingId = baseHeadingId;
var headingBuffer = StringBuilderCache.Local();
while (!identifiers.Add(headingId))
{
index++;
headingBuffer.Append(baseHeadingId);
headingBuffer.Append('-');
headingBuffer.Append(index);
headingId = headingBuffer.ToString();
headingBuffer.Length = 0;
}
processor.Block.GetAttributes().Id = headingId;
}
}
// 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.Collections.Generic;
using System.IO;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Renderers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
namespace Markdig.Extensions.AutoIdentifiers
{
/// <summary>
/// The auto-identifier extension
/// </summary>
/// <seealso cref="Markdig.IMarkdownExtension" />
public class AutoIdentifierExtension : IMarkdownExtension
{
private const string AutoIdentifierKey = "AutoIdentifier";
private readonly AutoIdentifierOptions options;
/// <summary>
/// Initializes a new instance of the <see cref="AutoIdentifierExtension"/> class.
/// </summary>
/// <param name="options">The options.</param>
public AutoIdentifierExtension(AutoIdentifierOptions options)
{
this.options = options;
}
public void Setup(MarkdownPipelineBuilder pipeline)
{
var headingBlockParser = pipeline.BlockParsers.Find<HeadingBlockParser>();
if (headingBlockParser != null)
{
// Install a hook on the HeadingBlockParser when a HeadingBlock is actually processed
headingBlockParser.Closed -= HeadingBlockParser_Closed;
headingBlockParser.Closed += HeadingBlockParser_Closed;
}
var paragraphBlockParser = pipeline.BlockParsers.FindExact<ParagraphBlockParser>();
if (paragraphBlockParser != null)
{
// Install a hook on the ParagraphBlockParser when a HeadingBlock is actually processed as a Setex heading
paragraphBlockParser.Closed -= HeadingBlockParser_Closed;
paragraphBlockParser.Closed += HeadingBlockParser_Closed;
}
}
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
}
/// <summary>
/// Process on a new <see cref="HeadingBlock"/>
/// </summary>
/// <param name="processor">The processor.</param>
/// <param name="block">The heading block.</param>
private void HeadingBlockParser_Closed(BlockProcessor processor, Block block)
{
// We may have a ParagraphBlock here as we have a hook on the ParagraphBlockParser
var headingBlock = block as HeadingBlock;
if (headingBlock == null)
{
return;
}
// If the AutoLink options is set, we register a LinkReferenceDefinition at the document level
if ((options & AutoIdentifierOptions.AutoLink) != 0)
{
var headingLine = headingBlock.Lines.Lines[0];
var text = headingLine.ToString();
var linkRef = new HeadingLinkReferenceDefinition()
{
Heading = headingBlock,
CreateLinkInline = CreateLinkInlineForHeading
};
var doc = processor.Document;
var dictionary = doc.GetData(this) as Dictionary<string, HeadingLinkReferenceDefinition>;
if (dictionary == null)
{
dictionary = new Dictionary<string, HeadingLinkReferenceDefinition>();
doc.SetData(this, dictionary);
doc.ProcessInlinesBegin += DocumentOnProcessInlinesBegin;
}
dictionary[text] = linkRef;
}
// Then we register after inline have been processed to actually generate the proper #id
headingBlock.ProcessInlinesEnd += HeadingBlock_ProcessInlinesEnd;
}
private void DocumentOnProcessInlinesBegin(InlineProcessor processor, Inline inline)
{
var doc = processor.Document;
doc.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;
var dictionary = (Dictionary<string, HeadingLinkReferenceDefinition>)doc.GetData(this);
foreach (var keyPair in dictionary)
{
// Here we make sure that auto-identifiers will not override an existing link definition
// defined in the document
// If it is the case, we skip the auto identifier for the Heading
if (!doc.TryGetLinkReferenceDefinition(keyPair.Key, out var linkDef))
{
doc.SetLinkReferenceDefinition(keyPair.Key, keyPair.Value);
}
}
// Once we are done, we don't need to keep the intermediate dictionary arround
doc.RemoveData(this);
}
/// <summary>
/// Callback when there is a reference to found to a heading.
/// Note that reference are only working if they are declared after.
/// </summary>
private Inline CreateLinkInlineForHeading(InlineProcessor inlineState, LinkReferenceDefinition linkRef, Inline child)
{
var headingRef = (HeadingLinkReferenceDefinition) linkRef;
return new LinkInline()
{
// Use GetDynamicUrl to allow late binding of the Url (as a link may occur before the heading is declared and
// the inlines of the heading are actually processed by HeadingBlock_ProcessInlinesEnd)
GetDynamicUrl = () => HtmlHelper.Unescape("#" + headingRef.Heading.GetAttributes().Id),
Title = HtmlHelper.Unescape(linkRef.Title),
};
}
/// <summary>
/// Process the inlines of the heading to create a unique identifier
/// </summary>
/// <param name="processor">The processor.</param>
/// <param name="inline">The inline.</param>
private void HeadingBlock_ProcessInlinesEnd(InlineProcessor processor, Inline inline)
{
var identifiers = processor.Document.GetData(AutoIdentifierKey) as HashSet<string>;
if (identifiers == null)
{
identifiers = new HashSet<string>();
processor.Document.SetData(AutoIdentifierKey, identifiers);
}
var headingBlock = (HeadingBlock) processor.Block;
if (headingBlock.Inline == null)
{
return;
}
// If id is already set, don't try to modify it
var attributes = processor.Block.GetAttributes();
if (attributes.Id != null)
{
return;
}
// Use internally a HtmlRenderer to strip links from a heading
var headingWriter = new StringWriter();
var stripRenderer = new HtmlRenderer(headingWriter)
{
// Set to false both to avoid having any HTML tags in the output
EnableHtmlForInline = false,
EnableHtmlEscape = false
};
stripRenderer.Render(headingBlock.Inline);
var headingText = headingWriter.ToString();
headingWriter.GetStringBuilder().Length = 0;
// Urilize the link
headingText = (options & AutoIdentifierOptions.GitHub) != 0
? LinkHelper.UrilizeAsGfm(headingText)
: LinkHelper.Urilize(headingText, (options & AutoIdentifierOptions.AllowOnlyAscii) != 0);
// If the heading is empty, use the word "section" instead
var baseHeadingId = string.IsNullOrEmpty(headingText) ? "section" : headingText;
// Add a trailing -1, -2, -3...etc. in case of collision
int index = 0;
var headingId = baseHeadingId;
var headingBuffer = StringBuilderCache.Local();
while (!identifiers.Add(headingId))
{
index++;
headingBuffer.Append(baseHeadingId);
headingBuffer.Append('-');
headingBuffer.Append(index);
headingId = headingBuffer.ToString();
headingBuffer.Length = 0;
}
attributes.Id = headingId;
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
@@ -12,7 +12,7 @@ namespace Markdig.Extensions.AutoIdentifiers
public enum AutoIdentifierOptions
{
/// <summary>
/// No options
/// No options: does not apply any additional formatting and/or transformations.
/// </summary>
None = 0,
@@ -30,5 +30,10 @@ namespace Markdig.Extensions.AutoIdentifiers
/// Allows only ASCII characters in the url (HTML 5 allows to have UTF8 characters). Default is <c>true</c>
/// </summary>
AllowOnlyAscii = 2,
/// <summary>
/// Renders auto identifiers like GitHub.
/// </summary>
GitHub = 4,
}
}
}

View File

@@ -0,0 +1,42 @@
// 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 Markdig.Renderers;
using Markdig.Renderers.Normalize;
using Markdig.Renderers.Normalize.Inlines;
namespace Markdig.Extensions.AutoLinks
{
/// <summary>
/// Extension to automatically create <see cref="LinkInline"/> when a link url http: or mailto: is found.
/// </summary>
/// <seealso cref="Markdig.IMarkdownExtension" />
public class AutoLinkExtension : IMarkdownExtension
{
public readonly string ValidPreviousCharacters;
public AutoLinkExtension(string validPreviousCharacters = AutoLinkParser.DefaultValidPreviousCharacters)
{
ValidPreviousCharacters = validPreviousCharacters;
}
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.InlineParsers.Contains<AutoLinkParser>())
{
// Insert the parser before any other parsers
pipeline.InlineParsers.Insert(0, new AutoLinkParser(ValidPreviousCharacters));
}
}
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var normalizeRenderer = renderer as NormalizeRenderer;
if (normalizeRenderer != null && !normalizeRenderer.ObjectRenderers.Contains<NormalizeAutoLinkRenderer>())
{
normalizeRenderer.ObjectRenderers.InsertBefore<LinkInlineRenderer>(new NormalizeAutoLinkRenderer());
}
}
}
}

View File

@@ -0,0 +1,257 @@
// 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.Collections.Generic;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Syntax.Inlines;
namespace Markdig.Extensions.AutoLinks
{
/// <summary>
/// The inline parser used to for autolinks.
/// </summary>
/// <seealso cref="Markdig.Parsers.InlineParser" />
public class AutoLinkParser : InlineParser
{
/// <summary>
/// Initializes a new instance of the <see cref="AutoLinkParser"/> class.
/// </summary>
public AutoLinkParser(string validPreviousCharacters = DefaultValidPreviousCharacters)
{
OpeningCharacters = new char[]
{
'h', // for http:// and https://
'f', // for ftp://
'm', // for mailto:
'w', // for www.
};
ValidPreviousCharacters = validPreviousCharacters;
}
// All such recognized autolinks can only come at the beginning of a line, after whitespace, or any of the delimiting characters *, _, ~, and (.
public readonly string ValidPreviousCharacters;
public const string DefaultValidPreviousCharacters = "*_~(";
public override bool Match(InlineProcessor processor, ref StringSlice slice)
{
// Previous char must be a whitespace or a punctuation
var previousChar = slice.PeekCharExtra(-1);
if (!previousChar.IsWhiteSpaceOrZero() && ValidPreviousCharacters.IndexOf(previousChar) == -1)
{
return false;
}
List<char> pendingEmphasis;
// Check that an autolink is possible in the current context
if (!IsAutoLinkValidInCurrentContext(processor, out pendingEmphasis))
{
return false;
}
var startPosition = slice.Start;
int domainOffset = 0;
var c = slice.CurrentChar;
// Precheck URL
switch (c)
{
case 'h':
if (slice.MatchLowercase("ttp://", 1))
{
domainOffset = 7; // http://
}
else if (slice.MatchLowercase("ttps://", 1))
{
domainOffset = 8; // https://
}
else return false;
break;
case 'f':
if (!slice.MatchLowercase("tp://", 1))
{
return false;
}
domainOffset = 6; // ftp://
break;
case 'm':
if (!slice.MatchLowercase("ailto:", 1))
{
return false;
}
break;
case 'w':
if (!slice.MatchLowercase("ww.", 1)) // We won't match http:/www. or /www.xxx
{
return false;
}
domainOffset = 4; // www.
break;
}
// Parse URL
string link;
if (!LinkHelper.TryParseUrl(ref slice, out link, true))
{
return false;
}
// If we have any pending emphasis, remove any pending emphasis characters from the end of the link
if (pendingEmphasis != null)
{
for (int i = link.Length - 1; i >= 0; i--)
{
if (pendingEmphasis.Contains(link[i]))
{
slice.Start--;
}
else
{
if (i < link.Length - 1)
{
link = link.Substring(0, i + 1);
}
break;
}
}
}
// Post-check URL
switch (c)
{
case 'h':
if (string.Equals(link, "http://", StringComparison.OrdinalIgnoreCase) ||
string.Equals(link, "https://", StringComparison.OrdinalIgnoreCase))
{
return false;
}
break;
case 'f':
if (string.Equals(link, "ftp://", StringComparison.OrdinalIgnoreCase))
{
return false;
}
break;
case 'm':
int atIndex = link.IndexOf('@');
if (atIndex == -1 ||
atIndex == 7) // mailto:@ - no email part
{
return false;
}
domainOffset = atIndex + 1;
break;
}
if (!LinkHelper.IsValidDomain(link, domainOffset))
{
return false;
}
int line;
int column;
var inline = new LinkInline()
{
Span =
{
Start = processor.GetSourcePosition(startPosition, out line, out column),
},
Line = line,
Column = column,
Url = c == 'w' ? "http://" + link : link,
IsClosed = true,
IsAutoLink = true,
};
var skipFromBeginning = c == 'm' ? 7 : 0; // For mailto: skip "mailto:" for content
inline.Span.End = inline.Span.Start + link.Length - 1;
inline.UrlSpan = inline.Span;
inline.AppendChild(new LiteralInline()
{
Span = inline.Span,
Line = line,
Column = column,
Content = new StringSlice(slice.Text, startPosition + skipFromBeginning, startPosition + link.Length - 1),
IsClosed = true
});
processor.Inline = inline;
return true;
}
private bool IsAutoLinkValidInCurrentContext(InlineProcessor processor, out List<char> pendingEmphasis)
{
pendingEmphasis = null;
// Case where there is a pending HtmlInline <a>
var currentInline = processor.Inline;
while (currentInline != null)
{
var htmlInline = currentInline as HtmlInline;
if (htmlInline != null)
{
// If we have a </a> we don't expect nested <a>
if (htmlInline.Tag.StartsWith("</a", StringComparison.OrdinalIgnoreCase))
{
break;
}
// If there is a pending <a>, we can't allow a link
if (htmlInline.Tag.StartsWith("<a", StringComparison.OrdinalIgnoreCase))
{
return false;
}
}
// Check previous sibling and parents in the tree
currentInline = currentInline.PreviousSibling ?? currentInline.Parent;
}
// Check that we don't have any pending brackets opened (where we could have a possible markdown link)
// NOTE: This assume that [ and ] are used for links, otherwise autolink will not work properly
currentInline = processor.Inline;
int countBrackets = 0;
while (currentInline != null)
{
var linkDelimiterInline = currentInline as LinkDelimiterInline;
if (linkDelimiterInline != null && linkDelimiterInline.IsActive)
{
if (linkDelimiterInline.Type == DelimiterType.Open)
{
countBrackets++;
}
else if (linkDelimiterInline.Type == DelimiterType.Close)
{
countBrackets--;
}
}
else
{
// Record all pending characters for emphasis
var emphasisDelimiter = currentInline as EmphasisDelimiterInline;
if (emphasisDelimiter != null)
{
if (pendingEmphasis == null)
{
// Not optimized for GC, but we don't expect this case much
pendingEmphasis = new List<char>();
}
if (!pendingEmphasis.Contains(emphasisDelimiter.DelimiterChar))
{
pendingEmphasis.Add(emphasisDelimiter.DelimiterChar);
}
}
}
currentInline = currentInline.Parent;
}
return countBrackets <= 0;
}
}
}

View File

@@ -0,0 +1,29 @@
using Markdig.Renderers;
using Markdig.Renderers.Normalize;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
namespace Markdig.Extensions.AutoLinks
{
public class NormalizeAutoLinkRenderer : NormalizeObjectRenderer<LinkInline>
{
public override bool Accept(RendererBase renderer, MarkdownObject obj)
{
if (base.Accept(renderer, obj))
{
var normalizeRenderer = renderer as NormalizeRenderer;
var link = obj as LinkInline;
return normalizeRenderer != null && link != null && !normalizeRenderer.Options.ExpandAutoLinks && link.IsAutoLink;
}
else
{
return false;
}
}
protected override void Write(NormalizeRenderer renderer, LinkInline obj)
{
renderer.Write(obj.Url);
}
}
}

View File

@@ -22,7 +22,7 @@ namespace Markdig.Extensions.Bootstrap
pipeline.DocumentProcessed += PipelineOnDocumentProcessed;
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
}

View File

@@ -24,7 +24,7 @@ namespace Markdig.Extensions.Citations
}
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)

View File

@@ -38,7 +38,7 @@ namespace Markdig.Extensions.CustomContainers
}
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)

View File

@@ -20,7 +20,7 @@ namespace Markdig.Extensions.DefinitionLists
}
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)

View File

@@ -51,8 +51,7 @@ namespace Markdig.Extensions.DefinitionLists
}
var previousParent = paragraphBlock.Parent;
var indexOfParagraph = previousParent.IndexOf(paragraphBlock);
var currentDefinitionList = indexOfParagraph - 1 >= 0 ? previousParent[indexOfParagraph - 1] as DefinitionList : null;
var currentDefinitionList = GetCurrentDefinitionList(paragraphBlock, previousParent);
processor.Discard(paragraphBlock);
@@ -98,11 +97,24 @@ namespace Markdig.Extensions.DefinitionLists
processor.Open(definitionItem);
// Update the end position
currentDefinitionList.Span.End = processor.Line.End;
currentDefinitionList.UpdateSpanEnd(processor.Line.End);
return BlockState.Continue;
}
private static DefinitionList GetCurrentDefinitionList(ParagraphBlock paragraphBlock, ContainerBlock previousParent)
{
var index = previousParent.IndexOf(paragraphBlock) - 1;
if (index < 0) return null;
var lastBlock = previousParent[index];
if (lastBlock is BlankLineBlock)
{
lastBlock = previousParent[index - 1];
previousParent.RemoveAt(index);
}
return lastBlock as DefinitionList;
}
public override BlockState TryContinue(BlockProcessor processor, Block block)
{
var definitionItem = (DefinitionItem)block;

View File

@@ -49,7 +49,7 @@ namespace Markdig.Extensions.DefinitionLists
{
if (!hasOpendd)
{
renderer.Write("<dd>");
renderer.Write("<dd").WriteAttributes(definitionItem).Write(">");
countdd = 0;
hasOpendd = true;
}

View File

@@ -0,0 +1,32 @@
// 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 Markdig.Renderers;
using Markdig.Renderers.Html;
namespace Markdig.Extensions.Diagrams
{
/// <summary>
/// Extension to allow diagrams.
/// </summary>
/// <seealso cref="Markdig.IMarkdownExtension" />
public class DiagramExtension : IMarkdownExtension
{
public void Setup(MarkdownPipelineBuilder pipeline)
{
}
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
var codeRenderer = htmlRenderer.ObjectRenderers.FindExact<CodeBlockRenderer>();
// TODO: Add other well known diagram languages
codeRenderer.BlocksAsDiv.Add("mermaid");
codeRenderer.BlocksAsDiv.Add("nomnoml");
}
}
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
@@ -12,16 +12,23 @@ namespace Markdig.Extensions.Emoji
/// <seealso cref="Markdig.IMarkdownExtension" />
public class EmojiExtension : IMarkdownExtension
{
public EmojiExtension(bool enableSmiley = true)
{
EnableSmiley = enableSmiley;
}
public bool EnableSmiley { get; set; }
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.InlineParsers.Contains<EmojiParser>())
{
// Insert the parser before any other parsers
pipeline.InlineParsers.Insert(0, new EmojiParser());
pipeline.InlineParsers.Insert(0, new EmojiParser(EnableSmiley));
}
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
@@ -24,13 +24,19 @@ namespace Markdig.Extensions.Emoji
/// <summary>
/// Initializes a new instance of the <see cref="EmojiParser"/> class.
/// </summary>
public EmojiParser()
public EmojiParser(bool enableSmiley = true)
{
EnableSmiley = enableSmiley;
OpeningCharacters = null;
EmojiToUnicode = new Dictionary<string, string>(EmojiToUnicodeDefault);
SmileyToEmoji = new Dictionary<string, string>(SmileyToEmojiDefault);
}
/// <summary>
/// Gets or sets a boolean indicating whether to process smiley.
/// </summary>
public bool EnableSmiley { get; set; }
/// <summary>
/// Gets the emoji to unicode mapping. This can be modified before this parser is initialized.
/// </summary>
@@ -41,7 +47,7 @@ namespace Markdig.Extensions.Emoji
/// </summary>
public Dictionary<string, string> SmileyToEmoji { get; }
public override void Initialize(InlineProcessor processor)
public override void Initialize()
{
var firstChars = new HashSet<char>();
var textToMatch = new HashSet<string>();
@@ -67,18 +73,31 @@ namespace Markdig.Extensions.Emoji
public override bool Match(InlineProcessor processor, ref StringSlice slice)
{
string match;
// Previous char must be a space
if (!slice.PeekCharExtra(-1).IsWhiteSpaceOrZero())
{
return false;
}
// Try to match an existing emoji
var startPosition = slice.Start;
if (!textMatchHelper.TryMatch(slice.Text, slice.Start, slice.Length, out match))
{
return false;
}
string emoji;
if (!SmileyToEmoji.TryGetValue(match, out emoji))
string emoji = match;
if (EnableSmiley)
{
emoji = match;
// If we have a smiley, we decode it to emoji
if (!SmileyToEmoji.TryGetValue(match, out emoji))
{
emoji = match;
}
}
// Decode the eomji to unicode
string unicode;
if (!EmojiToUnicode.TryGetValue(emoji, out unicode))
{
@@ -981,7 +1000,16 @@ namespace Markdig.Extensions.Emoji
{":large_orange_diamond:", "🔶"},
{":large_blue_diamond:", "🔷"},
{":small_orange_diamond:", "🔸"},
{":small_blue_diamond:", "🔹"}
{":small_blue_diamond:", "🔹"},
// Custom additions
{ ":custom_arrow_left:", "←"},
{ ":custom_arrow_right:", "→"},
{ ":custom_arrow_left_right:", "↔"},
{ ":custom_arrow_left_strong:", "⇐"},
{ ":custom_arrow_right_strong:", "⇒"},
{ ":custom_arrow_left_right_strong:", "⇔"},
};
SmileyToEmojiDefault = new Dictionary<string, string>()
@@ -1051,6 +1079,15 @@ namespace Markdig.Extensions.Emoji
{":-$", ":unamused:"},
{";)", ":wink:"},
{";-)", ":wink:"},
// Custom arrows
{"<-", ":custom_arrow_left:" },
{"->", ":custom_arrow_rigth:" },
{"<->", ":custom_arrow_left_rigth:" },
{"<=", ":custom_arrow_left_strong:" },
{"=>", ":custom_arrow_rigth_strong:" },
{"<=>", ":custom_arrow_left_rigth_strong:" },
};
}
#endregion

View File

@@ -85,7 +85,7 @@ namespace Markdig.Extensions.EmphasisExtras
}
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)

View File

@@ -114,7 +114,7 @@ namespace Markdig.Extensions.Figures
figure.Add(caption);
}
figure.Span.End = line.End;
figure.UpdateSpanEnd(line.End);
// Don't keep the last line
return BlockState.BreakDiscard;
@@ -123,7 +123,7 @@ namespace Markdig.Extensions.Figures
// Reset the indentation to the column before the indent
processor.GoToColumn(processor.ColumnBeforeIndent);
figure.Span.End = line.End;
figure.UpdateSpanEnd(line.End);
return BlockState.Continue;
}

View File

@@ -29,7 +29,7 @@ namespace Markdig.Extensions.Figures
}
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)

View File

@@ -80,7 +80,7 @@ namespace Markdig.Extensions.Footers
{
processor.NextChar(); // Skip following space
}
block.Span.End = processor.Line.End;
block.UpdateSpanEnd(processor.Line.End);
}
return result;
}

View File

@@ -29,7 +29,7 @@ namespace Markdig.Extensions.Footers
}
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)

View File

@@ -20,7 +20,7 @@ namespace Markdig.Extensions.Footnotes
}
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)

View File

@@ -5,8 +5,8 @@
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using Markdig.Syntax.Inlines;
namespace Markdig.Extensions.Footnotes
{
/// <summary>
@@ -26,9 +26,15 @@ namespace Markdig.Extensions.Footnotes
}
public override BlockState TryOpen(BlockProcessor processor)
{
return TryOpen(processor, false);
}
private BlockState TryOpen(BlockProcessor processor, bool isContinue)
{
// We expect footnote to appear only at document level and not indented more than a code indent block
if (processor.IsCodeIndent || processor.CurrentContainer.GetType() != typeof(MarkdownDocument) )
var currentContainer = processor.GetCurrentContainerOpened();
if (processor.IsCodeIndent || (!isContinue && currentContainer.GetType() != typeof(MarkdownDocument)) || (isContinue && !(currentContainer is FootnoteGroup)))
{
return BlockState.None;
}
@@ -42,7 +48,7 @@ namespace Markdig.Extensions.Footnotes
processor.GoToColumn(saved);
return BlockState.None;
}
// Advance the column
int deltaColumn = processor.Start - start;
processor.Column = processor.Column + deltaColumn;
@@ -69,7 +75,11 @@ namespace Markdig.Extensions.Footnotes
var linkRef = new FootnoteLinkReferenceDefinition()
{
Footnote = footnote,
CreateLinkInline = CreateLinkToFootnote
CreateLinkInline = CreateLinkToFootnote,
Line = processor.LineIndex,
Span = new SourceSpan(start, processor.Start - 2), // account for ]:
LabelSpan = labelSpan,
Label = label
};
processor.Document.SetLinkReferenceDefinition(footnote.Label, linkRef);
processor.NewBlocks.Push(footnote);
@@ -88,9 +98,23 @@ namespace Markdig.Extensions.Footnotes
return BlockState.ContinueDiscard;
}
if (footnote.IsLastLineEmpty && processor.Column == 0)
if (processor.Column == 0)
{
return BlockState.Break;
if (footnote.IsLastLineEmpty)
{
// Close the current footnote
processor.Close(footnote);
// Parse any opening footnote
return TryOpen(processor);
}
// Make sure that consecutive footnotes without a blanklines are parsed correctly
if (TryOpen(processor, true) == BlockState.Continue)
{
processor.Close(footnote);
return BlockState.Continue;
}
}
}
footnote.IsLastLineEmpty = false;

View File

@@ -37,7 +37,7 @@ namespace Markdig.Extensions.GenericAttributes
}
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
}
@@ -46,7 +46,7 @@ namespace Markdig.Extensions.GenericAttributes
// Try to find if there is any attributes { in the info string on the first line of a FencedCodeBlock
if (line.Start < line.End)
{
var indexOfAttributes = line.Text.LastIndexOf('{', line.End);
int indexOfAttributes = line.IndexOf('{');
if (indexOfAttributes >= 0)
{
// Work on a copy

View File

@@ -59,12 +59,12 @@ namespace Markdig.Extensions.GenericAttributes
{
objectToAttach = parent[indexOfParagraph + 1];
// We can remove the paragraph as it is empty
parent.RemoveAt(indexOfParagraph);
paragraph.RemoveAfterProcessInlines = true;
}
}
var currentHtmlAttributes = objectToAttach.GetAttributes();
attributes.CopyTo(currentHtmlAttributes);
attributes.CopyTo(currentHtmlAttributes, true, false);
// Update the position of the attributes
int line;
@@ -171,9 +171,10 @@ namespace Markdig.Extensions.GenericAttributes
// Skip any whitespaces
line.TrimStart();
c = line.CurrentChar;
// Handle boolean properties that are not followed by =
if ((hasSpace && (line.CurrentChar == '.' || line.CurrentChar == '#' || IsStartAttributeName(line.CurrentChar))) || line.CurrentChar == '}')
if ((hasSpace && (c == '.' || c == '#' || IsStartAttributeName(c))) || c == '}')
{
if (properties == null)
{

View File

@@ -0,0 +1,108 @@
// 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 Markdig.Extensions.Tables;
using Markdig.Extensions.TaskLists;
using Markdig.Helpers;
using Markdig.Renderers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using System.Collections.Generic;
namespace Markdig.Extensions.Globalization
{
/// <summary>
/// Extension to add support for RTL content.
/// </summary>
public class GlobalizationExtension : IMarkdownExtension
{
public void Setup(MarkdownPipelineBuilder pipeline)
{
// Make sure we don't have a delegate twice
pipeline.DocumentProcessed -= Pipeline_DocumentProcessed;
pipeline.DocumentProcessed += Pipeline_DocumentProcessed;
}
private void Pipeline_DocumentProcessed(MarkdownDocument document)
{
foreach (var node in document.Descendants())
{
if (node is TableRow || node is TableCell || node is ListItemBlock)
continue;
if (ShouldBeRightToLeft(node))
{
var attributes = node.GetAttributes();
attributes.AddPropertyIfNotExist("dir", "rtl");
if (node is Table table)
{
attributes.AddPropertyIfNotExist("align", "right");
}
}
}
}
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
}
private bool ShouldBeRightToLeft(MarkdownObject item)
{
if (item is IEnumerable<MarkdownObject> container)
{
foreach (var child in container)
{
// TaskList items contain an "X", which will cause
// the function to always return false.
if (child is TaskList)
continue;
return ShouldBeRightToLeft(child);
}
}
else if (item is LeafBlock leaf)
{
return ShouldBeRightToLeft(leaf.Inline);
}
else if (item is LiteralInline literal)
{
return StartsWithRtlCharacter(literal.ToString());
}
foreach (var descendant in item.Descendants())
{
if (descendant is ParagraphBlock p)
{
foreach (var i in p.Inline)
{
if (i is LiteralInline l)
{
return StartsWithRtlCharacter(l.ToString());
}
}
}
}
return false;
}
private bool StartsWithRtlCharacter(string text)
{
foreach (var c in CharHelper.ToUtf32(text))
{
if (CharHelper.IsRightToLeft(c))
return true;
else if (CharHelper.IsLeftToRight(c))
return false;
}
return false;
}
}
}

View File

@@ -24,7 +24,7 @@ namespace Markdig.Extensions.Hardlines
}
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
}
}

View File

@@ -0,0 +1,31 @@
// 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.Diagnostics;
using Markdig.Helpers;
using Markdig.Syntax.Inlines;
namespace Markdig.Extensions.JiraLinks
{
/// <summary>
/// Model for a JIRA link item
/// </summary>
[DebuggerDisplay("{ProjectKey}-{Issue}")]
public class JiraLink : LinkInline
{
public JiraLink()
{
IsClosed = true;
}
/// <summary>
/// JIRA Project Key
/// </summary>
public StringSlice ProjectKey { get; set; }
/// <summary>
/// JIRA Issue Number
/// </summary>
public StringSlice Issue { get; set; }
}
}

View File

@@ -0,0 +1,45 @@
// 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 Markdig.Parsers.Inlines;
using Markdig.Renderers;
using Markdig.Renderers.Normalize.Inlines;
using Markdig.Renderers.Normalize;
namespace Markdig.Extensions.JiraLinks
{
/// <summary>
/// Simple inline parser extension for Markdig to find, and
/// automatically add links to JIRA issue numbers.
/// </summary>
public class JiraLinkExtension : IMarkdownExtension
{
private readonly JiraLinkOptions _options;
public JiraLinkExtension(JiraLinkOptions options)
{
_options = options;
}
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.InlineParsers.Contains<JiraLinkInlineParser>())
{
// Insert the parser before the link inline parser
pipeline.InlineParsers.InsertBefore<LinkInlineParser>(new JiraLinkInlineParser(_options));
}
}
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
// No HTML renderer required, since JiraLink type derives from InlineLink (which already has an HTML renderer)
var normalizeRenderer = renderer as NormalizeRenderer;
if (normalizeRenderer != null && !normalizeRenderer.ObjectRenderers.Contains<NormalizeJiraLinksRenderer>())
{
normalizeRenderer.ObjectRenderers.InsertBefore<LinkInlineRenderer>(new NormalizeJiraLinksRenderer());
}
}
}
}

View File

@@ -0,0 +1,115 @@
// 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.Text;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Renderers.Html;
using Markdig.Syntax.Inlines;
namespace Markdig.Extensions.JiraLinks
{
/// <summary>
/// Finds and replaces JIRA links inline
/// </summary>
public class JiraLinkInlineParser : InlineParser
{
private readonly JiraLinkOptions _options;
private readonly string _baseUrl;
public JiraLinkInlineParser(JiraLinkOptions options)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
_baseUrl = _options.GetUrl();
//look for uppercase chars at the start (for the project key)
OpeningCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
}
public override bool Match(InlineProcessor processor, ref StringSlice slice)
{
// Allow preceding whitespace or `(`
var pc = slice.PeekCharExtra(-1);
if (!pc.IsWhiteSpaceOrZero() && pc != '(')
{
return false;
}
var current = slice.CurrentChar;
var startKey = slice.Start;
var endKey = slice.Start;
//read as many uppercase characters as required - project key
while (current.IsAlphaUpper())
{
endKey = slice.Start;
current = slice.NextChar();
}
//require a '-' between key and issue number
if (!current.Equals('-'))
{
return false;
}
current = slice.NextChar(); // skip -
//read as many numbers as required - issue number
if (!current.IsDigit())
{
return false;
}
var startIssue = slice.Start;
var endIssue = slice.Start;
while (current.IsDigit())
{
endIssue = slice.Start;
current = slice.NextChar();
}
if (!current.IsWhiteSpaceOrZero() && current != ')') //can be followed only by a whitespace or `)`
{
return false;
}
int line;
int column;
var jiraLink = new JiraLink() //create the link at the relevant position
{
Span =
{
Start = processor.GetSourcePosition(slice.Start, out line, out column)
},
Line = line,
Column = column,
Issue = new StringSlice(slice.Text, startIssue, endIssue),
ProjectKey = new StringSlice(slice.Text, startKey, endKey),
};
jiraLink.Span.End = jiraLink.Span.Start + (endIssue - startKey);
// Builds the Url
var builder = new StringBuilder();
builder.Append(_baseUrl).Append('/').Append(jiraLink.ProjectKey).Append('-').Append(jiraLink.Issue);
jiraLink.Url = builder.ToString();
// Builds the Label
builder.Length = 0;
builder.Append(jiraLink.ProjectKey).Append('-').Append(jiraLink.Issue);
jiraLink.AppendChild(new LiteralInline(builder.ToString()));
if (_options.OpenInNewWindow)
{
jiraLink.GetAttributes().AddProperty("target", "blank");
}
processor.Inline = jiraLink;
return true;
}
}
}

View File

@@ -0,0 +1,57 @@
// 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 Markdig.Extensions.JiraLinks
{
/// <summary>
/// Available options for replacing JIRA links
/// </summary>
public class JiraLinkOptions
{
/// <summary>
/// The base Url (e.g. `https://mycompany.atlassian.net`)
/// </summary>
public string BaseUrl { get; set; }
/// <summary>
/// The base path after the base url (default is `/browse`)
/// </summary>
public string BasePath { get; set; }
/// <summary>
/// Should the link open in a new window when clicked
/// </summary>
public bool OpenInNewWindow { get; set; }
public JiraLinkOptions(string baseUrl)
{
OpenInNewWindow = true; //default
BaseUrl = baseUrl;
BasePath = "/browse";
}
/// <summary>
/// Gets the full url composed of the <see cref="BaseUrl"/> and <see cref="BasePath"/> with no trailing `/`
/// </summary>
public virtual string GetUrl()
{
var url = new StringBuilder();
var baseUrl = BaseUrl;
if (baseUrl != null)
{
url.Append(baseUrl.TrimEnd('/'));
}
url.Append("/");
if (BasePath != null)
{
url.Append(BasePath.Trim('/'));
}
return url.ToString();
}
}
}

View File

@@ -0,0 +1,18 @@
using Markdig.Renderers.Normalize;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Markdig.Extensions.JiraLinks
{
public class NormalizeJiraLinksRenderer : NormalizeObjectRenderer<JiraLink>
{
protected override void Write(NormalizeRenderer renderer, JiraLink obj)
{
renderer.Write(obj.ProjectKey);
renderer.Write("-");
renderer.Write(obj.Issue);
}
}
}

View File

@@ -22,7 +22,7 @@ namespace Markdig.Extensions.ListExtras
}
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
}
}

View File

@@ -7,6 +7,8 @@ using Markdig.Parsers;
namespace Markdig.Extensions.ListExtras
{
using System;
/// <summary>
/// Parser that adds supports for parsing alpha/roman list items (e.g: `a)` or `a.` or `ii.` or `II.`)
/// </summary>
@@ -52,7 +54,7 @@ namespace Markdig.Extensions.ListExtras
c = state.NextChar();
}
result.OrderedStart = state.Line.Text.Substring(startChar, endChar - startChar + 1);
result.OrderedStart = CharHelper.RomanToArabic(state.Line.Text.Substring(startChar, endChar - startChar + 1)).ToString();
result.BulletType = isRomanLow ? 'i' : 'I';
result.DefaultOrderedStart = isRomanLow ? "i" : "I";
}
@@ -60,7 +62,7 @@ namespace Markdig.Extensions.ListExtras
{
// otherwise we expect a regular alpha lettered list with a single character.
var isUpper = c.IsAlphaUpper();
result.OrderedStart = c.ToString();
result.OrderedStart = (Char.ToUpperInvariant(c) - 64).ToString();
result.BulletType = isUpper ? 'A' : 'a';
result.DefaultOrderedStart = isUpper ? "A" : "a";
state.NextChar();

View File

@@ -1,15 +1,18 @@
// 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 Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
namespace Markdig.Extensions.Mathematics
{
/// <summary>
/// The block parser for a <see cref="MathBlock"/>.
/// </summary>
/// <seealso cref="Markdig.Parsers.FencedBlockParserBase{Markdig.Extensions.Mathematics.MathBlock}" />
/// <seealso cref="MathBlock" />
public class MathBlockParser : FencedBlockParserBase<MathBlock>
{
/// <summary>
@@ -22,6 +25,8 @@ namespace Markdig.Extensions.Mathematics
MinimumMatchCount = 2;
MaximumMatchCount = 2;
InfoParser = NoInfoParser;
DefaultClass = "math";
// We don't need a prefix
@@ -39,5 +44,19 @@ namespace Markdig.Extensions.Mathematics
}
return block;
}
private static bool NoInfoParser(BlockProcessor state, ref StringSlice line, IFencedBlock fenced)
{
var c = line.CurrentChar;
for (int i = line.Start; i <= line.End; i++)
{
c = line.Text[i];
if (!c.IsSpaceOrTab())
{
return false;
}
}
return true;
}
}
}

View File

@@ -29,7 +29,7 @@ namespace Markdig.Extensions.Mathematics
}
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)

View File

@@ -13,7 +13,7 @@ namespace Markdig.Extensions.Mathematics
/// An inline parser for <see cref="MathInline"/>.
/// </summary>
/// <seealso cref="Markdig.Parsers.InlineParser" />
/// <seealso cref="Markdig.Parsers.IDelimiterProcessor" />
/// <seealso cref="IPostInlineProcessor" />
public class MathInlineParser : InlineParser
{
/// <summary>
@@ -50,11 +50,18 @@ namespace Markdig.Extensions.Mathematics
c = slice.NextChar();
}
bool canOpen;
bool canClose;
// Check that opening $/$$ is correct, using the same heuristics than for emphasis delimiters
CharHelper.CheckOpenCloseDelimiter(pc, c, false, out canOpen, out canClose);
if (!canOpen)
bool openPrevIsPunctuation;
bool openPrevIsWhiteSpace;
bool openNextIsPunctuation;
bool openNextIsWhiteSpace;
bool openNextIsDigit = c.IsDigit();
pc.CheckUnicodeCategory(out openPrevIsWhiteSpace, out openPrevIsPunctuation);
c.CheckUnicodeCategory(out openNextIsWhiteSpace, out openNextIsPunctuation);
// Check that opening $/$$ is correct, using the different heuristics than for emphasis delimiters
// If a $/$$ is not preceded by a whitespace or punctuation, or followed by a digit
// this is a not a math block
if ((!openPrevIsWhiteSpace && !openPrevIsPunctuation) || openNextIsDigit)
{
return false;
}
@@ -62,24 +69,60 @@ namespace Markdig.Extensions.Mathematics
bool isMatching = false;
int closeDollars = 0;
// Eat any leading spaces
while (c.IsSpaceOrTab())
{
c = slice.NextChar();
}
var start = slice.Start;
var end = 0;
pc = match;
var lastWhiteSpace = -1;
while (c != '\0')
{
// Don't allow newline in an inline math expression
if (c == '\r' || c == '\n')
{
return false;
}
// Don't process sticks if we have a '\' as a previous char
if (pc != '\\' )
{
while (c == match)
// Record continous whitespaces at the end
if (c.IsSpaceOrTab())
{
closeDollars++;
c = slice.NextChar();
if (lastWhiteSpace < 0)
{
lastWhiteSpace = slice.Start;
}
}
else
{
bool hasClosingDollars = c == match;
if (hasClosingDollars)
{
while (c == match)
{
closeDollars++;
c = slice.NextChar();
}
}
if (closeDollars >= openDollars)
{
break;
if (closeDollars >= openDollars)
{
break;
}
lastWhiteSpace = -1;
if (hasClosingDollars)
{
pc = match;
continue;
}
}
pc = match;
}
if (closeDollars > 0)
@@ -95,13 +138,30 @@ namespace Markdig.Extensions.Mathematics
if (closeDollars >= openDollars)
{
// Check that closing $/$$ is correct
CharHelper.CheckOpenCloseDelimiter(pc, c, false, out canOpen, out canClose);
if (!canClose || c.IsDigit())
bool closePrevIsPunctuation;
bool closePrevIsWhiteSpace;
bool closeNextIsPunctuation;
bool closeNextIsWhiteSpace;
pc.CheckUnicodeCategory(out closePrevIsWhiteSpace, out closePrevIsPunctuation);
c.CheckUnicodeCategory(out closeNextIsWhiteSpace, out closeNextIsPunctuation);
// A closing $/$$ should be followed by at least a punctuation or a whitespace
// and if the character after an openning $/$$ was a whitespace, it should be
// a whitespace as well for the character preceding the closing of $/$$
if ((!closeNextIsPunctuation && !closeNextIsWhiteSpace) || (openNextIsWhiteSpace != closePrevIsWhiteSpace))
{
return false;
}
if (closePrevIsWhiteSpace && lastWhiteSpace > 0)
{
end = lastWhiteSpace + openDollars - 1;
}
else
{
end = slice.Start - 1;
}
// Create a new MathInline
int line;
int column;
@@ -116,7 +176,7 @@ namespace Markdig.Extensions.Mathematics
};
inline.Content.Start = start;
// We substract the end to the number of opening $ to keep inside the block the additionals $
inline.Content.End = inline.Content.End - openDollars;
inline.Content.End = end - openDollars;
// Add the default class if necessary
if (DefaultClass != null)

View File

@@ -1,8 +1,10 @@
// 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.Collections.Generic;
using System.Linq;
using Markdig.Renderers;
using Markdig.Renderers.Html;
using Markdig.Renderers.Html.Inlines;
@@ -31,7 +33,7 @@ namespace Markdig.Extensions.MediaLinks
{
}
public void Setup(IMarkdownRenderer renderer)
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
@@ -47,89 +49,178 @@ namespace Markdig.Extensions.MediaLinks
private bool TryLinkInlineRenderer(HtmlRenderer renderer, LinkInline linkInline)
{
if (linkInline.IsImage && linkInline.Url != null)
if (!linkInline.IsImage || linkInline.Url == null)
{
Uri uri;
// Only process absolute Uri
if (Uri.TryCreate(linkInline.Url, UriKind.RelativeOrAbsolute, out uri) && uri.IsAbsoluteUri)
return false;
}
Uri uri;
// Only process absolute Uri
if (!Uri.TryCreate(linkInline.Url, UriKind.RelativeOrAbsolute, out uri) || !uri.IsAbsoluteUri)
{
return false;
}
if (TryRenderIframeFromKnownProviders(uri, renderer, linkInline))
{
return true;
}
if (TryGuessAudioVideoFile(uri, renderer, linkInline))
{
return true;
}
return false;
}
private static HtmlAttributes GetHtmlAttributes(LinkInline linkInline)
{
var htmlAttributes = new HtmlAttributes();
var fromAttributes = linkInline.TryGetAttributes();
if (fromAttributes != null)
{
fromAttributes.CopyTo(htmlAttributes, false, false);
}
return htmlAttributes;
}
private bool TryGuessAudioVideoFile(Uri uri, HtmlRenderer renderer, LinkInline linkInline)
{
var path = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped);
// Otherwise try to detect if we have an audio/video from the file extension
string mimeType;
var lastDot = path.LastIndexOf('.');
if (lastDot >= 0 &&
Options.ExtensionToMimeType.TryGetValue(path.Substring(lastDot), out mimeType))
{
var htmlAttributes = GetHtmlAttributes(linkInline);
var isAudio = mimeType.StartsWith("audio");
var tagType = isAudio ? "audio" : "video";
renderer.Write($"<{tagType}");
htmlAttributes.AddPropertyIfNotExist("width", Options.Width);
if (!isAudio)
{
var htmlAttributes = new HtmlAttributes();
var fromAttributes = linkInline.TryGetAttributes();
if (fromAttributes != null)
{
fromAttributes.CopyTo(htmlAttributes, false, false);
}
// TODO: this code is not pluggable, so for now, we handle only the following web providers:
// - youtube
// - vimeo
var path = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped);
string iFrameUrl = null;
if (uri.Host.StartsWith("www.youtube.com", StringComparison.OrdinalIgnoreCase))
{
var query = SplitQuery(uri);
if (query.Length > 0 && query[0].StartsWith("v="))
{
iFrameUrl = $"https://www.youtube.com/embed/{query[0].Substring(2)}";
}
}
else if (uri.Host.StartsWith("vimeo.com", StringComparison.OrdinalIgnoreCase))
{
var items = path.Split('/');
if (items.Length > 0)
{
iFrameUrl = $"https://player.vimeo.com/video/{items[items.Length - 1]}";
}
}
if (iFrameUrl != null)
{
renderer.Write($"<iframe src=\"{iFrameUrl}\"");
htmlAttributes.AddPropertyIfNotExist("width", Options.Width);
htmlAttributes.AddPropertyIfNotExist("height", Options.Height);
htmlAttributes.AddPropertyIfNotExist("frameborder", "0");
htmlAttributes.AddPropertyIfNotExist("allowfullscreen", null);
renderer.WriteAttributes(htmlAttributes);
renderer.Write("></iframe>");
return true;
}
else
{
// Otherwise try to detect if we have an audio/video from the file extension
string mimeType;
var lastDot = path.LastIndexOf('.');
if (lastDot >= 0 &&
Options.ExtensionToMimeType.TryGetValue(path.Substring(lastDot), out mimeType))
{
var isAudio = mimeType.StartsWith("audio");
var tagType = isAudio ? "audio" : "video";
renderer.Write($"<{tagType}");
htmlAttributes.AddPropertyIfNotExist("width", Options.Width);
if (!isAudio)
{
htmlAttributes.AddPropertyIfNotExist("height", Options.Height);
}
htmlAttributes.AddPropertyIfNotExist("controls", null);
renderer.WriteAttributes(htmlAttributes);
renderer.Write($"><source type=\"{mimeType}\" src=\"{linkInline.Url}\"></source></{tagType}>");
renderer.Write("</iframe>");
return true;
}
}
htmlAttributes.AddPropertyIfNotExist("height", Options.Height);
}
htmlAttributes.AddPropertyIfNotExist("controls", null);
renderer.WriteAttributes(htmlAttributes);
renderer.Write($"><source type=\"{mimeType}\" src=\"{linkInline.Url}\"></source></{tagType}>");
return true;
}
return false;
}
#region Known providers
private class KnownProvider
{
public string HostPrefix { get; set; }
public Func<Uri, string> Delegate { get; set; }
public bool AllowFullScreen { get; set; } = true; //Should be false for audio embedding
}
private static readonly List<KnownProvider> KnownHosts = new List<KnownProvider>()
{
new KnownProvider {HostPrefix = "www.youtube.com", Delegate = YouTube},
new KnownProvider {HostPrefix = "vimeo.com", Delegate = Vimeo},
new KnownProvider {HostPrefix = "music.yandex.ru", Delegate = Yandex, AllowFullScreen = false},
new KnownProvider {HostPrefix = "ok.ru", Delegate = Odnoklassniki},
};
private bool TryRenderIframeFromKnownProviders(Uri uri, HtmlRenderer renderer, LinkInline linkInline)
{
var foundProvider =
KnownHosts
.Where(pair => uri.Host.StartsWith(pair.HostPrefix, StringComparison.OrdinalIgnoreCase)) // when host is match
.Select(provider =>
new
{
provider.AllowFullScreen,
Result = provider.Delegate(uri) // try to call delegate to get iframeUrl
}
)
.FirstOrDefault(provider => provider.Result != null); // use first success
if (foundProvider == null)
{
return false;
}
var htmlAttributes = GetHtmlAttributes(linkInline);
renderer.Write($"<iframe src=\"{foundProvider.Result}\"");
if(!string.IsNullOrEmpty(Options.Width))
htmlAttributes.AddPropertyIfNotExist("width", Options.Width);
if (!string.IsNullOrEmpty(Options.Height))
htmlAttributes.AddPropertyIfNotExist("height", Options.Height);
if (!string.IsNullOrEmpty(Options.Class))
htmlAttributes.AddPropertyIfNotExist("class", Options.Class);
htmlAttributes.AddPropertyIfNotExist("frameborder", "0");
if (foundProvider.AllowFullScreen)
{
htmlAttributes.AddPropertyIfNotExist("allowfullscreen", null);
}
renderer.WriteAttributes(htmlAttributes);
renderer.Write("></iframe>");
return true;
}
private static readonly string[] SplitAnd = {"&"};
private static string[] SplitQuery(Uri uri)
{
var query = uri.Query.Substring(uri.Query.IndexOf('?') + 1);
return query.Split(SplitAnd, StringSplitOptions.RemoveEmptyEntries);
}
private static string YouTube(Uri uri)
{
var query = SplitQuery(uri);
return query.Length > 0 && query[0].StartsWith("v=")
? $"https://www.youtube.com/embed/{query[0].Substring(2)}"
: null;
}
private static string Vimeo(Uri uri)
{
var items = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped).Split('/');
return items.Length > 0 ? $"https://player.vimeo.com/video/{items[items.Length - 1]}" : null;
}
private static string Odnoklassniki(Uri uri)
{
var items = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped).Split('/');
return items.Length > 0 ? $"https://ok.ru/videoembed/{items[items.Length - 1]}" : null;
}
private static string Yandex(Uri uri)
{
var items = uri.GetComponents(UriComponents.Path, UriFormat.Unescaped).Split('/');
var albumKeyword
= items.Skip(0).FirstOrDefault();
var albumId
= items.Skip(1).FirstOrDefault();
var trackKeyword
= items.Skip(2).FirstOrDefault();
var trackId
= items.Skip(3).FirstOrDefault();
if (albumKeyword != "album" || albumId == null || trackKeyword != "track" || trackId == null)
{
return null;
}
return $"https://music.yandex.ru/iframe/#track/{trackId}/{albumId}/";
}
#endregion
}
}

View File

@@ -16,6 +16,7 @@ namespace Markdig.Extensions.MediaLinks
{
Width = "500";
Height = "281";
Class = "";
ExtensionToMimeType = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{".3gp", "video/3gpp"},
@@ -68,6 +69,7 @@ namespace Markdig.Extensions.MediaLinks
{".wma", "audio/x-ms-wma"},
{".wax", "audio/x-ms-wax"},
{".mid", "audio/midi"},
{".mp3", "audio/mpeg"},
{".mpga", "audio/mpeg"},
{".mp4a", "audio/mp4"},
{".ecelp4800", "audio/vnd.nuera.ecelp4800"},
@@ -86,6 +88,8 @@ namespace Markdig.Extensions.MediaLinks
public string Height { get; set; }
public string Class { get; set; }
public Dictionary<string, string> ExtensionToMimeType { get; }
}
}
}

View File

@@ -0,0 +1,34 @@
// 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 Markdig.Renderers;
using Markdig.Renderers.Html.Inlines;
namespace Markdig.Extensions.NoRefLinks
{
/// <summary>
/// Extension to automatically render rel=nofollow to all links in an HTML output.
/// </summary>
public class NoFollowLinksExtension : IMarkdownExtension
{
public void Setup(MarkdownPipelineBuilder pipeline)
{
}
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var linkRenderer = renderer.ObjectRenderers.Find<LinkInlineRenderer>();
if (linkRenderer != null)
{
linkRenderer.AutoRelNoFollow = true;
}
var autolinkRenderer = renderer.ObjectRenderers.Find<AutolinkInlineRenderer>();
if (autolinkRenderer != null)
{
autolinkRenderer.AutoRelNoFollow = true;
}
}
}
}

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