Compare commits

...

96 Commits

Author SHA1 Message Date
Alexandre Mutel
ea13b33f1a Allow to tag with version without v prefix 2019-05-11 17:22:14 +02:00
Alexandre Mutel
9b2ec2e2e5 Bump to 0.17.0 2019-05-10 18:25:10 +02:00
Alexandre Mutel
d8dffc28b4 Merge pull request #336 from FranklinWhale/youtube-enhancements
Enhancements for YouTube Links
2019-05-10 18:20:38 +02:00
Alexandre Mutel
0735042599 Merge pull request #334 from OpportunityLiu/master
fix code inline normalize
2019-05-10 18:18:03 +02:00
Franklin Tse
d1f99cdb69 Add test cases and URL escaping 2019-04-30 03:00:35 +08:00
Franklin Tse
d0a2ae2b50 Code formatting change 2019-04-30 01:46:54 +08:00
OpportunityLiu
dd4d1b349f lf as line ending 2019-04-29 16:57:33 +08:00
OpportunityLiu
e33281b8ae optimize 2019-04-29 16:39:55 +08:00
Franklin Tse
50d42560da Use null-conditional operator 2019-04-29 15:11:32 +08:00
Franklin Tse
a1e96d717b Correct lambda 2019-04-29 15:08:48 +08:00
Franklin Tse
56278657fd Fixes #335 2019-04-29 14:58:06 +08:00
OpportunityLiu
b7202084d2 fix code inline normalize 2019-04-29 10:49:37 +08:00
Alexandre Mutel
88052168d0 Merge pull request #304 from nschonni/patch-1
typo: dib -> div
2019-04-23 11:52:41 +02:00
Alexandre Mutel
27cb88773a Merge pull request #331 from nschonni/typos
typo roundup
2019-04-23 11:52:00 +02:00
Nick Schonning
fe3d5b0ccf typo: occuring -> occurring 2019-04-15 02:05:17 -04:00
Nick Schonning
86759e44e1 typo: jsut -> just 2019-04-15 02:02:30 -04:00
Nick Schonning
dd60990d3d typo: Sibliing -> Sibling 2019-04-15 02:01:46 -04:00
Nick Schonning
a3a9496c6f typo: inlnie -> inline 2019-04-15 02:01:16 -04:00
Nick Schonning
d09013ba14 typo: childrens -> children 2019-04-15 02:00:47 -04:00
Nick Schonning
9499660f83 typo: columWidth -> columnWidth 2019-04-15 02:00:25 -04:00
Nick Schonning
df24ab9846 typo: succesfull -> successful 2019-04-15 01:59:18 -04:00
Nick Schonning
40a8cd09b6 typo: proessed -> processed 2019-04-15 01:58:43 -04:00
Nick Schonning
b556f06d31 typo: balanaced -> balanced 2019-04-15 01:58:25 -04:00
Nick Schonning
54e61d3b91 typo: succsesfull -> successful 2019-04-15 01:57:18 -04:00
Nick Schonning
221afcc4d3 typo: curent -> current 2019-04-15 01:56:43 -04:00
Nick Schonning
f1fc64af42 typo: arround -> around 2019-04-15 01:55:52 -04:00
Nick Schonning
b39614f10c typo: ommitted -> omitted 2019-04-15 01:54:44 -04:00
Nick Schonning
5bda352f7a typo: backstick -> backtick 2019-04-15 01:54:08 -04:00
Nick Schonning
4f08094de0 typo: accross -> across 2019-04-15 01:51:25 -04:00
Nick Schonning
13a733a061 typo: Abreviation -> Abbreviation 2019-04-15 01:51:09 -04:00
Nick Schonning
d010a4f21e typo: occured -> occurred 2019-04-15 01:45:25 -04:00
Nick Schonning
a353c59e26 typo: Repressents -> Represents 2019-04-15 01:42:55 -04:00
Nick Schonning
01c8aedced typo: dicard -> discard 2019-04-15 01:40:58 -04:00
Nick Schonning
01d8842ac1 typo: sucessfull -> successful 2019-04-15 01:40:35 -04:00
Nick Schonning
69a2261562 typo: perfoming -> performing 2019-04-15 01:40:03 -04:00
Nick Schonning
09905db72a typo: Sligthly -> Slightly 2019-04-15 01:39:34 -04:00
Nick Schonning
754409c8fa typo: minumun -> minimum 2019-04-15 01:39:10 -04:00
Nick Schonning
cd67a3049a typo: porcess -> process 2019-04-15 01:38:35 -04:00
Nick Schonning
277aef341a typo: occurence -> occurrence 2019-04-15 01:38:03 -04:00
Nick Schonning
9a5ef61bd1 typo: implemts -> implements 2019-04-15 01:37:37 -04:00
Nick Schonning
ed6c59dd07 typo: blcok -> block 2019-04-15 01:37:08 -04:00
Nick Schonning
4deeac538a typo: openning -> opening 2019-04-15 01:36:31 -04:00
Nick Schonning
b05161d20e typo: continous -> continuous 2019-04-15 01:36:11 -04:00
Nick Schonning
d02adf1bf3 typo: elemeent -> element 2019-04-15 01:35:39 -04:00
Nick Schonning
f767b649e6 typo: mulitple -> multiple 2019-04-15 01:33:54 -04:00
Nick Schonning
bdec15d943 typo: capitalised -> capitalized
US spelling since that is normal in .Net
2019-04-15 01:30:36 -04:00
Nick Schonning
fe4ac6643a typo: preceeded -> preceded 2019-04-15 01:29:12 -04:00
Nick Schonning
eb72bc6d4f typo: onre -> one 2019-04-15 01:28:03 -04:00
Nick Schonning
f484366612 typo: hypen -> hyphen 2019-04-15 01:27:32 -04:00
Nick Schonning
bb5e45b939 typo: Nuitrion -> Nutrition 2019-04-15 01:25:52 -04:00
Nick Schonning
2b41e47170 typo: outputing -> outputting 2019-04-15 01:23:59 -04:00
Nick Schonning
3c16852219 typo: ponctuation -> punctuation 2019-04-15 01:21:33 -04:00
Nick Schonning
744417c7a2 typo: dib -> div 2019-04-15 01:07:01 -04:00
Alexandre Mutel
4039e11e08 Merge pull request #327 from MihaZupan/pr327
CommonMark 0.29, DisableHeadings extension, AutoLinkOptions
2019-04-12 17:59:01 +02:00
Miha Zupan
3c73a2d05a Apply CommonMark spec patch 2019-04-12 17:28:35 +02:00
Miha Zupan
1ef247b093 Update readme and changelog 2019-04-11 16:56:11 +02:00
Miha Zupan
d45d8873f5 [CM 0.29] Use new ListItem indendation rules 2019-04-11 16:53:18 +02:00
Miha Zupan
396a03567c Update code comments for Link- and CharHelper 2019-04-11 14:54:40 +02:00
Miha Zupan
6a98e204a5 [CM 0.29] Remove 'meta' from recognised html tags 2019-04-11 11:01:48 +02:00
Miha Zupan
210a6612c7 [CM 0.29] Apply new entity length limits 2019-04-11 11:01:37 +02:00
Miha Zupan
27c35b3b09 [CM 0.29] Correctly handle empty setex headings under a LinkReferenceDefinition 2019-04-10 23:19:40 +02:00
Miha Zupan
ac1db841d5 [CM 0.29] Permit empty urls and spaces for angle-bracket links 2019-04-10 23:18:06 +02:00
Miha Zupan
09f29615c1 [CM 0.29] Use new rule for EmphasisDelimiter matching 2019-04-10 23:07:21 +02:00
Miha Zupan
05e5a3f2bb [CM 0.29] Use new infostring rules for tilde FencedBlocks 2019-04-10 23:06:09 +02:00
Miha Zupan
6d238de69e [CM 0.29] Use new space-trimming rules for CodeInline 2019-04-10 23:02:50 +02:00
Miha Zupan
5b88dbb90a Use StringBuilderCache in JiraLink parser 2019-04-10 23:00:14 +02:00
Miha Zupan
fbd822cef7 Update CommonMark spec to 0.29 2019-04-10 22:59:23 +02:00
Miha Zupan
0e22a120f1 Use a '.generated' suffix for generated test code files 2019-04-05 18:18:26 +02:00
Alexandre Mutel
590f3d0b1b Merge pull request #326 from manuel-guilbault/master
Set ContainerBlock.Count to zero upon Clear() calls.
2019-04-05 09:01:47 -07:00
Miha Zupan
23766d84fa Rename UseHttpsForWWWLinks option 2019-04-05 16:59:51 +02:00
Miha Zupan
14e9e618a2 Update changelog 2019-04-05 16:48:11 +02:00
Miha Zupan
f523cb243b Add UseHttpsPrefixForWWWLinks option to AutoLinks 2019-04-05 16:45:09 +02:00
Miha Zupan
1064818b49 Add OpenInNewWindow option to AutoLinks 2019-04-05 16:37:15 +02:00
Miha Zupan
befd1ca846 Add 'DisableHeadings' extension 2019-04-05 16:07:15 +02:00
Miha Zupan
65c671d014 Bump target UWP version 2019-04-03 22:27:01 +02:00
Manuel Guilbault
25bc0b8bf6 Set Count to zero upon Clear() calls. 2019-04-01 15:27:17 +02:00
Miha Zupan
343a2a17e1 Add sample Normalize/PlainText spec files 2019-03-17 13:00:11 +01:00
Miha Zupan
5100ed0b68 Update changelog 2019-03-17 11:58:48 +01:00
Miha Zupan
95dd2c148c Add Spec files for Normalize and PlainText renderers 2019-03-17 11:39:47 +01:00
Alexandre Mutel
354db6b306 Merge pull request #319 from MihaZupan/allow-unicode-domain-names
Allow unicode domain names
2019-03-11 18:30:01 +01:00
Miha Zupan
50313a36dc Update changelog 2019-03-11 18:19:04 +01:00
Miha Zupan
77d5dcb6dd Drop support for netstandard1.1 and legacy PCL 2019-03-11 16:35:14 +01:00
Miha Zupan
1c88fb65c8 Use IDNA encoding for domain names containing non-ascii chars 2019-03-11 15:36:03 +01:00
Miha Zupan
db9660d090 Allow non-ascii characters in domain names 2019-03-11 15:35:55 +01:00
Miha Zupan
39ab066e2d Exclude nuget.props from source control 2019-03-11 15:35:44 +01:00
Miha Zupan
47f395dad7 Add unicode domain name test for LinkReferenceDefinitions 2019-03-09 20:16:01 +01:00
Miha Zupan
04e195cdb2 Add tests for unicode in domain names 2019-03-09 19:59:23 +01:00
Miha Zupan
3b3872ffe3 Use a universal time format for spec timestamps 2019-03-09 19:59:00 +01:00
Alexandre Mutel
c7cda0df45 Merge pull request #318 from OpportunityLiu/master
fix #242: HtmlCustomContainerRenderer does not honour EnableHtmlForBlock == false
2019-03-08 10:12:49 +01:00
OpportunityLiu
7651e75bfa fix #242 2019-03-08 16:59:47 +08:00
Alexandre Mutel
ea99cd6115 Merge pull request #315 from OpportunityLiu/patch-1
Fix ToPlainText with htmlentity
2019-03-07 10:14:40 +01:00
Opportunity
3af4f33b5d Update TestPlainText.cs 2019-03-07 16:39:03 +08:00
Opportunity
41e8d5e5fc Update LiteralInlineRenderer.cs 2019-03-07 16:37:43 +08:00
Opportunity
5a73c2bf2b Update HtmlEntityInlineRenderer.cs 2019-03-07 16:37:11 +08:00
Opportunity
f542fd0a88 Update LiteralInlineRenderer.cs 2019-03-07 16:36:29 +08:00
Opportunity
5a6300823c Update HtmlEntityInlineRenderer.cs 2019-03-07 16:33:43 +08:00
105 changed files with 5023 additions and 3680 deletions

1
.gitignore vendored
View File

@@ -6,6 +6,7 @@
*.user
*.userosscache
*.sln.docstates
*.nuget.props
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

View File

@@ -1,6 +1,7 @@
version: 10.0.{build}
image: Visual Studio 2017
configuration: Release
install:
- ps: >-
cd src
@@ -8,14 +9,14 @@ install:
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]') {
if($env:appveyor_repo_tag_name -match '^[0-9]') {
$env:appveyor_nuget_push = 'true'
$env:MARKDIG_VERSION_SUFFIX = ""
}
@@ -25,21 +26,29 @@ install:
}
}
}
build:
project: src/Markdig.sln
verbosity: minimal
after_build:
- dotnet run --project SpecFileGen/SpecFileGen.csproj -c Release
- cmd: >-
dotnet run --project SpecFileGen/SpecFileGen.csproj -c Release
dotnet test -v n Markdig.Tests
before_package:
- cmd: >-
msbuild /t:pack /p:VersionSuffix="%MARKDIG_VERSION_SUFFIX%" /p:Configuration=Release Markdig/Markdig.csproj
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
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:

View File

@@ -1,5 +1,14 @@
# Changelog
## 0.17.0 (10 May 2019)
- Update to latest CommonMark specs 0.29 ([(PR #327)](https://github.com/lunet-io/markdig/pull/327))
- Add `AutoLinkOptions` with `OpenInNewWindow`, `UseHttpsForWWWLinks` ([(PR #327)](https://github.com/lunet-io/markdig/pull/327))
- Add `DisableHeadings` extension method to `MarkdownPipelineBuilder` ([(PR #327)](https://github.com/lunet-io/markdig/pull/327))
- Drop support for netstandard1.1 and Portable Class Libraries ([(PR #319)](https://github.com/lunet-io/markdig/pull/319))
- Allow non-ASCII characters in url domain names ([(PR #319)](https://github.com/lunet-io/markdig/pull/319))
- Add better support for youtu.be link ([(PR #336)](https://github.com/lunet-io/markdig/pull/336))
- Fix backsticks in Markdown.Normalize ([(PR #334)](https://github.com/lunet-io/markdig/pull/334))
## 0.16.0 (25 Feb 2019)
- Improve performance of emoji-abbreviation parser ([(PR #305)](https://github.com/lunet-io/markdig/pull/305))
- Change output for math extension to use a rendering more compatible with existing Math JS libraries ([(PR #311)](https://github.com/lunet-io/markdig/pull/311))
@@ -25,7 +34,7 @@
- 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))
- Add autolink domain GFM validation ([(PR #253)](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))
@@ -132,7 +141,7 @@
## 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
- fix potential cast exception with Abbreviation 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

View File

@@ -14,7 +14,7 @@ You can **try Markdig online** and compare it to other implementations on [babel
- **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 (0.28)](http://spec.commonmark.org/)
- Passing more than **600+ tests** from the latest [CommonMark specs (0.29)](http://spec.commonmark.org/)
- Includes all the core elements of CommonMark:
- including **GFM fenced code blocks**.
- **Extensible** architecture

View File

@@ -5397,7 +5397,7 @@ foo
## Entity and numeric character references
All valid HTML entity references and numeric character
references, except those occuring in code blocks and code spans,
references, except those occurring in code blocks and code spans,
are recognized as such and treated as equivalent to the
corresponding Unicode characters. Conforming CommonMark parsers
need not store information about whether a particular character

View File

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

View File

@@ -1,154 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Markdig.Tests</RootNamespace>
<AssemblyName>Markdig.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<CopyNuGetImplementations>true</CopyNuGetImplementations>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
<TargetFrameworks>net451;netcoreapp2.1</TargetFrameworks>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Specs\AbbreviationSpecs.cs" />
<Compile Include="Specs\AutoIdentifierSpecs.cs" />
<Compile Include="Specs\AutoLinks.cs" />
<Compile Include="Specs\BootstrapSpecs.cs" />
<Compile Include="Specs\CommonMark.cs" />
<Compile Include="Specs\CustomContainerSpecs.cs" />
<Compile Include="Specs\DefinitionListSpecs.cs" />
<Compile Include="Specs\DiagramsSpecs.cs" />
<Compile Include="Specs\EmojiSpecs.cs" />
<Compile Include="Specs\EmphasisExtraSpecs.cs" />
<Compile Include="Specs\FigureFooterAndCiteSpecs.cs" />
<Compile Include="Specs\FootnotesSpecs.cs" />
<Compile Include="Specs\GenericAttributesSpecs.cs" />
<Compile Include="Specs\GlobalizationSpecs.cs" />
<Compile Include="Specs\GridTableSpecs.cs" />
<Compile Include="Specs\HardlineBreakSpecs.cs" />
<Compile Include="Specs\JiraLinks.cs" />
<Compile Include="Specs\ListExtraSpecs.cs" />
<Compile Include="Specs\MathSpecs.cs" />
<Compile Include="Specs\MediaSpecs.cs" />
<Compile Include="Specs\NoHtmlSpecs.cs" />
<Compile Include="Specs\PipeTableSpecs.cs" />
<Compile Include="Specs\SmartyPantsSpecs.cs" />
<Compile Include="Specs\TaskListSpecs.cs" />
<Compile Include="Specs\YamlSpecs.cs" />
<Compile Include="TestEmphasisExtended.cs" />
<Compile Include="TestEmphasisPlus.cs" />
<Compile Include="TestEmphasisExtraOptions.cs" />
<Compile Include="TestDescendantsOrder.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" />
<Compile Include="TextAssert.cs" />
<Compile Include="TestParser.cs" />
<ProjectReference Include="..\Markdig\Markdig.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<Content Include="ArgumentOutOfRangeException.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="hang.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="project.json" />
<None Include="Specs\CommonMark.md" />
<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" />
<None Include="Specs\ListExtraSpecs.md" />
<None Include="Specs\GenericAttributesSpecs.md" />
<None Include="Specs\CustomContainerSpecs.md" />
<None Include="Specs\DefinitionListSpecs.md" />
<None Include="Specs\EmojiSpecs.md" />
<None Include="Specs\FootnotesSpecs.md" />
<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" />
<None Include="Specs\MathSpecs.md" />
<None Include="Specs\PipeTableSpecs.md" />
<None Include="Specs\EmphasisExtraSpecs.md" />
</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.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,183 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
using Markdig.Extensions.AutoLinks;
using NUnit.Framework;
namespace Markdig.Tests
{
public class MiscTests
{
[Test]
public void TestChangelogPRLinksMatchDescription()
{
string solutionFolder = Path.GetFullPath(Path.Combine(TestParser.TestsDirectory, "../.."));
string changelogPath = Path.Combine(solutionFolder, "changelog.md");
string changelog = File.ReadAllText(changelogPath);
var matches = Regex.Matches(changelog, @"\(\[\(PR #(\d+)\)\]\(.*?pull\/(\d+)\)\)");
Assert.Greater(matches.Count, 0);
foreach (Match match in matches)
{
Assert.True(int.TryParse(match.Groups[1].Value, out int textNr));
Assert.True(int.TryParse(match.Groups[2].Value, out int linkNr));
Assert.AreEqual(textNr, linkNr);
}
}
[Test]
public void TestFixHang()
{
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "hang.md"));
_ = Markdown.ToHtml(input);
}
[Test]
public void TestInvalidHtmlEntity()
{
var input = "9&ddr;&*&ddr;&de<64><65>__";
TestParser.TestSpec(input, "<p>9&amp;ddr;&amp;*&amp;ddr;&amp;de<64><65>__</p>");
}
[Test]
public void TestInvalidCharacterHandling()
{
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "ArgumentOutOfRangeException.md"));
_ = Markdown.ToHtml(input);
}
[Test]
public void TestInvalidCodeEscape()
{
var input = "```**Header** ";
_ = Markdown.ToHtml(input);
}
[Test]
public void TestEmphasisAndHtmlEntity()
{
var markdownText = "*Unlimited-Fun&#174;*&#174;";
TestParser.TestSpec(markdownText, "<p><em>Unlimited-Fun®</em>®</p>");
}
[Test]
public void TestThematicInsideCodeBlockInsideList()
{
var input = @"1. In the :
```
Id DisplayName Description
-- ----------- -----------
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
```";
TestParser.TestSpec(input, @"<ol>
<li><p>In the :</p>
<pre><code>Id DisplayName Description
-- ----------- -----------
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
</code></pre></li>
</ol>");
}
[Test]
public void VisualizeMathExpressions()
{
string math = @"Math expressions
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
$$\frac{n!}{k!(n-k)!} = \binom{n}{k}$$
$$
\frac{n!}{k!(n-k)!} = \binom{n}{k}
$$
<div class=""math"">
\begin{align}
\sqrt{37} & = \sqrt{\frac{73^2-1}{12^2}} \\
& = \sqrt{\frac{73^2}{12^2}\cdot\frac{73^2-1}{73^2}} \\
& = \sqrt{\frac{73^2}{12^2}}\sqrt{\frac{73^2-1}{73^2}} \\
& = \frac{73}{12}\sqrt{1 - \frac{1}{73^2}} \\
& \approx \frac{73}{12}\left(1 - \frac{1}{2\cdot73^2}\right)
\end{align}
</div>
";
Console.WriteLine("Math Expressions:\n");
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
var html = Markdown.ToHtml(math, pl);
Console.WriteLine(html);
}
[Test]
public void InlineMathExpression()
{
string math = @"Math expressions
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
";
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
var html = Markdown.ToHtml(math, pl);
Console.WriteLine(html);
Assert.IsTrue(html.Contains("<p><span class=\"math\">\\("), "Leading bracket missing");
Assert.IsTrue(html.Contains("\\)</span></p>"), "Trailing bracket missing");
}
[Test]
public void BlockMathExpression()
{
string math = @"Math expressions
$$
\frac{n!}{k!(n-k)!} = \binom{n}{k}
$$
";
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
var html = Markdown.ToHtml(math, pl);
Console.WriteLine(html);
Assert.IsTrue(html.Contains("<div class=\"math\">\n\\["), "Leading bracket missing");
Assert.IsTrue(html.Contains("\\]</div>"), "Trailing bracket missing");
}
[Test]
public void CanDisableParsingHeadings()
{
var noHeadingsPipeline = new MarkdownPipelineBuilder().DisableHeadings().Build();
TestParser.TestSpec("Foo\n===", "<h1>Foo</h1>");
TestParser.TestSpec("Foo\n===", "<p>Foo\n===</p>", noHeadingsPipeline);
TestParser.TestSpec("# Heading 1", "<h1>Heading 1</h1>");
TestParser.TestSpec("# Heading 1", "<p># Heading 1</p>", noHeadingsPipeline);
// Does not also disable link reference definitions
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>");
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>", noHeadingsPipeline);
}
[Test]
public void CanOpenAutoLinksInNewWindow()
{
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
var newWindowPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { OpenInNewWindow = true }).Build();
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\" target=\"blank\">www.foo.bar</a></p>", newWindowPipeline);
}
[Test]
public void CanUseHttpsPrefixForWWWAutoLinks()
{
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
var httpsPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { UseHttpsForWWWLinks = true }).Build();
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
TestParser.TestSpec("www.foo.bar", "<p><a href=\"https://www.foo.bar\">www.foo.bar</a></p>", httpsPipeline);
}
}
}

View File

@@ -0,0 +1,93 @@
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Headings
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.Normalize.Headings
{
[TestFixture]
public class TestHeadings
{
// # Headings
[Test]
public void Headings_Example001()
{
// Example 1
// Section: Headings
//
// The following Markdown:
// # Heading 1
//
// ## Heading 2
//
// ### Heading 3
//
// #### Heading 4
//
// ##### Heading 5
//
// ###### Heading 6
//
// Should be rendered as:
// # Heading 1
//
// ## Heading 2
//
// ### Heading 3
//
// #### Heading 4
//
// ##### Heading 5
//
// ###### Heading 6
Console.WriteLine("Example 1\nSection Headings\n");
TestNormalize.TestSpec("# Heading 1\n\n## Heading 2\n\n### Heading 3\n\n#### Heading 4\n\n##### Heading 5\n\n###### Heading 6", "# Heading 1\n\n## Heading 2\n\n### Heading 3\n\n#### Heading 4\n\n##### Heading 5\n\n###### Heading 6", "");
}
[Test]
public void Headings_Example002()
{
// Example 2
// Section: Headings
//
// The following Markdown:
// ###### Heading
//
// Text after two newlines
//
// Should be rendered as:
// ###### Heading
//
// Text after two newlines
Console.WriteLine("Example 2\nSection Headings\n");
TestNormalize.TestSpec("###### Heading\n\nText after two newlines", "###### Heading\n\nText after two newlines", "");
}
[Test]
public void Headings_Example003()
{
// Example 3
// Section: Headings
//
// The following Markdown:
// Heading
// =======
//
// Text after two newlines
//
// Should be rendered as:
// # Heading
//
// Text after two newlines
Console.WriteLine("Example 3\nSection Headings\n");
TestNormalize.TestSpec("Heading\n=======\n\nText after two newlines", "# Heading\n\nText after two newlines", "");
}
}
}

View File

@@ -0,0 +1,48 @@
# Headings
```````````````````````````````` example
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
.
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
````````````````````````````````
```````````````````````````````` example
###### Heading
Text after two newlines
.
###### Heading
Text after two newlines
````````````````````````````````
```````````````````````````````` example
Heading
=======
Text after two newlines
.
# Heading
Text after two newlines
````````````````````````````````

View File

@@ -0,0 +1,35 @@
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Sample
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.PlainText.Sample
{
[TestFixture]
public class TestSamplePlainTextSpec
{
// # Sample plain text spec
//
// Emphasis and anchors are stripped. A newline is ensured.
[Test]
public void SamplePlainTextSpec_Example001()
{
// Example 1
// Section: Sample plain text spec
//
// The following Markdown:
// *Hello*, [world](http://example.com)!
//
// Should be rendered as:
// Hello, world!
//
Console.WriteLine("Example 1\nSection Sample plain text spec\n");
TestPlainText.TestSpec("*Hello*, [world](http://example.com)!", "Hello, world!\n", "");
}
}
}

View File

@@ -0,0 +1,10 @@
# Sample plain text spec
Emphasis and anchors are stripped. A newline is ensured.
```````````````````````````````` example
*Hello*, [world](http://example.com)!
.
Hello, world!
````````````````````````````````

View File

@@ -1,14 +0,0 @@
// 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.
namespace Markdig.Tests
{
public class Program
{
public static void Main()
{
new TestPlayParser().TestSimple();
}
}
}

View File

@@ -1,36 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Textamin.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Textamin.Tests")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("a0c5cb5f-5568-40ab-b945-d6d2664d51b0")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -1,4 +1,4 @@
// Generated: 6. 02. 2019 16:15:54
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Abbreviations

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Auto Identifiers

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Auto Links
@@ -446,4 +446,113 @@ namespace Markdig.Tests.Specs.AutoLinks
TestParser.TestSpec("https://github.com?\n\nhttps://github.com?a\n\nhttps://github.com#a\n\nhttps://github.com:\n\nhttps://github.com:443", "<p><a href=\"https://github.com\">https://github.com</a>?</p>\n<p><a href=\"https://github.com?a\">https://github.com?a</a></p>\n<p><a href=\"https://github.com#a\">https://github.com#a</a></p>\n<p><a href=\"https://github.com\">https://github.com</a>:</p>\n<p><a href=\"https://github.com:443\">https://github.com:443</a></p>", "autolinks|advanced");
}
}
[TestFixture]
public class TestExtensionsAutoLinksUnicodeSupport
{
// ### Unicode support
//
// Links with unicode characters in the path / query / fragment are matched and url encoded
[Test]
public void ExtensionsAutoLinksUnicodeSupport_Example021()
{
// Example 21
// Section: Extensions / AutoLinks / Unicode support
//
// The following Markdown:
// http://abc.net/☃
//
// http://abc.net?☃
//
// http://abc.net#☃
//
// http://abc.net/foo#☃
//
// Should be rendered as:
// <p><a href="http://abc.net/%E2%98%83">http://abc.net/☃</a></p>
// <p><a href="http://abc.net?%E2%98%83">http://abc.net?☃</a></p>
// <p><a href="http://abc.net#%E2%98%83">http://abc.net#☃</a></p>
// <p><a href="http://abc.net/foo#%E2%98%83">http://abc.net/foo#☃</a></p>
Console.WriteLine("Example 21\nSection Extensions / AutoLinks / Unicode support\n");
TestParser.TestSpec("http://abc.net/☃\n\nhttp://abc.net?☃\n\nhttp://abc.net#☃\n\nhttp://abc.net/foo#☃", "<p><a href=\"http://abc.net/%E2%98%83\">http://abc.net/☃</a></p>\n<p><a href=\"http://abc.net?%E2%98%83\">http://abc.net?☃</a></p>\n<p><a href=\"http://abc.net#%E2%98%83\">http://abc.net#☃</a></p>\n<p><a href=\"http://abc.net/foo#%E2%98%83\">http://abc.net/foo#☃</a></p>", "autolinks|advanced");
}
// Unicode characters in the FQDN are matched and IDNA encoded
[Test]
public void ExtensionsAutoLinksUnicodeSupport_Example022()
{
// Example 22
// Section: Extensions / AutoLinks / Unicode support
//
// The following Markdown:
// http://☃.net?☃
//
// Should be rendered as:
// <p><a href="http://xn--n3h.net?%E2%98%83">http://☃.net?☃</a></p>
Console.WriteLine("Example 22\nSection Extensions / AutoLinks / Unicode support\n");
TestParser.TestSpec("http://☃.net?☃", "<p><a href=\"http://xn--n3h.net?%E2%98%83\">http://☃.net?☃</a></p>", "autolinks|advanced");
}
// Same goes for regular autolinks
[Test]
public void ExtensionsAutoLinksUnicodeSupport_Example023()
{
// Example 23
// Section: Extensions / AutoLinks / Unicode support
//
// The following Markdown:
// <http://abc.net/☃>
//
// <http://abc.net?☃>
//
// <http://abc.net#☃>
//
// <http://abc.net/foo#☃>
//
// Should be rendered as:
// <p><a href="http://abc.net/%E2%98%83">http://abc.net/☃</a></p>
// <p><a href="http://abc.net?%E2%98%83">http://abc.net?☃</a></p>
// <p><a href="http://abc.net#%E2%98%83">http://abc.net#☃</a></p>
// <p><a href="http://abc.net/foo#%E2%98%83">http://abc.net/foo#☃</a></p>
Console.WriteLine("Example 23\nSection Extensions / AutoLinks / Unicode support\n");
TestParser.TestSpec("<http://abc.net/☃>\n\n<http://abc.net?☃>\n\n<http://abc.net#☃>\n\n<http://abc.net/foo#☃>", "<p><a href=\"http://abc.net/%E2%98%83\">http://abc.net/☃</a></p>\n<p><a href=\"http://abc.net?%E2%98%83\">http://abc.net?☃</a></p>\n<p><a href=\"http://abc.net#%E2%98%83\">http://abc.net#☃</a></p>\n<p><a href=\"http://abc.net/foo#%E2%98%83\">http://abc.net/foo#☃</a></p>", "autolinks|advanced");
}
[Test]
public void ExtensionsAutoLinksUnicodeSupport_Example024()
{
// Example 24
// Section: Extensions / AutoLinks / Unicode support
//
// The following Markdown:
// <http://☃.net?☃>
//
// Should be rendered as:
// <p><a href="http://xn--n3h.net?%E2%98%83">http://☃.net?☃</a></p>
Console.WriteLine("Example 24\nSection Extensions / AutoLinks / Unicode support\n");
TestParser.TestSpec("<http://☃.net?☃>", "<p><a href=\"http://xn--n3h.net?%E2%98%83\">http://☃.net?☃</a></p>", "autolinks|advanced");
}
// It also complies with CommonMark's vision of priority.
// This will therefore be seen as an autolink and not as code inline.
[Test]
public void ExtensionsAutoLinksUnicodeSupport_Example025()
{
// Example 25
// Section: Extensions / AutoLinks / Unicode support
//
// The following Markdown:
// <http://foö.bar.`baz>`
//
// Should be rendered as:
// <p><a href="http://xn--fo-gka.bar.%60baz">http://foö.bar.`baz</a>`</p>
Console.WriteLine("Example 25\nSection Extensions / AutoLinks / Unicode support\n");
TestParser.TestSpec("<http://foö.bar.`baz>`", "<p><a href=\"http://xn--fo-gka.bar.%60baz\">http://foö.bar.`baz</a>`</p>", "autolinks|advanced");
}
}
}

View File

@@ -239,4 +239,63 @@ https://github.com:443
<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>
````````````````````````````````
### Unicode support
Links with unicode characters in the path / query / fragment are matched and url encoded
```````````````````````````````` example
http://abc.net/☃
http://abc.net?☃
http://abc.net#☃
http://abc.net/foo#☃
.
<p><a href="http://abc.net/%E2%98%83">http://abc.net/☃</a></p>
<p><a href="http://abc.net?%E2%98%83">http://abc.net?☃</a></p>
<p><a href="http://abc.net#%E2%98%83">http://abc.net#☃</a></p>
<p><a href="http://abc.net/foo#%E2%98%83">http://abc.net/foo#☃</a></p>
````````````````````````````````
Unicode characters in the FQDN are matched and IDNA encoded
```````````````````````````````` example
http://☃.net?☃
.
<p><a href="http://xn--n3h.net?%E2%98%83">http://☃.net?☃</a></p>
````````````````````````````````
Same goes for regular autolinks
```````````````````````````````` example
<http://abc.net/☃>
<http://abc.net?☃>
<http://abc.net#☃>
<http://abc.net/foo#☃>
.
<p><a href="http://abc.net/%E2%98%83">http://abc.net/☃</a></p>
<p><a href="http://abc.net?%E2%98%83">http://abc.net?☃</a></p>
<p><a href="http://abc.net#%E2%98%83">http://abc.net#☃</a></p>
<p><a href="http://abc.net/foo#%E2%98%83">http://abc.net/foo#☃</a></p>
````````````````````````````````
```````````````````````````````` example
<http://☃.net?☃>
.
<p><a href="http://xn--n3h.net?%E2%98%83">http://☃.net?☃</a></p>
````````````````````````````````
It also complies with CommonMark's vision of priority.
This will therefore be seen as an autolink and not as code inline.
```````````````````````````````` example
<http://foö.bar.`baz>`
.
<p><a href="http://xn--fo-gka.bar.%60baz">http://foö.bar.`baz</a>`</p>
````````````````````````````````

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-15 05:23:49
// --------------------------------
// Bootstrap
@@ -14,7 +14,7 @@ namespace Markdig.Tests.Specs.Bootstrap
{
// # Extensions
//
// Adds support for outputing bootstrap ready tags:
// Adds support for outputting bootstrap ready tags:
//
// ## Bootstrap
//

View File

@@ -1,6 +1,6 @@
# Extensions
Adds support for outputing bootstrap ready tags:
Adds support for outputting bootstrap ready tags:
## Bootstrap

View File

@@ -1,8 +1,8 @@
---
title: CommonMark Spec
author: John MacFarlane
version: 0.28
date: '2017-08-01'
version: 0.29
date: '2019-04-06'
license: '[CC-BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)'
...
@@ -248,7 +248,7 @@ satisfactory replacement for a spec.
Because there is no unambiguous spec, implementations have diverged
considerably. As a result, users are often surprised to find that
a document that renders one way on one system (say, a github wiki)
a document that renders one way on one system (say, a GitHub wiki)
renders differently on another (say, converting to docbook using
pandoc). To make matters worse, because nothing in Markdown counts
as a "syntax error," the divergence often isn't discovered right away.
@@ -328,8 +328,10 @@ that is not a [whitespace character].
An [ASCII punctuation character](@)
is `!`, `"`, `#`, `$`, `%`, `&`, `'`, `(`, `)`,
`*`, `+`, `,`, `-`, `.`, `/`, `:`, `;`, `<`, `=`, `>`, `?`, `@`,
`[`, `\`, `]`, `^`, `_`, `` ` ``, `{`, `|`, `}`, or `~`.
`*`, `+`, `,`, `-`, `.`, `/` (U+00212F),
`:`, `;`, `<`, `=`, `>`, `?`, `@` (U+003A0040),
`[`, `\`, `]`, `^`, `_`, `` ` `` (U+005B0060),
`{`, `|`, `}`, or `~` (U+007B007E).
A [punctuation character](@) is an [ASCII
punctuation character] or anything in
@@ -514,8 +516,8 @@ one block element does not affect the inline parsing of any other.
## Container blocks and leaf blocks
We can divide blocks into two types:
[container block](@)s,
which can contain other blocks, and [leaf block](@)s,
[container blocks](@),
which can contain other blocks, and [leaf blocks](@),
which cannot.
# Leaf blocks
@@ -527,7 +529,7 @@ Markdown document.
A line consisting of 0-3 spaces of indentation, followed by a sequence
of three or more matching `-`, `_`, or `*` characters, each followed
optionally by any number of spaces, forms a
optionally by any number of spaces or tabs, forms a
[thematic break](@).
```````````````````````````````` example
@@ -825,7 +827,7 @@ Contents are parsed as inlines:
````````````````````````````````
Leading and trailing blanks are ignored in parsing inline content:
Leading and trailing [whitespace] is ignored in parsing inline content:
```````````````````````````````` example
# foo
@@ -1024,6 +1026,20 @@ baz*
baz</em></h1>
````````````````````````````````
The contents are the result of parsing the headings's raw
content as inlines. The heading's raw content is formed by
concatenating the lines and removing initial and final
[whitespace].
```````````````````````````````` example
Foo *bar
baz*→
====
.
<h1>Foo <em>bar
baz</em></h1>
````````````````````````````````
The underlining can be any length:
@@ -1584,8 +1600,8 @@ begins with a code fence, indented no more than three spaces.
The line with the opening code fence may optionally contain some text
following the code fence; this is trimmed of leading and trailing
spaces and called the [info string](@).
The [info string] may not contain any backtick
whitespace and called the [info string](@). If the [info string] comes
after a backtick fence, it may not contain any backtick
characters. (The reason for this restriction is that otherwise
some inline code would be incorrectly interpreted as the
beginning of a fenced code block.)
@@ -1870,7 +1886,7 @@ Code fences (opening and closing) cannot contain internal spaces:
``` ```
aaa
.
<p><code></code>
<p><code> </code>
aaa</p>
````````````````````````````````
@@ -1922,9 +1938,11 @@ bar
An [info string] can be provided after the opening code fence.
Opening and closing spaces will be stripped, and the first word, prefixed
with `language-`, is used as the value for the `class` attribute of the
`code` element within the enclosing `pre` element.
Although this spec doesn't mandate any particular treatment of
the info string, the first word is typically used to specify
the language of the code block. In HTML output, the language is
normally indicated by adding a class to the `code` element consisting
of `language-` followed by the language name.
```````````````````````````````` example
```ruby
@@ -1973,6 +1991,18 @@ foo</p>
````````````````````````````````
[Info strings] for tilde code blocks can contain backticks and tildes:
```````````````````````````````` example
~~~ aa ``` ~~~
foo
~~~
.
<pre><code class="language-aa">foo
</code></pre>
````````````````````````````````
Closing code fences cannot have [info strings]:
```````````````````````````````` example
@@ -1991,14 +2021,15 @@ Closing code fences cannot have [info strings]:
An [HTML block](@) is a group of lines that is treated
as raw HTML (and will not be escaped in HTML output).
There are seven kinds of [HTML block], which can be defined
by their start and end conditions. The block begins with a line that
meets a [start condition](@) (after up to three spaces
optional indentation). It ends with the first subsequent line that
meets a matching [end condition](@), or the last line of
the document or other [container block]), if no line is encountered that meets the
[end condition]. If the first line meets both the [start condition]
and the [end condition], the block will contain just that line.
There are seven kinds of [HTML block], which can be defined by their
start and end conditions. The block begins with a line that meets a
[start condition](@) (after up to three spaces optional indentation).
It ends with the first subsequent line that meets a matching [end
condition](@), or the last line of the document, or the last line of
the [container block](#container-blocks) containing the current HTML
block, if no line is encountered that meets the [end condition]. If
the first line meets both the [start condition] and the [end
condition], the block will contain just that line.
1. **Start condition:** line begins with the string `<script`,
`<pre`, or `<style` (case-insensitive), followed by whitespace,
@@ -2029,7 +2060,7 @@ followed by one of the strings (case-insensitive) `address`,
`footer`, `form`, `frame`, `frameset`,
`h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `head`, `header`, `hr`,
`html`, `iframe`, `legend`, `li`, `link`, `main`, `menu`, `menuitem`,
`meta`, `nav`, `noframes`, `ol`, `optgroup`, `option`, `p`, `param`,
`nav`, `noframes`, `ol`, `optgroup`, `option`, `p`, `param`,
`section`, `source`, `summary`, `table`, `tbody`, `td`,
`tfoot`, `th`, `thead`, `title`, `tr`, `track`, `ul`, followed
by [whitespace], the end of the line, the string `>`, or
@@ -2037,16 +2068,17 @@ the string `/>`.\
**End condition:** line is followed by a [blank line].
7. **Start condition:** line begins with a complete [open tag]
or [closing tag] (with any [tag name] other than `script`,
`style`, or `pre`) followed only by [whitespace]
or the end of the line.\
(with any [tag name] other than `script`,
`style`, or `pre`) or a complete [closing tag],
followed only by [whitespace] or the end of the line.\
**End condition:** line is followed by a [blank line].
HTML blocks continue until they are closed by their appropriate
[end condition], or the last line of the document or other [container block].
This means any HTML **within an HTML block** that might otherwise be recognised
as a start condition will be ignored by the parser and passed through as-is,
without changing the parser's state.
[end condition], or the last line of the document or other [container
block](#container-blocks). This means any HTML **within an HTML
block** that might otherwise be recognised as a start condition will
be ignored by the parser and passed through as-is, without changing
the parser's state.
For instance, `<pre>` within a HTML block started by `<table>` will not affect
the parser state; as the HTML block was started in by start condition 6, it
@@ -2069,7 +2101,7 @@ _world_.
</td></tr></table>
````````````````````````````````
In this case, the HTML block is terminated by the newline — the `**hello**`
In this case, the HTML block is terminated by the newline — the `**Hello**`
text remains verbatim — and regular parsing resumes, with a paragraph,
emphasised `world` and inline and block HTML following.
@@ -2612,7 +2644,8 @@ bar
However, a following blank line is needed, except at the end of
a document, and except for blocks of types 1--5, above:
a document, and except for blocks of types 1--5, [above][HTML
block]:
```````````````````````````````` example
<div>
@@ -2758,8 +2791,8 @@ an indented code block:
Fortunately, blank lines are usually not necessary and can be
deleted. The exception is inside `<pre>` tags, but as described
above, raw HTML blocks starting with `<pre>` *can* contain blank
lines.
[above][HTML blocks], raw HTML blocks starting with `<pre>`
*can* contain blank lines.
## Link reference definitions
@@ -2811,7 +2844,7 @@ them.
```````````````````````````````` example
[Foo bar]:
<my%20url>
<my url>
'title'
[Foo bar]
@@ -2877,6 +2910,29 @@ The link destination may not be omitted:
<p>[foo]</p>
````````````````````````````````
However, an empty link destination may be specified using
angle brackets:
```````````````````````````````` example
[foo]: <>
[foo]
.
<p><a href="">foo</a></p>
````````````````````````````````
The title must be separated from the link destination by
whitespace:
```````````````````````````````` example
[foo]: <bar>(baz)
[foo]
.
<p>[foo]: <bar>(baz)</p>
<p>[foo]</p>
````````````````````````````````
Both title and destination can contain backslash escapes
and literal backslashes:
@@ -3034,6 +3090,25 @@ and thematic breaks, and it need not be followed by a blank line.
</blockquote>
````````````````````````````````
```````````````````````````````` example
[foo]: /url
bar
===
[foo]
.
<h1>bar</h1>
<p><a href="/url">foo</a></p>
````````````````````````````````
```````````````````````````````` example
[foo]: /url
===
[foo]
.
<p>===
<a href="/url">foo</a></p>
````````````````````````````````
Several [link reference definitions]
can occur one after another, without intervening blank lines.
@@ -3070,6 +3145,17 @@ are defined:
````````````````````````````````
Whether something is a [link reference definition] is
independent of whether the link reference it defines is
used in the document. Thus, for example, the following
document contains just a link reference definition, and
no visible content:
```````````````````````````````` example
[foo]: /url
.
````````````````````````````````
## Paragraphs
@@ -3207,7 +3293,7 @@ aaa
# Container blocks
A [container block] is a block that has other
A [container block](#container-blocks) is a block that has other
blocks as its contents. There are two basic kinds of container blocks:
[block quotes] and [list items].
[Lists] are meta-containers for [list items].
@@ -3669,9 +3755,8 @@ in some browsers.)
The following rules define [list items]:
1. **Basic case.** If a sequence of lines *Ls* constitute a sequence of
blocks *Bs* starting with a [non-whitespace character] and not separated
from each other by more than one blank line, and *M* is a list
marker of width *W* followed by 1 ≤ *N* ≤ 4 spaces, then the result
blocks *Bs* starting with a [non-whitespace character], and *M* is a
list marker of width *W* followed by 1 ≤ *N* ≤ 4 spaces, then the result
of prepending *M* and the following spaces to the first line of
*Ls*, and indenting subsequent lines of *Ls* by *W + N* spaces, is a
list item with *Bs* as its contents. The type of the list item
@@ -3981,8 +4066,7 @@ A start number may not be negative:
2. **Item starting with indented code.** If a sequence of lines *Ls*
constitute a sequence of blocks *Bs* starting with an indented code
block and not separated from each other by more than one blank line,
and *M* is a list marker of width *W* followed by
block, and *M* is a list marker of width *W* followed by
one space, then the result of prepending *M* and the following
space to the first line of *Ls*, and indenting subsequent lines of
*Ls* by *W + 1* spaces, is a list item with *Bs* as its contents.
@@ -4458,9 +4542,10 @@ continued here.</p>
6. **That's all.** Nothing that is not counted as a list item by rules
#1--5 counts as a [list item](#list-items).
The rules for sublists follow from the general rules above. A sublist
must be indented the same number of spaces a paragraph would need to be
in order to be included in the list item.
The rules for sublists follow from the general rules
[above][List items]. A sublist must be indented the same number
of spaces a paragraph would need to be in order to be included
in the list item.
So, in this case we need two spaces indent:
@@ -5049,11 +5134,9 @@ item:
- b
- c
- d
- e
- f
- g
- h
- i
- e
- f
- g
.
<ul>
<li>a</li>
@@ -5063,12 +5146,54 @@ item:
<li>e</li>
<li>f</li>
<li>g</li>
<li>h</li>
<li>i</li>
</ul>
````````````````````````````````
```````````````````````````````` example
1. a
2. b
3. c
.
<ol>
<li>
<p>a</p>
</li>
<li>
<p>b</p>
</li>
<li>
<p>c</p>
</li>
</ol>
````````````````````````````````
Note, however, that list items may not be indented more than
three spaces. Here `- e` is treated as a paragraph continuation
line, because it is indented more than three spaces:
```````````````````````````````` example
- a
- b
- c
- d
- e
.
<ul>
<li>a</li>
<li>b</li>
<li>c</li>
<li>d
- e</li>
</ul>
````````````````````````````````
And here, `3. c` is treated as in indented code block,
because it is indented four spaces and preceded by a
blank line.
```````````````````````````````` example
1. a
@@ -5083,10 +5208,9 @@ item:
<li>
<p>b</p>
</li>
<li>
<p>c</p>
</li>
</ol>
<pre><code>3. c
</code></pre>
````````````````````````````````
@@ -5378,10 +5502,10 @@ Thus, for example, in
<p><code>hi</code>lo`</p>
````````````````````````````````
`hi` is parsed as code, leaving the backtick at the end as a literal
backtick.
## Backslash escapes
Any ASCII punctuation character may be backslash-escaped:
@@ -5415,6 +5539,7 @@ not have their usual Markdown meanings:
\* not a list
\# not a heading
\[foo]: /url "not a reference"
\&ouml; not a character entity
.
<p>*not emphasized*
&lt;br/&gt; not a tag
@@ -5423,7 +5548,8 @@ not have their usual Markdown meanings:
1. not a list
* not a list
# not a heading
[foo]: /url &quot;not a reference&quot;</p>
[foo]: /url &quot;not a reference&quot;
&amp;ouml; not a character entity</p>
````````````````````````````````
@@ -5521,13 +5647,23 @@ foo
## Entity and numeric character references
All valid HTML entity references and numeric character
references, except those occuring in code blocks and code spans,
are recognized as such and treated as equivalent to the
corresponding Unicode characters. Conforming CommonMark parsers
need not store information about whether a particular character
was represented in the source using a Unicode character or
an entity reference.
Valid HTML entity references and numeric character references
can be used in place of the corresponding Unicode character,
with the following exceptions:
- Entity and character references are not recognized in code
blocks and code spans.
- Entity and character references cannot stand in place of
special characters that define structural elements in
CommonMark. For example, although `&#42;` can be used
in place of a literal `*` character, `&#42;` cannot replace
`*` in emphasis delimiters, bullet list markers, or thematic
breaks.
Conforming CommonMark parsers need not store information about
whether a particular character was represented in the source
using a Unicode character or an entity reference.
[Entity references](@) consist of `&` + any of the valid
HTML5 entity names + `;`. The
@@ -5548,22 +5684,22 @@ references and their corresponding code points.
[Decimal numeric character
references](@)
consist of `&#` + a string of 1--8 arabic digits + `;`. A
consist of `&#` + a string of 1--7 arabic digits + `;`. A
numeric character reference is parsed as the corresponding
Unicode character. Invalid Unicode code points will be replaced by
the REPLACEMENT CHARACTER (`U+FFFD`). For security reasons,
the code point `U+0000` will also be replaced by `U+FFFD`.
```````````````````````````````` example
&#35; &#1234; &#992; &#98765432; &#0;
&#35; &#1234; &#992; &#0;
.
<p># Ӓ Ϡ <20> <20></p>
<p># Ӓ Ϡ <20></p>
````````````````````````````````
[Hexadecimal numeric character
references](@) consist of `&#` +
either `X` or `x` + a string of 1-8 hexadecimal digits + `;`.
either `X` or `x` + a string of 1-6 hexadecimal digits + `;`.
They too are parsed as the corresponding Unicode character (this
time specified with a hexadecimal numeral instead of decimal).
@@ -5578,9 +5714,13 @@ Here are some nonentities:
```````````````````````````````` example
&nbsp &x; &#; &#x;
&#87654321;
&#abcdef0;
&ThisIsNotDefined; &hi?;
.
<p>&amp;nbsp &amp;x; &amp;#; &amp;#x;
&amp;#87654321;
&amp;#abcdef0;
&amp;ThisIsNotDefined; &amp;hi?;</p>
````````````````````````````````
@@ -5661,6 +5801,51 @@ text in code spans and code blocks:
````````````````````````````````
Entity and numeric character references cannot be used
in place of symbols indicating structure in CommonMark
documents.
```````````````````````````````` example
&#42;foo&#42;
*foo*
.
<p>*foo*
<em>foo</em></p>
````````````````````````````````
```````````````````````````````` example
&#42; foo
* foo
.
<p>* foo</p>
<ul>
<li>foo</li>
</ul>
````````````````````````````````
```````````````````````````````` example
foo&#10;&#10;bar
.
<p>foo
bar</p>
````````````````````````````````
```````````````````````````````` example
&#9;foo
.
<p>→foo</p>
````````````````````````````````
```````````````````````````````` example
[a](url &quot;tit&quot;)
.
<p>[a](url &quot;tit&quot;)</p>
````````````````````````````````
## Code spans
A [backtick string](@)
@@ -5669,9 +5854,16 @@ preceded nor followed by a backtick.
A [code span](@) begins with a backtick string and ends with
a backtick string of equal length. The contents of the code span are
the characters between the two backtick strings, with leading and
trailing spaces and [line endings] removed, and
[whitespace] collapsed to single spaces.
the characters between the two backtick strings, normalized in the
following ways:
- First, [line endings] are converted to [spaces].
- If the resulting string both begins *and* ends with a [space]
character, but does not consist entirely of [space]
characters, a single [space] character is removed from the
front and back. This allows you to include code that begins
or ends with backtick characters, which must be separated by
whitespace from the opening or closing backtick strings.
This is a simple code span:
@@ -5683,10 +5875,11 @@ This is a simple code span:
Here two backticks are used, because the code contains a backtick.
This example also illustrates stripping of leading and trailing spaces:
This example also illustrates stripping of a single leading and
trailing space:
```````````````````````````````` example
`` foo ` bar ``
`` foo ` bar ``
.
<p><code>foo ` bar</code></p>
````````````````````````````````
@@ -5701,57 +5894,78 @@ spaces:
<p><code>``</code></p>
````````````````````````````````
Note that only *one* space is stripped:
```````````````````````````````` example
` `` `
.
<p><code> `` </code></p>
````````````````````````````````
The stripping only happens if the space is on both
sides of the string:
```````````````````````````````` example
` a`
.
<p><code> a</code></p>
````````````````````````````````
Only [spaces], and not [unicode whitespace] in general, are
stripped in this way:
```````````````````````````````` example
` b `
.
<p><code> b </code></p>
````````````````````````````````
No stripping occurs if the code span contains only spaces:
```````````````````````````````` example
` `
` `
.
<p><code> </code>
<code> </code></p>
````````````````````````````````
[Line endings] are treated like spaces:
```````````````````````````````` example
``
foo
bar
baz
``
.
<p><code>foo</code></p>
<p><code>foo bar baz</code></p>
````````````````````````````````
Interior spaces and [line endings] are collapsed into
single spaces, just as they would be by a browser:
```````````````````````````````` example
`foo bar
baz`
``
foo
``
.
<p><code>foo bar baz</code></p>
<p><code>foo </code></p>
````````````````````````````````
Not all [Unicode whitespace] (for instance, non-breaking space) is
collapsed, however:
Interior spaces are not collapsed:
```````````````````````````````` example
`a  b`
`foo bar
baz`
.
<p><code>a  b</code></p>
<p><code>foo bar baz</code></p>
````````````````````````````````
Note that browsers will typically collapse consecutive spaces
when rendering `<code>` elements, so it is recommended that
the following CSS be used:
Q: Why not just leave the spaces, since browsers will collapse them
anyway? A: Because we might be targeting a non-HTML format, and we
shouldn't rely on HTML-specific rendering assumptions.
(Existing implementations differ in their treatment of internal
spaces and [line endings]. Some, including `Markdown.pl` and
`showdown`, convert an internal [line ending] into a
`<br />` tag. But this makes things difficult for those who like to
hard-wrap their paragraphs, since a line break in the midst of a code
span will cause an unintended line break in the output. Others just
leave internal spaces as they are, which is fine if only HTML is being
targeted.)
```````````````````````````````` example
`foo `` bar`
.
<p><code>foo `` bar</code></p>
````````````````````````````````
code{white-space: pre-wrap;}
Note that backslash escapes do not work in code spans. All backslashes
@@ -5768,6 +5982,19 @@ Backslash escapes are never needed, because one can always choose a
string of *n* backtick characters as delimiters, where the code does
not contain any strings of exactly *n* backtick characters.
```````````````````````````````` example
``foo`bar``
.
<p><code>foo`bar</code></p>
````````````````````````````````
```````````````````````````````` example
` foo `` bar `
.
<p><code>foo `` bar</code></p>
````````````````````````````````
Code span backticks have higher precedence than any other inline
constructs except HTML tags and autolinks. Thus, for example, this is
not parsed as emphasized text, since the second `*` is part of a code
@@ -5905,15 +6132,17 @@ of one or more `_` characters that is not preceded or followed by
a non-backslash-escaped `_` character.
A [left-flanking delimiter run](@) is
a [delimiter run] that is (a) not followed by [Unicode whitespace],
and (b) not followed by a [punctuation character], or
a [delimiter run] that is (1) not followed by [Unicode whitespace],
and either (2a) not followed by a [punctuation character], or
(2b) followed by a [punctuation character] and
preceded by [Unicode whitespace] or a [punctuation character].
For purposes of this definition, the beginning and the end of
the line count as Unicode whitespace.
A [right-flanking delimiter run](@) is
a [delimiter run] that is (a) not preceded by [Unicode whitespace],
and (b) not preceded by a [punctuation character], or
a [delimiter run] that is (1) not preceded by [Unicode whitespace],
and either (2a) not preceded by a [punctuation character], or
(2b) preceded by a [punctuation character] and
followed by [Unicode whitespace] or a [punctuation character].
For purposes of this definition, the beginning and the end of
the line count as Unicode whitespace.
@@ -6005,7 +6234,8 @@ The following rules define emphasis and strong emphasis:
[delimiter runs]. If one of the delimiters can both
open and close emphasis, then the sum of the lengths of the
delimiter runs containing the opening and closing delimiters
must not be a multiple of 3.
must not be a multiple of 3 unless both lengths are
multiples of 3.
10. Strong emphasis begins with a delimiter that
[can open strong emphasis] and ends with a delimiter that
@@ -6015,7 +6245,8 @@ The following rules define emphasis and strong emphasis:
[delimiter runs]. If one of the delimiters can both open
and close strong emphasis, then the sum of the lengths of
the delimiter runs containing the opening and closing
delimiters must not be a multiple of 3.
delimiters must not be a multiple of 3 unless both lengths
are multiples of 3.
11. A literal `*` character cannot occur at the beginning or end of
`*`-delimited emphasis or `**`-delimited strong emphasis, unless it
@@ -6634,7 +6865,19 @@ is precluded by the condition that a delimiter that
can both open and close (like the `*` after `foo`)
cannot form emphasis if the sum of the lengths of
the delimiter runs containing the opening and
closing delimiters is a multiple of 3.
closing delimiters is a multiple of 3 unless
both lengths are multiples of 3.
For the same reason, we don't get two consecutive
emphasis sections in this example:
```````````````````````````````` example
*foo**bar*
.
<p><em>foo**bar</em></p>
````````````````````````````````
The same condition ensures that the following
cases are all strong emphasis nested inside
@@ -6663,6 +6906,23 @@ omitted:
````````````````````````````````
When the lengths of the interior closing and opening
delimiter runs are *both* multiples of 3, though,
they can match to create emphasis:
```````````````````````````````` example
foo***bar***baz
.
<p>foo<em><strong>bar</strong></em>baz</p>
````````````````````````````````
```````````````````````````````` example
foo******bar*********baz
.
<p>foo<strong><strong><strong>bar</strong></strong></strong>***baz</p>
````````````````````````````````
Indefinite levels of nesting are possible:
```````````````````````````````` example
@@ -7198,15 +7458,16 @@ following rules apply:
A [link destination](@) consists of either
- a sequence of zero or more characters between an opening `<` and a
closing `>` that contains no spaces, line breaks, or unescaped
closing `>` that contains no line breaks or unescaped
`<` or `>` characters, or
- a nonempty sequence of characters that does not include
ASCII space or control characters, and includes parentheses
only if (a) they are backslash-escaped or (b) they are part of
a balanced pair of unescaped parentheses. (Implementations
may impose limits on parentheses nesting to avoid performance
issues, but at least three levels of nesting should be supported.)
- a nonempty sequence of characters that does not start with
`<`, does not include ASCII space or control characters, and
includes parentheses only if (a) they are backslash-escaped or
(b) they are part of a balanced pair of unescaped parentheses.
(Implementations may impose limits on parentheses nesting to
avoid performance issues, but at least three levels of nesting
should be supported.)
A [link title](@) consists of either
@@ -7219,7 +7480,8 @@ A [link title](@) consists of either
backslash-escaped, or
- a sequence of zero or more characters between matching parentheses
(`(...)`), including a `)` character only if it is backslash-escaped.
(`(...)`), including a `(` or `)` character only if it is
backslash-escaped.
Although [link titles] may span multiple lines, they may not contain
a [blank line].
@@ -7269,9 +7531,8 @@ Both the title and the destination may be omitted:
<p><a href="">link</a></p>
````````````````````````````````
The destination cannot contain spaces or line breaks,
even if enclosed in pointy brackets:
The destination can only contain spaces if it is
enclosed in pointy brackets:
```````````````````````````````` example
[link](/my uri)
@@ -7279,13 +7540,14 @@ even if enclosed in pointy brackets:
<p>[link](/my uri)</p>
````````````````````````````````
```````````````````````````````` example
[link](</my uri>)
.
<p>[link](&lt;/my uri&gt;)</p>
<p><a href="/my%20uri">link</a></p>
````````````````````````````````
The destination cannot contain line breaks,
even if enclosed in pointy brackets:
```````````````````````````````` example
[link](foo
@@ -7295,7 +7557,6 @@ bar)
bar)</p>
````````````````````````````````
```````````````````````````````` example
[link](<foo
bar>)
@@ -7304,6 +7565,36 @@ bar>)
bar>)</p>
````````````````````````````````
The destination can contain `)` if it is enclosed
in pointy brackets:
```````````````````````````````` example
[a](<b)c>)
.
<p><a href="b)c">a</a></p>
````````````````````````````````
Pointy brackets that enclose links must be unescaped:
```````````````````````````````` example
[link](<foo\>)
.
<p>[link](&lt;foo&gt;)</p>
````````````````````````````````
These are not links, because the opening pointy bracket
is not matched properly:
```````````````````````````````` example
[a](<b)c
[a](<b)c>
[a](<b>c)
.
<p>[a](&lt;b)c
[a](&lt;b)c&gt;
[a](<b>c)</p>
````````````````````````````````
Parentheses inside the link destination may be escaped:
```````````````````````````````` example
@@ -8411,7 +8702,7 @@ If you want a link after a literal `!`, backslash-escape the
as the link label.
A [URI autolink](@) consists of `<`, followed by an
[absolute URI] not containing `<`, followed by `>`. It is parsed as
[absolute URI] followed by `>`. It is parsed as
a link to the URI, with the URI as the link's label.
An [absolute URI](@),
@@ -8624,7 +8915,7 @@ a [single-quoted attribute value], or a [double-quoted attribute value].
An [unquoted attribute value](@)
is a nonempty string of characters not
including spaces, `"`, `'`, `=`, `<`, `>`, or `` ` ``.
including [whitespace], `"`, `'`, `=`, `<`, `>`, or `` ` ``.
A [single-quoted attribute value](@)
consists of `'`, zero or more
@@ -8745,9 +9036,13 @@ Illegal [whitespace]:
```````````````````````````````` example
< a><
foo><bar/ >
<foo bar=baz
bim!bop />
.
<p>&lt; a&gt;&lt;
foo&gt;&lt;bar/ &gt;</p>
foo&gt;&lt;bar/ &gt;
&lt;foo bar=baz
bim!bop /&gt;</p>
````````````````````````````````
@@ -8944,10 +9239,10 @@ bar</em></p>
Line breaks do not occur inside code spans
```````````````````````````````` example
`code
`code
span`
.
<p><code>code span</code></p>
<p><code>code span</code></p>
````````````````````````````````
@@ -9365,7 +9660,8 @@ just above `stack_bottom` (or the first element if `stack_bottom`
is NULL).
We keep track of the `openers_bottom` for each delimiter
type (`*`, `_`). Initialize this to `stack_bottom`.
type (`*`, `_`) and each length of the closing delimiter run
(modulo 3). Initialize this to `stack_bottom`.
Then we repeat the following until we run out of potential
closers:
@@ -9397,7 +9693,7 @@ closers:
of the delimiter stack. If the closing node is removed, reset
`current_position` to the next element in the stack.
- If none in found:
- If none is found:
+ Set `openers_bottom` to the element before `current_position`.
(We know that there are no openers for this kind of closer up to and

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Custom Containers

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-15 05:06:35
// --------------------------------
// Definition Lists
@@ -18,7 +18,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
//
// ## Definition lists
//
// 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.
[Test]
public void ExtensionsDefinitionLists_Example001()
{

View File

@@ -4,7 +4,7 @@ This section describes the different extensions supported:
## Definition lists
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
@@ -129,4 +129,4 @@ Definition lists can be nested inside list items
<dd>Second Definition</dd>
</dl></li>
</ol>
````````````````````````````````
````````````````````````````````

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Diagrams

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-15 05:20:50
// --------------------------------
// Emoji
@@ -52,7 +52,7 @@ namespace Markdig.Tests.Specs.Emoji
TestParser.TestSpec("These are not:) an emoji with a:) x:angry:x", "<p>These are not:) an emoji with a:) x:angry:x</p>", "emojis|advanced+emojis");
}
// Emoji can be followed by close ponctuation (or any other characters):
// Emoji can be followed by close punctuation (or any other characters):
[Test]
public void ExtensionsEmoji_Example003()
{

View File

@@ -20,7 +20,7 @@ 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):
Emoji can be followed by close punctuation (or any other characters):
```````````````````````````````` example
We all need :), it makes us :muscle:. (and :ok_hand:).

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Emphasis Extra

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:42:25
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Figures, Footers and Cites

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-15 05:33:49
// --------------------------------
// Footnotes
@@ -84,7 +84,7 @@ namespace Markdig.Tests.Specs.Footnotes
TestParser.TestSpec("Here is a footnote reference,[^1] and another.[^longnote]\n\nThis is another reference to [^1]\n\n[^1]: Here is the footnote.\n\nAnd another reference to [^longnote]\n\n[^longnote]: Here's one with multiple blocks.\n\n Subsequent paragraphs are indented to show that they\nbelong to the previous footnote.\n\n > This is a block quote\n > Inside a footnote\n\n { some.code }\n\n The whole paragraph can be indented, or just the first\n line. In this way, multi-paragraph footnotes work like\n multi-paragraph list items.\n\nThis paragraph won't be part of the note, because it\nisn't indented.", "<p>Here is a footnote reference,<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a> and another.<a id=\"fnref:3\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a></p>\n<p>This is another reference to <a id=\"fnref:2\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a></p>\n<p>And another reference to <a id=\"fnref:4\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a></p>\n<p>This paragraph won't be part of the note, because it\nisn't indented.</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Here is the footnote.<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a><a href=\"#fnref:2\" class=\"footnote-back-ref\">&#8617;</a></p>\n</li>\n<li id=\"fn:2\">\n<p>Here's one with multiple blocks.</p>\n<p>Subsequent paragraphs are indented to show that they\nbelong to the previous footnote.</p>\n<blockquote>\n<p>This is a block quote\nInside a footnote</p>\n</blockquote>\n<pre><code>{ some.code }\n</code></pre>\n<p>The whole paragraph can be indented, or just the first\nline. In this way, multi-paragraph footnotes work like\nmulti-paragraph list items.<a href=\"#fnref:3\" class=\"footnote-back-ref\">&#8617;</a><a href=\"#fnref:4\" class=\"footnote-back-ref\">&#8617;</a></p>\n</li>\n</ol>\n</div>", "footnotes|advanced");
}
// Check with mulitple consecutive footnotes:
// Check with multiple consecutive footnotes:
[Test]
public void ExtensionsFootnotes_Example002()
{

View File

@@ -61,7 +61,7 @@ multi-paragraph list items.<a href="#fnref:3" class="footnote-back-ref">&#8617;<
</div>
````````````````````````````````
Check with mulitple consecutive footnotes:
Check with multiple consecutive footnotes:
```````````````````````````````` example
Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 15:02:43
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Generic Attributes

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-15 05:25:26
// --------------------------------
// Globalization
@@ -173,7 +173,7 @@ namespace Markdig.Tests.Specs.Globalization
// Section: Extensions / Globalization
//
// The following Markdown:
// Nuitrion |Apple | Oranges
// Nutrition |Apple | Oranges
// --|-- | --
// Calories|52|47
// Sugar|10g|9g
@@ -187,7 +187,7 @@ namespace Markdig.Tests.Specs.Globalization
// <table>
// <thead>
// <tr>
// <th>Nuitrion</th>
// <th>Nutrition</th>
// <th>Apple</th>
// <th>Oranges</th>
// </tr>
@@ -228,7 +228,7 @@ namespace Markdig.Tests.Specs.Globalization
// </table>
Console.WriteLine("Example 3\nSection Extensions / Globalization\n");
TestParser.TestSpec("Nuitrion |Apple | Oranges\n--|-- | --\nCalories|52|47\nSugar|10g|9g\n\n پێکهاتە |سێو | پڕتەقاڵ\n--|-- | --\nکالۆری|٥٢|٤٧\nشەکر| ١٠گ|٩گ", "<table>\n<thead>\n<tr>\n<th>Nuitrion</th>\n<th>Apple</th>\n<th>Oranges</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Calories</td>\n<td>52</td>\n<td>47</td>\n</tr>\n<tr>\n<td>Sugar</td>\n<td>10g</td>\n<td>9g</td>\n</tr>\n</tbody>\n</table>\n<table dir=\"rtl\" align=\"right\">\n<thead>\n<tr>\n<th>پێکهاتە</th>\n<th>سێو</th>\n<th>پڕتەقاڵ</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>کالۆری</td>\n<td>٥٢</td>\n<td>٤٧</td>\n</tr>\n<tr>\n<td>شەکر</td>\n<td>١٠گ</td>\n<td>٩گ</td>\n</tr>\n</tbody>\n</table>", "globalization+advanced+emojis");
TestParser.TestSpec("Nutrition |Apple | Oranges\n--|-- | --\nCalories|52|47\nSugar|10g|9g\n\n پێکهاتە |سێو | پڕتەقاڵ\n--|-- | --\nکالۆری|٥٢|٤٧\nشەکر| ١٠گ|٩گ", "<table>\n<thead>\n<tr>\n<th>Nutrition</th>\n<th>Apple</th>\n<th>Oranges</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Calories</td>\n<td>52</td>\n<td>47</td>\n</tr>\n<tr>\n<td>Sugar</td>\n<td>10g</td>\n<td>9g</td>\n</tr>\n</tbody>\n</table>\n<table dir=\"rtl\" align=\"right\">\n<thead>\n<tr>\n<th>پێکهاتە</th>\n<th>سێو</th>\n<th>پڕتەقاڵ</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>کالۆری</td>\n<td>٥٢</td>\n<td>٤٧</td>\n</tr>\n<tr>\n<td>شەکر</td>\n<td>١٠گ</td>\n<td>٩گ</td>\n</tr>\n</tbody>\n</table>", "globalization+advanced+emojis");
}
}
}

View File

@@ -134,7 +134,7 @@ Lists:
Tables:
```````````````````````````````` example
Nuitrion |Apple | Oranges
Nutrition |Apple | Oranges
--|-- | --
Calories|52|47
Sugar|10g|9g
@@ -147,7 +147,7 @@ Sugar|10g|9g
<table>
<thead>
<tr>
<th>Nuitrion</th>
<th>Nutrition</th>
<th>Apple</th>
<th>Oranges</th>
</tr>

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Grid Tables

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Hardline Breaks

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:22:45
// Generated: 2019-04-15 05:30:00
// --------------------------------
// Jira Links
@@ -14,7 +14,7 @@ namespace Markdig.Tests.Specs.JiraLinks
{
// ## 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.
// 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()
@@ -24,10 +24,10 @@ namespace Markdig.Tests.Specs.JiraLinks
//
// 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 project key must be composed of one or more capitalized ASCII letter `[A-Z]+`
// - A single hyphen `-` must separate the project key and issue number.
// - The issue number is composed of 1 or more digits `[0, 9]+`
// - The reference must be preceeded by either `(` or whitespace or EOF.
// - The reference must be preceded by either `(` or whitespace or EOF.
// - The reference must be followed by either `)` or whitespace or EOF.
//
// The following are valid examples:

View File

@@ -1,6 +1,6 @@
## 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.
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()
@@ -10,10 +10,10 @@ var pipeline = new MarkdownPipelineBuilder()
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 project key must be composed of one or more capitalized ASCII letter `[A-Z]+`
- A single hyphen `-` must separate the project key and issue number.
- The issue number is composed of 1 or more digits `[0, 9]+`
- The reference must be preceeded by either `(` or whitespace or EOF.
- The reference must be preceded by either `(` or whitespace or EOF.
- The reference must be followed by either `)` or whitespace or EOF.
The following are valid examples:

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-05 16:06:14
// --------------------------------
// List Extras

View File

@@ -1,4 +1,4 @@
// Generated: 2/22/2019 8:27:26 PM
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Math

View File

@@ -1,50 +0,0 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Media
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.Media
{
[TestFixture]
public class TestExtensionsMediaLinks
{
// # Extensions
//
// Adds support for media links:
//
// ## Media links
//
// Allows to embed audio/video links to popular website:
[Test]
public void ExtensionsMediaLinks_Example001()
{
// Example 1
// Section: Extensions / Media links
//
// The following Markdown:
// ![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)
//
// Should be rendered as:
// <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>
Console.WriteLine("Example 1\nSection Extensions / Media links\n");
TestParser.TestSpec("![Video1](https://www.youtube.com/watch?v=mswPy5bt3TQ)\n\n![Video2](https://vimeo.com/8607834)\n\n![Video3](https://sample.com/video.mp4)\n\n![Audio4](https://music.yandex.ru/album/411845/track/4402274)\n\n![Video5](https://ok.ru/video/26870090463)", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://player.vimeo.com/video/8607834\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"https://sample.com/video.mp4\"></source></video></p>\n<p><iframe src=\"https://music.yandex.ru/iframe/#track/4402274/411845/\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n<p><iframe src=\"https://ok.ru/videoembed/26870090463\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>", "medialinks|advanced+medialinks");
}
}
}

View File

@@ -0,0 +1,65 @@
// Generated: 2019-04-29 18:40:06
// --------------------------------
// Media
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.Media
{
[TestFixture]
public class TestExtensionsMediaLinks
{
// # Extensions
//
// Adds support for media links:
//
// ## Media links
//
// Allows to embed audio/video links to popular website:
[Test]
public void ExtensionsMediaLinks_Example001()
{
// Example 1
// Section: Extensions / Media links
//
// The following Markdown:
// ![youtube.com](https://www.youtube.com/watch?v=mswPy5bt3TQ)
//
// ![youtube.com with t](https://www.youtube.com/watch?v=mswPy5bt3TQ&t=100)
//
// ![youtu.be](https://youtu.be/mswPy5bt3TQ)
//
// ![youtu.be with t](https://youtu.be/mswPy5bt3TQ?t=100)
//
// ![youtube.com/embed 1](https://www.youtube.com/embed/mswPy5bt3TQ?start=100&rel=0)
//
// ![youtube.com/embed 2](https://www.youtube.com/embed?listType=playlist&list=PLC77007E23FF423C6)
//
// ![vimeo](https://vimeo.com/8607834)
//
// ![static mp4](https://sample.com/video.mp4)
//
// ![yandex.ru](https://music.yandex.ru/album/411845/track/4402274)
//
// ![ok.ru](https://ok.ru/video/26870090463)
//
// Should be rendered as:
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100&amp;rel=0" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
// <p><iframe src="https://www.youtube.com/embed?listType=playlist&amp;list=PLC77007E23FF423C6" 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>
Console.WriteLine("Example 1\nSection Extensions / Media links\n");
TestParser.TestSpec("![youtube.com](https://www.youtube.com/watch?v=mswPy5bt3TQ)\n\n![youtube.com with t](https://www.youtube.com/watch?v=mswPy5bt3TQ&t=100)\n\n![youtu.be](https://youtu.be/mswPy5bt3TQ)\n\n![youtu.be with t](https://youtu.be/mswPy5bt3TQ?t=100)\n\n![youtube.com/embed 1](https://www.youtube.com/embed/mswPy5bt3TQ?start=100&rel=0)\n \n![youtube.com/embed 2](https://www.youtube.com/embed?listType=playlist&list=PLC77007E23FF423C6)\n\n![vimeo](https://vimeo.com/8607834)\n\n![static mp4](https://sample.com/video.mp4)\n\n![yandex.ru](https://music.yandex.ru/album/411845/track/4402274)\n\n![ok.ru](https://ok.ru/video/26870090463)", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100&amp;rel=0\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed?listType=playlist&amp;list=PLC77007E23FF423C6\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://player.vimeo.com/video/8607834\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"https://sample.com/video.mp4\"></source></video></p>\n<p><iframe src=\"https://music.yandex.ru/iframe/#track/4402274/411845/\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n<p><iframe src=\"https://ok.ru/videoembed/26870090463\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>", "medialinks|advanced+medialinks");
}
}
}

View File

@@ -7,17 +7,32 @@ Adds support for media links:
Allows to embed audio/video links to popular website:
```````````````````````````````` example
![Video1](https://www.youtube.com/watch?v=mswPy5bt3TQ)
![youtube.com](https://www.youtube.com/watch?v=mswPy5bt3TQ)
![Video2](https://vimeo.com/8607834)
![youtube.com with t](https://www.youtube.com/watch?v=mswPy5bt3TQ&t=100)
![Video3](https://sample.com/video.mp4)
![youtu.be](https://youtu.be/mswPy5bt3TQ)
![Audio4](https://music.yandex.ru/album/411845/track/4402274)
![youtu.be with t](https://youtu.be/mswPy5bt3TQ?t=100)
![Video5](https://ok.ru/video/26870090463)
![youtube.com/embed 1](https://www.youtube.com/embed/mswPy5bt3TQ?start=100&rel=0)
![youtube.com/embed 2](https://www.youtube.com/embed?listType=playlist&list=PLC77007E23FF423C6)
![vimeo](https://vimeo.com/8607834)
![static mp4](https://sample.com/video.mp4)
![yandex.ru](https://music.yandex.ru/album/411845/track/4402274)
![ok.ru](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://www.youtube.com/embed/mswPy5bt3TQ?start=100" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100&amp;rel=0" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
<p><iframe src="https://www.youtube.com/embed?listType=playlist&amp;list=PLC77007E23FF423C6" 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>

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-05 16:06:14
// --------------------------------
// No Html

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-15 05:54:35
// --------------------------------
// Pipe Tables
@@ -21,7 +21,7 @@ namespace Markdig.Tests.Specs.PipeTables
// A pipe table is detected when:
//
// **Rule #1**
// - Each line of a paragraph block have to contain at least a **column delimiter** `|` that is not embedded by either a code inline (backstick \`) or a HTML inline.
// - Each line of a paragraph block have to contain at least a **column delimiter** `|` that is not embedded by either a code inline (backtick \`) or a HTML inline.
// - The second row must separate the first header row from sub-sequent rows by containing a **header column separator** for each column separated by a column delimiter. A header column separator is:
// - starting by optional spaces
// - followed by an optional `:` to specify left align
@@ -347,7 +347,7 @@ namespace Markdig.Tests.Specs.PipeTables
TestParser.TestSpec("|a|b|\n|-|-|\n|0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// Or may be ommitted on one side:
// Or may be omitted on one side:
[Test]
public void ExtensionsPipeTable_Example011()
{
@@ -629,7 +629,7 @@ namespace Markdig.Tests.Specs.PipeTables
// **Rule #7**
//
// A backstick/code delimiter has a higher precedence than a column delimiter `|`:
// A backtick/code delimiter has a higher precedence than a column delimiter `|`:
[Test]
public void ExtensionsPipeTable_Example019()
{

View File

@@ -7,7 +7,7 @@ This section describes the different extensions supported:
A pipe table is detected when:
**Rule #1**
- Each line of a paragraph block have to contain at least a **column delimiter** `|` that is not embedded by either a code inline (backstick \`) or a HTML inline.
- Each line of a paragraph block have to contain at least a **column delimiter** `|` that is not embedded by either a code inline (backtick \`) or a HTML inline.
- The second row must separate the first header row from sub-sequent rows by containing a **header column separator** for each column separated by a column delimiter. A header column separator is:
- starting by optional spaces
- followed by an optional `:` to specify left align
@@ -243,7 +243,7 @@ A pipe may be present at both the beginning/ending of each line:
</table>
````````````````````````````````
Or may be ommitted on one side:
Or may be omitted on one side:
```````````````````````````````` example
a|b|
@@ -454,7 +454,7 @@ A column delimiter has a higher priority than emphasis delimiter
**Rule #7**
A backstick/code delimiter has a higher precedence than a column delimiter `|`:
A backtick/code delimiter has a higher precedence than a column delimiter `|`:
```````````````````````````````` example
a | b `

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Smarty Pants

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Task Lists

View File

@@ -1,4 +1,4 @@
// Generated: 21. 01. 2019 14:26:34
// Generated: 2019-04-05 16:06:14
// --------------------------------
// Yaml

View File

@@ -1,9 +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 NUnit.Framework;
using Markdig.Helpers;
using Markdig.Syntax;
namespace Markdig.Tests
{
@@ -15,8 +14,7 @@ namespace Markdig.Tests
{
var inputTag = "<a>";
var text = new StringSlice(inputTag);
string outputTag;
Assert.True(HtmlHelper.TryParseHtmlTag(text, out outputTag));
Assert.True(HtmlHelper.TryParseHtmlTag(text, out string outputTag));
Assert.AreEqual(inputTag, outputTag);
}
@@ -25,8 +23,7 @@ namespace Markdig.Tests
{
var inputTag = "<a href='http://google.com'>";
var text = new StringSlice(inputTag);
string outputTag;
Assert.True(HtmlHelper.TryParseHtmlTag(text, out outputTag));
Assert.True(HtmlHelper.TryParseHtmlTag(text, out string outputTag));
Assert.AreEqual(inputTag, outputTag);
}
}

View File

@@ -409,5 +409,11 @@ namespace Markdig.Tests
{
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
}
[Test]
public void TestUnicodeInDomainNameOfLinkReferenceDefinition()
{
TestParser.TestSpec("[Foo]\n\n[Foo]: http://ünicode.com", "<p><a href=\"http://xn--nicode-2ya.com\">Foo</a></p>");
}
}
}

View File

@@ -271,8 +271,14 @@ asdf
[Test]
public void CodeInline()
{
AssertNormalizeNoTrim("This has a ` ` in it");
AssertNormalizeNoTrim("This has a `HelloWorld()` in it");
AssertNormalizeNoTrim(@"This has a ``Hello`World()`` in it");
AssertNormalizeNoTrim(@"This has a ``` Hello`World() ``` in it", @"This has a ``Hello`World()`` in it");
AssertNormalizeNoTrim(@"This has a ``Hello`World()` `` in it");
AssertNormalizeNoTrim(@"This has a ```` ``Hello```World()` ```` in it");
AssertNormalizeNoTrim(@"This has a `` `Hello`World()`` in it");
AssertNormalizeNoTrim(@"This has a ``` ``Hello`World()` ``` in it");
}
[Test]
@@ -409,8 +415,6 @@ This is a last line";
AssertNormalizeNoTrim("[ ] This is not a task list");
}
[Test]
public void JiraLinks()
{
@@ -453,16 +457,24 @@ This is a last line";
Assert.AreEqual(expected, actual);
}
public void AssertNormalizeNoTrim(string input, string expected = null, NormalizeOptions options = null)
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null)
{
foreach (var pipeline in TestParser.GetPipeline(extensions))
{
AssertNormalize(inputText, expectedOutputText, trim: false, pipeline: pipeline.Value);
}
}
public static 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)
public static void AssertNormalize(string input, string expected = null, bool trim = true, NormalizeOptions options = null, MarkdownPipeline pipeline = null)
{
expected = expected ?? input;
input = NormText(input, trim);
expected = NormText(expected, trim);
var pipeline = new MarkdownPipelineBuilder()
pipeline = pipeline ?? new MarkdownPipelineBuilder()
.UseAutoLinks()
.UseJiraLinks(new Extensions.JiraLinks.JiraLinkOptions("https://jira.example.com"))
.UseTaskLists()
@@ -471,16 +483,7 @@ This is a last line";
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);
TestParser.PrintAssertExpected(input, result, expected);
}
private static string NormText(string text, bool trim)

View File

@@ -14,60 +14,6 @@ namespace Markdig.Tests
{
public class TestParser
{
[Test]
public void TestFixHang()
{
var input = File.ReadAllText(Path.Combine(Path.GetDirectoryName(typeof(TestParser).Assembly.Location), "hang.md"));
var html = Markdown.ToHtml(input);
}
[Test]
public void TestInvalidHtmlEntity()
{
var input = "9&ddr;&*&ddr;&de<64><65>__";
TestSpec(input, "<p>9&amp;ddr;&amp;*&amp;ddr;&amp;de<64><65>__</p>");
}
[Test]
public void TestInvalidCharacterHandling()
{
var input = File.ReadAllText(Path.Combine(Path.GetDirectoryName(typeof(TestParser).Assembly.Location), "ArgumentOutOfRangeException.md"));
var html = Markdown.ToHtml(input);
}
[Test]
public void TestInvalidCodeEscape()
{
var input = "```**Header** ";
var html = Markdown.ToHtml(input);
}
[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>");
}
[Test]
public void EnsureSpecsAreUpToDate()
{
@@ -77,7 +23,7 @@ namespace Markdig.Tests
foreach (var specFilePath in SpecsFilePaths)
{
string testFilePath = Path.ChangeExtension(specFilePath, ".cs");
string testFilePath = Path.ChangeExtension(specFilePath, ".generated.cs");
Assert.True(File.Exists(testFilePath),
"A new specification file has been added. Add the spec to the list in SpecFileGen and regenerate the tests.");
@@ -98,104 +44,41 @@ namespace Markdig.Tests
}
}
[Test]
public void VisualizeMathExpressions()
{
string math = @"Math expressions
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
$$\frac{n!}{k!(n-k)!} = \binom{n}{k}$$
$$
\frac{n!}{k!(n-k)!} = \binom{n}{k}
$$
<div class=""math"">
\begin{align}
\sqrt{37} & = \sqrt{\frac{73^2-1}{12^2}} \\
& = \sqrt{\frac{73^2}{12^2}\cdot\frac{73^2-1}{73^2}} \\
& = \sqrt{\frac{73^2}{12^2}}\sqrt{\frac{73^2-1}{73^2}} \\
& = \frac{73}{12}\sqrt{1 - \frac{1}{73^2}} \\
& \approx \frac{73}{12}\left(1 - \frac{1}{2\cdot73^2}\right)
\end{align}
</div>
";
Console.WriteLine("Math Expressions:\n");
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
var html = Markdown.ToHtml(math, pl);
Console.WriteLine(html);
}
[Test]
public void InlineMathExpression()
{
string math = @"Math expressions
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
";
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
var html = Markdown.ToHtml(math, pl);
Console.WriteLine(html);
Assert.IsTrue(html.Contains("<p><span class=\"math\">\\("),"Leading bracket missing");
Assert.IsTrue(html.Contains("\\)</span></p>"), "Trailing bracket missing");
}
[Test]
public void BlockMathExpression()
{
string math = @"Math expressions
$$
\frac{n!}{k!(n-k)!} = \binom{n}{k}
$$
";
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
var html = Markdown.ToHtml(math, pl);
Console.WriteLine(html);
Assert.IsTrue(html.Contains("<div class=\"math\">\n\\["), "Leading bracket missing");
Assert.IsTrue(html.Contains("\\]</div>"), "Trailing bracket missing");
}
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null)
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null, bool plainText = false)
{
foreach (var pipeline in GetPipeline(extensions))
{
Console.WriteLine($"Pipeline configured with extensions: {pipeline.Key}");
TestSpec(inputText, expectedOutputText, pipeline.Value);
TestSpec(inputText, expectedOutputText, pipeline.Value, plainText);
}
}
public static void TestSpec(string inputText, string expectedOutputText, MarkdownPipeline pipeline)
public static void TestSpec(string inputText, string expectedOutputText, MarkdownPipeline pipeline, bool plainText = false)
{
// Uncomment this line to get more debug information for process inlines.
//pipeline.DebugLog = Console.Out;
var result = Markdown.ToHtml(inputText, pipeline);
var result = plainText ? Markdown.ToPlainText(inputText, pipeline) : Markdown.ToHtml(inputText, pipeline);
result = Compact(result);
expectedOutputText = Compact(expectedOutputText);
PrintAssertExpected(inputText, result, expectedOutputText);
}
public static void PrintAssertExpected(string source, string result, string expected)
{
Console.WriteLine("```````````````````Source");
Console.WriteLine(DisplaySpaceAndTabs(inputText));
Console.WriteLine(DisplaySpaceAndTabs(source));
Console.WriteLine("```````````````````Result");
Console.WriteLine(DisplaySpaceAndTabs(result));
Console.WriteLine("```````````````````Expected");
Console.WriteLine(DisplaySpaceAndTabs(expectedOutputText));
Console.WriteLine(DisplaySpaceAndTabs(expected));
Console.WriteLine("```````````````````");
Console.WriteLine();
TextAssert.AreEqual(expectedOutputText, result);
TextAssert.AreEqual(expected, result);
}
private static IEnumerable<KeyValuePair<string, MarkdownPipeline>> GetPipeline(string extensionsGroupText)
public 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!
if (string.IsNullOrEmpty(extensionsGroupText))
@@ -257,6 +140,9 @@ $$
public static readonly bool IsContinuousIntegration = Environment.GetEnvironmentVariable("CI") != null;
public static readonly string TestsDirectory =
Path.GetFullPath(Path.Combine(Path.GetDirectoryName(typeof(TestParser).Assembly.Location), "../../.."));
/// <summary>
/// Contains absolute paths to specification markdown files (order is the same as in <see cref="SpecsMarkdown"/>)
/// </summary>
@@ -267,11 +153,11 @@ $$
public static readonly string[] SpecsMarkdown;
static TestParser()
{
string assemblyDir = Path.GetDirectoryName(typeof(TestParser).Assembly.Location);
string specsDir = Path.GetFullPath(Path.Combine(assemblyDir, "../../Specs"));
SpecsFilePaths = Directory.GetFiles(specsDir)
.Where(file => file.EndsWith(".md", StringComparison.Ordinal) && !file.Contains("readme"))
SpecsFilePaths = Directory.GetDirectories(TestsDirectory)
.Where(dir => dir.EndsWith("Specs"))
.SelectMany(dir => Directory.GetFiles(dir)
.Where(file => file.EndsWith(".md", StringComparison.OrdinalIgnoreCase))
.Where(file => file.IndexOf("readme", StringComparison.OrdinalIgnoreCase) == -1))
.ToArray();
SpecsMarkdown = new string[SpecsFilePaths.Length];

View File

@@ -5,15 +5,6 @@ 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")]
@@ -28,11 +19,25 @@ namespace Markdig.Tests
[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")]
[TestCase(/* markdownText: */ "- foo<baz", /* expected: */ "foo<baz\n")]
[TestCase(/* markdownText: */ "- foo&lt;baz", /* expected: */ "foo<baz\n")]
public void TestPlainEnsureNewLine(string markdownText, string expected)
{
var actual = Markdown.ToPlainText(markdownText);
Assert.AreEqual(expected, actual);
}
[Test]
[TestCase(/* markdownText: */ ":::\nfoo\n:::", /* expected: */ "foo\n", /*extensions*/ "customcontainers|advanced")]
[TestCase(/* markdownText: */ ":::bar\nfoo\n:::", /* expected: */ "foo\n", /*extensions*/ "customcontainers+attributes|advanced")]
public void TestPlainWithExtensions(string markdownText, string expected, string extensions)
{
TestParser.TestSpec(markdownText, expected, extensions, plainText: true);
}
public static void TestSpec(string markdownText, string expected, string extensions)
{
TestParser.TestSpec(markdownText, expected, extensions, plainText: true);
}
}
}

View File

@@ -1,24 +0,0 @@
{
"runtimes": {
"win": {}
},
"frameworks": {
"net451": {
"compilationOptions": {
"define": [
"CLASSIC"
]
},
"frameworkAssemblies": {
"Microsoft.Build": "4.0.0.0",
"Microsoft.Build.Framework": "4.0.0.0",
"Microsoft.Build.Utilities.v4.0": "4.0.0.0",
"System.Management": "4.0.0.0"
}
}
},
"dependencies": {
"NUnit": "3.2.0",
"NUnit3TestAdapter": "3.9.0"
}
}

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
@@ -111,12 +111,12 @@ namespace Markdig.Extensions.AutoIdentifiers
doc.SetLinkReferenceDefinition(keyPair.Key, keyPair.Value);
}
}
// Once we are done, we don't need to keep the intermediate dictionary arround
// Once we are done, we don't need to keep the intermediate dictionary around
doc.RemoveData(this);
}
/// <summary>
/// Callback when there is a reference to found to a heading.
/// 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)

View File

@@ -11,14 +11,14 @@ 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" />
/// <seealso cref="IMarkdownExtension" />
public class AutoLinkExtension : IMarkdownExtension
{
public readonly string ValidPreviousCharacters;
public readonly AutoLinkOptions Options;
public AutoLinkExtension(string validPreviousCharacters = AutoLinkParser.DefaultValidPreviousCharacters)
public AutoLinkExtension(AutoLinkOptions options)
{
ValidPreviousCharacters = validPreviousCharacters;
Options = options ?? new AutoLinkOptions();
}
public void Setup(MarkdownPipelineBuilder pipeline)
@@ -26,14 +26,13 @@ namespace Markdig.Extensions.AutoLinks
if (!pipeline.InlineParsers.Contains<AutoLinkParser>())
{
// Insert the parser before any other parsers
pipeline.InlineParsers.Insert(0, new AutoLinkParser(ValidPreviousCharacters));
pipeline.InlineParsers.Insert(0, new AutoLinkParser(Options));
}
}
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var normalizeRenderer = renderer as NormalizeRenderer;
if (normalizeRenderer != null && !normalizeRenderer.ObjectRenderers.Contains<NormalizeAutoLinkRenderer>())
if (renderer is NormalizeRenderer normalizeRenderer && !normalizeRenderer.ObjectRenderers.Contains<NormalizeAutoLinkRenderer>())
{
normalizeRenderer.ObjectRenderers.InsertBefore<LinkInlineRenderer>(new NormalizeAutoLinkRenderer());
}

View File

@@ -0,0 +1,22 @@
namespace Markdig.Extensions.AutoLinks
{
public class AutoLinkOptions
{
public AutoLinkOptions()
{
ValidPreviousCharacters = "*_~(";
}
public string ValidPreviousCharacters { get; set; }
/// <summary>
/// Should the link open in a new window when clicked (false by default)
/// </summary>
public bool OpenInNewWindow { get; set; }
/// <summary>
/// Should a www link be prefixed with https:// instead of http:// (false by default)
/// </summary>
public bool UseHttpsForWWWLinks { get; set; }
}
}

View File

@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Renderers.Html;
using Markdig.Syntax.Inlines;
namespace Markdig.Extensions.AutoLinks
@@ -19,8 +20,10 @@ namespace Markdig.Extensions.AutoLinks
/// <summary>
/// Initializes a new instance of the <see cref="AutoLinkParser"/> class.
/// </summary>
public AutoLinkParser(string validPreviousCharacters = DefaultValidPreviousCharacters)
public AutoLinkParser(AutoLinkOptions options)
{
Options = options ?? throw new ArgumentNullException(nameof(options));
OpeningCharacters = new char[]
{
'h', // for http:// and https://
@@ -28,19 +31,15 @@ namespace Markdig.Extensions.AutoLinks
'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 readonly AutoLinkOptions Options;
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)
if (!previousChar.IsWhiteSpaceOrZero() && Options.ValidPreviousCharacters.IndexOf(previousChar) == -1)
{
return false;
}
@@ -153,17 +152,15 @@ namespace Markdig.Extensions.AutoLinks
return false;
}
int line;
int column;
var inline = new LinkInline()
{
Span =
{
Start = processor.GetSourcePosition(startPosition, out line, out column),
Start = processor.GetSourcePosition(startPosition, out int line, out int column),
},
Line = line,
Column = column,
Url = c == 'w' ? "http://" + link : link,
Url = c == 'w' ? ((Options.UseHttpsForWWWLinks ? "https://" : "http://") + link) : link,
IsClosed = true,
IsAutoLink = true,
};
@@ -182,6 +179,11 @@ namespace Markdig.Extensions.AutoLinks
});
processor.Inline = inline;
if (Options.OpenInNewWindow)
{
inline.GetAttributes().AddPropertyIfNotExist("target", "blank");
}
return true;
}

View File

@@ -15,10 +15,16 @@ namespace Markdig.Extensions.CustomContainers
protected override void Write(HtmlRenderer renderer, CustomContainer obj)
{
renderer.EnsureLine();
renderer.Write("<div").WriteAttributes(obj).Write(">");
if (renderer.EnableHtmlForBlock)
{
renderer.Write("<div").WriteAttributes(obj).Write(">");
}
// We don't escape a CustomContainer
renderer.WriteChildren(obj);
renderer.WriteLine("</div>");
if (renderer.EnableHtmlForBlock)
{
renderer.WriteLine("</div>");
}
}
}
}

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Parsers;
@@ -8,7 +8,7 @@ using Markdig.Syntax;
namespace Markdig.Extensions.Footers
{
/// <summary>
/// A block elemeent for a footer.
/// A block element for a footer.
/// </summary>
/// <seealso cref="Markdig.Syntax.ContainerBlock" />
public class FooterBlock : ContainerBlock

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System.Collections.Generic;
@@ -33,7 +33,7 @@ namespace Markdig.Extensions.GenericAttributes
{
var inline = processor.Inline;
// If the curent object to attach is either a literal or delimiter
// If the current object to attach is either a literal or delimiter
// try to find a suitable parent, otherwise attach the html attributes to the block
if (inline is LiteralInline)
{
@@ -86,7 +86,7 @@ namespace Markdig.Extensions.GenericAttributes
/// </summary>
/// <param name="slice">The slice to parse.</param>
/// <param name="attributes">The output attributes or null if not found or invalid</param>
/// <returns><c>true</c> if parsing the HTML attributes was succsesfull</returns>
/// <returns><c>true</c> if parsing the HTML attributes was successful</returns>
public static bool TryParse(ref StringSlice slice, out HtmlAttributes attributes)
{
attributes = null;
@@ -173,7 +173,7 @@ namespace Markdig.Extensions.GenericAttributes
line.TrimStart();
c = line.CurrentChar;
// Handle boolean properties that are not followed by =
// Handle boolean properties that are not followed by =
if ((hasSpace && (c == '.' || c == '#' || IsStartAttributeName(c))) || c == '}')
{
if (properties == null)
@@ -184,7 +184,7 @@ namespace Markdig.Extensions.GenericAttributes
properties.Add(new KeyValuePair<string, string>(name, null));
continue;
}
// Else we expect a regular property
if (line.CurrentChar != '=')
{
@@ -264,7 +264,7 @@ namespace Markdig.Extensions.GenericAttributes
Properties = properties
};
// Assign back the current processor of the line to
// Assign back the current processor of the line to
slice = line;
}
return isValid;

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;
@@ -92,7 +92,7 @@ namespace Markdig.Extensions.JiraLinks
jiraLink.Span.End = jiraLink.Span.Start + (endIssue - startKey);
// Builds the Url
var builder = new StringBuilder();
var builder = StringBuilderCache.Local();
builder.Append(_baseUrl).Append('/').Append(jiraLink.ProjectKey).Append('-').Append(jiraLink.Issue);
jiraLink.Url = builder.ToString();

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.
@@ -45,7 +45,7 @@ namespace Markdig.Extensions.Mathematics
return block;
}
private static bool NoInfoParser(BlockProcessor state, ref StringSlice line, IFencedBlock fenced)
private static bool NoInfoParser(BlockProcessor state, ref StringSlice line, IFencedBlock fenced, char openingCharacter)
{
var c = line.CurrentChar;
for (int i = line.Start; i <= line.End; i++)

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Helpers;
@@ -57,7 +57,7 @@ namespace Markdig.Extensions.Mathematics
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
@@ -91,7 +91,7 @@ namespace Markdig.Extensions.Mathematics
// Don't process sticks if we have a '\' as a previous char
if (pc != '\\' )
{
// Record continous whitespaces at the end
// Record continuous whitespaces at the end
if (c.IsSpaceOrTab())
{
if (lastWhiteSpace < 0)
@@ -146,7 +146,7 @@ namespace Markdig.Extensions.Mathematics
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
// and if the character after an opening $/$$ was a whitespace, it should be
// a whitespace as well for the character preceding the closing of $/$$
if ((!closeNextIsPunctuation && !closeNextIsWhiteSpace) || (openNextIsWhiteSpace != closePrevIsWhiteSpace))
{

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
@@ -127,6 +127,7 @@ namespace Markdig.Extensions.MediaLinks
private static readonly List<KnownProvider> KnownHosts = new List<KnownProvider>()
{
new KnownProvider {HostPrefix = "www.youtube.com", Delegate = YouTube},
new KnownProvider {HostPrefix = "youtu.be", Delegate = YouTubeShortened},
new KnownProvider {HostPrefix = "vimeo.com", Delegate = Vimeo},
new KnownProvider {HostPrefix = "music.yandex.ru", Delegate = Yandex, AllowFullScreen = false},
new KnownProvider {HostPrefix = "ok.ru", Delegate = Odnoklassniki},
@@ -135,7 +136,7 @@ namespace Markdig.Extensions.MediaLinks
private bool TryRenderIframeFromKnownProviders(Uri uri, HtmlRenderer renderer, LinkInline linkInline)
{
var foundProvider =
var foundProvider =
KnownHosts
.Where(pair => uri.Host.StartsWith(pair.HostPrefix, StringComparison.OrdinalIgnoreCase)) // when host is match
.Select(provider =>
@@ -153,7 +154,9 @@ namespace Markdig.Extensions.MediaLinks
}
var htmlAttributes = GetHtmlAttributes(linkInline);
renderer.Write($"<iframe src=\"{foundProvider.Result}\"");
renderer.Write("<iframe src=\"");
renderer.WriteEscapeUrl(foundProvider.Result);
renderer.Write("\"");
if(!string.IsNullOrEmpty(Options.Width))
htmlAttributes.AddPropertyIfNotExist("width", Options.Width);
@@ -184,10 +187,38 @@ namespace Markdig.Extensions.MediaLinks
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;
string uriPath = uri.AbsolutePath;
if (string.Equals(uriPath, "/embed", StringComparison.OrdinalIgnoreCase) || uriPath.StartsWith("/embed/", StringComparison.OrdinalIgnoreCase))
{
return uri.ToString();
}
if (!string.Equals(uriPath, "/watch", StringComparison.OrdinalIgnoreCase) && !uriPath.StartsWith("/watch/", StringComparison.OrdinalIgnoreCase))
{
return null;
}
var queryParams = SplitQuery(uri);
return BuildYouTubeIframeUrl(
queryParams.FirstOrDefault(p => p.StartsWith("v="))?.Substring(2),
queryParams.FirstOrDefault(p => p.StartsWith("t="))?.Substring(2)
);
}
private static string YouTubeShortened(Uri uri)
{
return BuildYouTubeIframeUrl(
uri.AbsolutePath.Substring(1),
SplitQuery(uri).FirstOrDefault(p => p.StartsWith("t="))?.Substring(2)
);
}
private static string BuildYouTubeIframeUrl(string videoId, string startTime)
{
if (string.IsNullOrEmpty(videoId))
{
return null;
}
string url = $"https://www.youtube.com/embed/{videoId}";
return string.IsNullOrEmpty(startTime) ? url : $"{url}?start={startTime}";
}
private static string Vimeo(Uri uri)
@@ -223,4 +254,4 @@ namespace Markdig.Extensions.MediaLinks
}
#endregion
}
}
}

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System.Collections.Generic;
using Markdig.Helpers;
@@ -168,7 +168,7 @@ namespace Markdig.Extensions.SmartyPants
};
pant.Span.End = pant.Span.Start + slice.Start - startingPosition - 1;
// We will check in a post-process step for balanaced open/close quotes
// We will check in a post-process step for balanced open/close quotes
if (postProcess)
{
var quotePants = GetOrCreateState(processor);

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
@@ -51,7 +51,7 @@ namespace Markdig.Extensions.Tables
var c = slice.CurrentChar;
// If we have not a delimiter on the first line of a paragraph, don't bother to continue
// If we have not a delimiter on the first line of a paragraph, don't bother to continue
// tracking other delimiters on following lines
var tableState = processor.ParserStates[Index] as TableState;
bool isFirstLineEmpty = false;
@@ -66,7 +66,7 @@ namespace Markdig.Extensions.Tables
{
// A table could be preceded by an empty line or a line containing an inline
// that has not been added to the stack, so we consider this as a valid
// that has not been added to the stack, so we consider this as a valid
// start for a table. Typically, with this, we can have an attributes {...}
// starting on the first line of a pipe table, even if the first line
// doesn't have a pipe
@@ -188,7 +188,7 @@ namespace Markdig.Extensions.Tables
tableState.ColumnAndLineDelimiters.Remove(pipeDelimiter);
}
// If we didn't have any delimiter before and after the delimiters we jsut removed, we mark the processor of the current line as no pipe
// If we didn't have any delimiter before and after the delimiters we just removed, we mark the processor of the current line as no pipe
if (!leftIsDelimiter && !rightIsDelimiter)
{
tableState.LineHasPipe = false;
@@ -237,7 +237,7 @@ namespace Markdig.Extensions.Tables
// So the following:
// | a | b \n
// | d | e \n
//
//
// Will generate a tree of the following node:
// |
// a
@@ -343,7 +343,7 @@ namespace Markdig.Extensions.Tables
}
break;
}
beginOfCell = cellContentIt;
if (endOfCell == null)
{
@@ -353,7 +353,7 @@ namespace Markdig.Extensions.Tables
// If the current deilimiter is a pipe `|` OR
// the beginOfCell/endOfCell are not null and
// the beginOfCell/endOfCell are not null and
// either they are :
// - different
// - they contain a single element, but it is not a line break (\n) or an empty/whitespace Literal.
@@ -489,7 +489,7 @@ namespace Markdig.Extensions.Tables
return false;
}
private List<TableColumnDefinition> FindHeaderRow(List<Inline> delimiters)
private List<TableColumnDefinition> FindHeaderRow(List<Inline> delimiters)
{
bool isValidRow = false;
List<TableColumnDefinition> aligns = null;

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Helpers;
using Markdig.Parsers;
@@ -8,7 +8,7 @@ using Markdig.Syntax;
namespace Markdig.Extensions.Yaml
{
/// <summary>
/// Block parser for a YAML frontmatter.
/// Block parser for a YAML frontmatter.
/// </summary>
/// <seealso cref="YamlFrontMatterBlock" />
public class YamlFrontMatterParser : BlockParser
@@ -51,7 +51,7 @@ namespace Markdig.Extensions.Yaml
{
return BlockState.None;
}
int count = 0;
var line = processor.Line;
char c = line.CurrentChar;
@@ -64,7 +64,7 @@ namespace Markdig.Extensions.Yaml
}
// If three dashes (optionally followed by whitespace)
// this is a YAML front matter blcok
// this is a YAML front matter block
if (count == 3 && (c == '\0' || c.IsWhitespace()) && line.TrimEnd())
{
bool hasFullYamlFrontMatter = false;

View File

@@ -33,13 +33,12 @@
//SOFTWARE.
using System;
using System.Globalization;
using System.Globalization;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Markdig.Helpers
{
using System.Collections.Generic;
{
/// <summary>
/// Helper class for handling characters.
/// </summary>
@@ -66,25 +65,25 @@ namespace Markdig.Helpers
public static void CheckOpenCloseDelimiter(char pc, char c, bool enableWithinWord, out bool canOpen, out bool canClose)
{
// A left-flanking delimiter run is a delimiter run that is
// (a) not followed by Unicode whitespace, and
// (b) either not followed by a punctuation character, or preceded by Unicode whitespace
// or a punctuation character.
// For purposes of this definition, the beginning and the end of the line count as Unicode whitespace.
pc.CheckUnicodeCategory(out bool prevIsWhiteSpace, out bool prevIsPunctuation);
c.CheckUnicodeCategory(out bool nextIsWhiteSpace, out bool nextIsPunctuation);
var prevIsExcepted = prevIsPunctuation && punctuationExceptions.Contains(pc);
var nextIsExcepted = nextIsPunctuation && punctuationExceptions.Contains(c);
// A left-flanking delimiter run is a delimiter run that is
// (1) not followed by Unicode whitespace, and either
// (2a) not followed by a punctuation character or
// (2b) followed by a punctuation character and preceded by Unicode whitespace or a punctuation character.
// For purposes of this definition, the beginning and the end of the line count as Unicode whitespace.
canOpen = !nextIsWhiteSpace &&
((!nextIsPunctuation || nextIsExcepted) || prevIsWhiteSpace || prevIsPunctuation);
// A right-flanking delimiter run is a delimiter run that is
// (a) not preceded by Unicode whitespace, and
// (b) either not preceded by a punctuation character, or followed by Unicode whitespace
// or a punctuation character.
// A right-flanking delimiter run is a delimiter run that is
// (1) not preceded by Unicode whitespace, and either
// (1a) not preceded by a punctuation character, or
// (2b) preceded by a punctuation character and followed by Unicode whitespace or a punctuation character.
// For purposes of this definition, the beginning and the end of the line count as Unicode whitespace.
canClose = !prevIsWhiteSpace &&
((!prevIsPunctuation || prevIsExcepted) || nextIsWhiteSpace || nextIsPunctuation);
@@ -225,6 +224,30 @@ namespace Markdig.Helpers
|| category == UnicodeCategory.FinalQuotePunctuation
|| category == UnicodeCategory.OtherPunctuation);
}
}
// Same as CheckUnicodeCategory
internal static bool IsSpaceOrPunctuation(this char c)
{
if (c <= 'ÿ')
{
return c == '\0' || c == ' ' || (c >= '\t' && c <= '\r') || c == '\u00a0' || c == '\u0085' ||
(c >= 33 && c <= 47 && c != 38) || (c >= 58 && c <= 64) || (c >= 91 && c <= 96) || (c >= 123 && c <= 126);
}
else
{
var category = CharUnicodeInfo.GetUnicodeCategory(c);
return category == UnicodeCategory.SpaceSeparator
|| category == UnicodeCategory.LineSeparator
|| category == UnicodeCategory.ParagraphSeparator
|| category == UnicodeCategory.ConnectorPunctuation
|| category == UnicodeCategory.DashPunctuation
|| category == UnicodeCategory.OpenPunctuation
|| category == UnicodeCategory.ClosePunctuation
|| category == UnicodeCategory.InitialQuotePunctuation
|| category == UnicodeCategory.FinalQuotePunctuation
|| category == UnicodeCategory.OtherPunctuation;
}
}
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]

View File

@@ -540,12 +540,6 @@ namespace Markdig.Helpers
// Copyright (c) 2014, Kārlis Gaņģis All rights reserved.
// See license for details: https://github.com/Knagis/CommonMark.NET/blob/master/LICENSE.md
/*!re2c
[&] ([#] ([Xx][A-Fa-f0-9]{1,8}|[0-9]{1,8}) |[A-Za-z][A-Za-z0-9]{1,31} ) [;]
{ return (p - start); }
.? { return 0; }
*/
numericEntity = 0;
namedEntityStart = 0;
namedEntityLength = 0;
@@ -565,25 +559,25 @@ namespace Markdig.Helpers
if (c == 'x' || c == 'X')
{
c = slice.NextChar(); // skip #
// expect 1-8 hex digits starting from pos+3
// expect 1-6 hex digits starting from pos+3
while (c != '\0')
{
c = slice.NextChar();
if (c >= '0' && c <= '9')
{
if (++counter == 9) return 0;
if (++counter == 7) return 0;
numericEntity = numericEntity*16 + (c - '0');
continue;
}
else if (c >= 'A' && c <= 'F')
{
if (++counter == 9) return 0;
if (++counter == 7) return 0;
numericEntity = numericEntity*16 + (c - 'A' + 10);
continue;
}
else if (c >= 'a' && c <= 'f')
{
if (++counter == 9) return 0;
if (++counter == 7) return 0;
numericEntity = numericEntity*16 + (c - 'a' + 10);
continue;
}
@@ -596,14 +590,14 @@ namespace Markdig.Helpers
}
else
{
// expect 1-8 digits starting from pos+2
// expect 1-7 digits starting from pos+2
while (c != '\0')
{
c = slice.NextChar();
if (c >= '0' && c <= '9')
{
if (++counter == 9) return 0;
if (++counter == 8) return 0;
numericEntity = numericEntity*10 + (c - '0');
continue;
}

View File

@@ -1,7 +1,7 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using System;
using System.Runtime.CompilerServices;
using Markdig.Syntax;
@@ -302,14 +302,19 @@ namespace Markdig.Helpers
}
// Chars valid for both scheme and email
if (c > ' ' && c < 127 && c != '<')
{
builder.Append(c);
if (c <= 127)
{
if (c > ' ' && c != '>')
{
builder.Append(c);
}
else break;
}
else
{
break;
else if (!c.IsSpaceOrPunctuation())
{
builder.Append(c);
}
else break;
}
}
@@ -319,9 +324,7 @@ namespace Markdig.Helpers
public static bool TryParseInlineLink(StringSlice text, out string link, out string title)
{
SourceSpan linkSpan;
SourceSpan titleSpan;
return TryParseInlineLink(ref text, out link, out title, out linkSpan, out titleSpan);
return TryParseInlineLink(ref text, out link, out title, out _, out _);
}
public static bool TryParseInlineLink(StringSlice text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan)
@@ -521,7 +524,7 @@ namespace Markdig.Helpers
var c = text.CurrentChar;
// a sequence of zero or more characters between an opening < and a closing >
// that contains no spaces, line breaks, or unescaped < or > characters, or
// that contains no line breaks, or unescaped < or > characters, or
if (c == '<')
{
bool hasEscape = false;
@@ -551,20 +554,20 @@ namespace Markdig.Helpers
continue;
}
hasEscape = false;
if (c.IsWhitespace()) // TODO: specs unclear. space is strict or relaxed? (includes tabs?)
{
break;
if (c.IsNewLine())
{
break;
}
hasEscape = false;
buffer.Append(c);
} while (c != '\0');
}
else
{
// a nonempty sequence of characters that does not include ASCII space or control characters,
// a nonempty sequence of characters that does not start with <, does not include ASCII space or control characters,
// and includes parentheses only if (a) they are backslash-escaped or (b) they are part of a
// balanced pair of unescaped parentheses that is not itself inside a balanced pair of unescaped
// parentheses.
@@ -670,6 +673,9 @@ namespace Markdig.Helpers
// A valid domain consists of alphanumeric characters, underscores (_), hyphens (-) and periods (.).
// There must be at least one period, and no underscores may be present in the last two segments of the domain.
// Extended as of https://github.com/lunet-io/markdig/issues/316 to accept non-ascii characters,
// as long as they are not in the space or punctuation categories
int segmentCount = 1;
bool segmentHasCharacters = false;
int lastUnderscoreSegment = -1;
@@ -697,7 +703,7 @@ namespace Markdig.Helpers
{
lastUnderscoreSegment = segmentCount;
}
else if (c != '-')
else if (c != '-' && c.IsSpaceOrPunctuation())
{
// An invalid character has been found
return false;
@@ -752,7 +758,8 @@ namespace Markdig.Helpers
text.TrimStart();
urlSpan.Start = text.Start;
if (!TryParseUrl(ref text, out url) || string.IsNullOrEmpty(url))
bool isAngleBracketsUrl = text.CurrentChar == '<';
if (!TryParseUrl(ref text, out url) || (!isAngleBracketsUrl && string.IsNullOrEmpty(url)))
{
return false;
}

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using System.Collections.Generic;
@@ -11,7 +11,7 @@ namespace Markdig.Helpers
/// </summary>
/// <typeparam name="T">Type of the list item</typeparam>
/// <seealso cref="System.Collections.Generic.List{T}" />
/// <remarks>We use a typed list and don't use extension methods because it would pollute all list implemts and the top level namespace.</remarks>
/// <remarks>We use a typed list and don't use extension methods because it would pollute all list implements and the top level namespace.</remarks>
public class OrderedList<T> : List<T>
{
public OrderedList()
@@ -45,7 +45,13 @@ namespace Markdig.Helpers
return (TElement)this[i];
}
}
return default(TElement);
return default;
}
public bool TryFind<TElement>(out TElement element) where TElement : T
{
element = Find<TElement>();
return element != null;
}
public TElement FindExact<TElement>() where TElement : T
@@ -57,7 +63,7 @@ namespace Markdig.Helpers
return (TElement)this[i];
}
}
return default(TElement);
return default;
}
public void AddIfNotAlready<TElement>() where TElement : class, T, new()
@@ -128,13 +134,29 @@ namespace Markdig.Helpers
/// <typeparam name="TElement">Element type to find in the list</typeparam>
/// <param name="newElement">Object to add/replace the found element with</param>
/// <returns><c>true</c> if a replacement was made; otherwise <c>false</c>.</returns>
public bool ReplaceOrAdd<TElement>(T newElement) where TElement : T
{
if (Replace<TElement>(newElement))
return true;
Add(newElement);
return false;
public bool ReplaceOrAdd<TElement>(T newElement) where TElement : T
{
if (Replace<TElement>(newElement))
return true;
Add(newElement);
return false;
}
/// <summary>
/// Removes the first occurrence of <typeparamref name="TElement"/>
/// </summary>
public bool TryRemove<TElement>() where TElement : T
{
for (int i = 0; i < Count; i++)
{
if (this[i] is TElement)
{
RemoveAt(i);
return true;
}
}
return false;
}
}
}

View File

@@ -1,95 +1,84 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>A fast, powerful, CommonMark compliant, extensible Markdown processor for .NET with 20+ builtin extensions (pipetables, footnotes, definition lists... etc.)</Description>
<Copyright>Alexandre Mutel</Copyright>
<AssemblyTitle>Markdig</AssemblyTitle>
<NeutralLanguage>en-US</NeutralLanguage>
<VersionPrefix>0.16.0</VersionPrefix>
<Authors>Alexandre Mutel</Authors>
<TargetFrameworks>net35;net40;portable40-net40+sl5+win8+wp8+wpa81;netstandard1.1;netstandard2.0;uap10.0;netcoreapp2.1</TargetFrameworks>
<AssemblyName>Markdig</AssemblyName>
<PackageId>Markdig</PackageId>
<PackageId Condition="'$(SignAssembly)' == 'true'">Markdig.Signed</PackageId>
<PackageTags>Markdown CommonMark md html md2html</PackageTags>
<PackageReleaseNotes>https://github.com/lunet-io/markdig/blob/master/changelog.md</PackageReleaseNotes>
<PackageLicenseExpression>BSD-2-Clause</PackageLicenseExpression>
<PackageIconUrl>https://raw.githubusercontent.com/lunet-io/markdig/master/img/markdig.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/lunet-io/markdig</PackageProjectUrl>
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.1' ">1.6.0</NetStandardImplicitPackageVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net35' ">
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'portable40-net40+sl5+win8+wp8+wpa81' ">
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net35' ">
<DefineConstants>$(DefineConstants);SUPPORT_FIXED_STRING;SUPPORT_UNSAFE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net40' ">
<DefineConstants>$(DefineConstants);SUPPORT_FIXED_STRING;SUPPORT_UNSAFE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' ">
<DefineConstants>$(DefineConstants);NETSTANDARD_11;SUPPORT_UNSAFE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>$(DefineConstants);SUPPORT_FIXED_STRING;SUPPORT_UNSAFE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">
<DefineConstants>$(DefineConstants);SUPPORT_FIXED_STRING;SUPPORT_UNSAFE;NETCORE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'uap10.0' ">
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion Condition="'$(TargetPlatformVersion)' == ''">10.0.10240.0</TargetPlatformVersion>
<TargetPlatformMinVersion Condition="'$(TargetPlatformMinVersion)' == ''">10.0.10240.0</TargetPlatformMinVersion>
<DefineConstants>$(DefineConstants);NETSTANDARD_11;SUPPORT_UNSAFE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'portable40-net40+sl5+win8+wp8+wpa81'">
<TargetFrameworkIdentifier>.NETPortable</TargetFrameworkIdentifier>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Profile328</TargetFrameworkProfile>
<LanguageTargets>$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets</LanguageTargets>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<NoWarn>$(NoWarn);CS1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(SignAssembly)' == 'true' ">
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<!-- Special packages and imports for UWP support -->
<ItemGroup>
<PackageReference Include="MSBuild.Sdk.Extras" Version="1.0.9" PrivateAssets="all" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'uap10.0' ">
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform " Version="5.2.2" />
</ItemGroup>
<Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>A fast, powerful, CommonMark compliant, extensible Markdown processor for .NET with 20+ builtin extensions (pipetables, footnotes, definition lists... etc.)</Description>
<Copyright>Alexandre Mutel</Copyright>
<AssemblyTitle>Markdig</AssemblyTitle>
<NeutralLanguage>en-US</NeutralLanguage>
<VersionPrefix>0.17.0</VersionPrefix>
<Authors>Alexandre Mutel</Authors>
<TargetFrameworks>net35;net40;netstandard2.0;uap10.0;netcoreapp2.1</TargetFrameworks>
<AssemblyName>Markdig</AssemblyName>
<PackageId>Markdig</PackageId>
<PackageId Condition="'$(SignAssembly)' == 'true'">Markdig.Signed</PackageId>
<PackageTags>Markdown CommonMark md html md2html</PackageTags>
<PackageReleaseNotes>https://github.com/lunet-io/markdig/blob/master/changelog.md</PackageReleaseNotes>
<PackageLicenseExpression>BSD-2-Clause</PackageLicenseExpression>
<PackageIconUrl>https://raw.githubusercontent.com/lunet-io/markdig/master/img/markdig.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/lunet-io/markdig</PackageProjectUrl>
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.1' ">1.6.0</NetStandardImplicitPackageVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net35' ">
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'portable40-net40+sl5+win8+wp8+wpa81' ">
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net35' ">
<DefineConstants>$(DefineConstants);SUPPORT_FIXED_STRING;SUPPORT_UNSAFE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'net40' ">
<DefineConstants>$(DefineConstants);SUPPORT_FIXED_STRING;SUPPORT_UNSAFE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>$(DefineConstants);SUPPORT_FIXED_STRING;SUPPORT_UNSAFE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">
<DefineConstants>$(DefineConstants);SUPPORT_FIXED_STRING;SUPPORT_UNSAFE;NETCORE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'uap10.0' ">
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion Condition="'$(TargetPlatformVersion)' == ''">10.0.17763.0</TargetPlatformVersion>
<TargetPlatformMinVersion Condition="'$(TargetPlatformMinVersion)' == ''">10.0.10240.0</TargetPlatformMinVersion>
<DefineConstants>$(DefineConstants);NETSTANDARD_11;SUPPORT_UNSAFE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<NoWarn>$(NoWarn);CS1591</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(SignAssembly)' == 'true' ">
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<!-- Special packages and imports for UWP support -->
<ItemGroup>
<PackageReference Include="MSBuild.Sdk.Extras" Version="1.0.9" PrivateAssets="all" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'uap10.0' ">
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform " Version="5.2.2" />
</ItemGroup>
<Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />
</Project>

View File

@@ -99,9 +99,9 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipelineBuilder UseAutoLinks(this MarkdownPipelineBuilder pipeline, string validPreviousCharacters = AutoLinkParser.DefaultValidPreviousCharacters)
public static MarkdownPipelineBuilder UseAutoLinks(this MarkdownPipelineBuilder pipeline, AutoLinkOptions options = null)
{
pipeline.Extensions.ReplaceOrAdd<AutoLinkExtension>(new AutoLinkExtension(validPreviousCharacters));
pipeline.Extensions.ReplaceOrAdd<AutoLinkExtension>(new AutoLinkExtension(options));
return pipeline;
}
@@ -616,5 +616,20 @@ namespace Markdig
pipeline.Use(new ConfigureNewLineExtension(newLine));
return pipeline;
}
/// <summary>
/// Disables parsing of ATX and Setex headings
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipelineBuilder DisableHeadings(this MarkdownPipelineBuilder pipeline)
{
pipeline.BlockParsers.TryRemove<HeadingBlockParser>();
if (pipeline.BlockParsers.TryFind<ParagraphBlockParser>(out var parser))
{
parser.ParseSetexHeadings = false;
}
return pipeline;
}
}
}

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using System.Collections.Generic;
@@ -129,7 +129,7 @@ namespace Markdig.Parsers
public int Column { get; set; }
/// <summary>
/// Gets the position of the current character in the line being processed.
/// Gets the position of the current character in the line being processed.
/// </summary>
public int Start => Line.Start;
@@ -144,12 +144,12 @@ namespace Markdig.Parsers
public bool IsCodeIndent => Indent >= 4;
/// <summary>
/// Gets the column position before the indent occured.
/// Gets the column position before the indent occurred.
/// </summary>
public int ColumnBeforeIndent { get; private set; }
/// <summary>
/// Gets the character position before the indent occured.
/// Gets the character position before the indent occurred.
/// </summary>
public int StartBeforeIndent { get; private set; }
@@ -207,7 +207,7 @@ namespace Markdig.Parsers
public void NextColumn()
{
var c = Line.CurrentChar;
// If we are accross a tab, we should just add 1 column
// If we are across a tab, we should just add 1 column
if (c == '\t' && CharHelper.IsAcrossTab(Column))
{
Column++;
@@ -239,7 +239,7 @@ namespace Markdig.Parsers
}
/// <summary>
/// Parses the indentation from the current position in the line, updating <see cref="StartBeforeIndent"/>,
/// Parses the indentation from the current position in the line, updating <see cref="StartBeforeIndent"/>,
/// <see cref="ColumnBeforeIndent"/>, <see cref="Start"/> and <see cref="Column"/> accordingly
/// taking into account space taken by tabs.
/// </summary>
@@ -542,7 +542,7 @@ namespace Markdig.Parsers
OpenedBlocks[i].IsOpen = true;
}
}
/// <summary>
/// Updates the <see cref="CurrentBlock"/> and <see cref="CurrentContainer"/>.
/// </summary>
@@ -580,7 +580,7 @@ namespace Markdig.Parsers
/// </exception>
private void TryContinueBlocks()
{
// Set all blocks non opened.
// Set all blocks non opened.
// They will be marked as open in the following loop
for (int i = 1; i < OpenedBlocks.Count; i++)
{

View File

@@ -1,231 +1,241 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using Markdig.Helpers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
namespace Markdig.Parsers
{
public abstract class FencedBlockParserBase : BlockParser, IAttributesParseable
{
/// <summary>
/// Delegate used to parse the string on the first line after the fenced code block special characters (usually ` or ~)
/// </summary>
/// <param name="state">The parser processor.</param>
/// <param name="line">The being processed line.</param>
/// <param name="fenced">The fenced code block.</param>
/// <returns><c>true</c> if parsing of the line is successfull; <c>false</c> otherwise</returns>
public delegate bool InfoParserDelegate(BlockProcessor state, ref StringSlice line, IFencedBlock fenced);
/// <summary>
/// Gets or sets the information parser.
/// </summary>
public InfoParserDelegate InfoParser { get; set; }
/// <summary>
/// A delegates that allows to process attached attributes
/// </summary>
public TryParseAttributesDelegate TryParseAttributes { get; set; }
}
/// <summary>
/// Base parser for fenced blocks (opened by 3 or more character delimiters on a first line, and closed by at least the same number of delimiters)
/// </summary>
/// <seealso cref="Markdig.Parsers.BlockParser" />
public abstract class FencedBlockParserBase<T> : FencedBlockParserBase where T : Block, IFencedBlock
{
/// <summary>
/// Initializes a new instance of the <see cref="FencedBlockParserBase{T}"/> class.
/// </summary>
protected FencedBlockParserBase()
{
InfoParser = DefaultInfoParser;
MinimumMatchCount = 3;
MaximumMatchCount = Int32.MaxValue;
}
/// <summary>
/// Gets or sets the language prefix (default is "language-")
/// </summary>
public string InfoPrefix { get; set; }
public int MinimumMatchCount { get; set; }
public int MaximumMatchCount { get; set; }
/// <summary>
/// The default parser for the information after the fenced code block special characters (usually ` or ~)
/// </summary>
/// <param name="state">The parser processor.</param>
/// <param name="line">The line.</param>
/// <param name="fenced">The fenced code block.</param>
/// <returns><c>true</c> if parsing of the line is successfull; <c>false</c> otherwise</returns>
public static bool DefaultInfoParser(BlockProcessor state, ref StringSlice line,
IFencedBlock fenced)
{
string infoString;
string argString = null;
var c = line.CurrentChar;
// An info string cannot contain any backsticks
int firstSpace = -1;
for (int i = line.Start; i <= line.End; i++)
{
c = line.Text[i];
if (c == '`')
{
return false;
}
if (firstSpace < 0 && c.IsSpaceOrTab())
{
firstSpace = i;
}
}
if (firstSpace > 0)
{
infoString = line.Text.Substring(line.Start, firstSpace - line.Start).Trim();
// Skip any spaces after info string
firstSpace++;
while (firstSpace <= line.End)
{
c = line[firstSpace];
if (c.IsSpaceOrTab())
{
firstSpace++;
}
else
{
break;
}
}
argString = line.Text.Substring(firstSpace, line.End - firstSpace + 1).Trim();
}
else
{
infoString = line.ToString().Trim();
}
fenced.Info = HtmlHelper.Unescape(infoString);
fenced.Arguments = HtmlHelper.Unescape(argString);
return true;
}
public override BlockState TryOpen(BlockProcessor processor)
{
// We expect no indentation for a fenced code block.
if (processor.IsCodeIndent)
{
return BlockState.None;
}
var startPosition = processor.Start;
// Match fenced char
int count = 0;
var line = processor.Line;
char c = line.CurrentChar;
var matchChar = c;
while (c != '\0')
{
if (c != matchChar)
{
break;
}
count++;
c = line.NextChar();
}
// A fenced codeblock requires at least 3 opening chars
if (count < MinimumMatchCount || count > MaximumMatchCount)
{
return BlockState.None;
}
// specs spaces: Is space and tabs? or only spaces? Use space and tab for this case
line.TrimStart();
var fenced = CreateFencedBlock(processor);
{
fenced.Column = processor.Column;
fenced.FencedChar = matchChar;
fenced.FencedCharCount = count;
fenced.Span.Start = startPosition;
fenced.Span.End = line.Start;
};
// Try to parse any attached attributes
if (TryParseAttributes != null)
{
TryParseAttributes(processor, ref line, fenced);
}
// If the info parser was not successfull, early exit
if (InfoParser != null && !InfoParser(processor, ref line, fenced))
{
return BlockState.None;
}
// Add the language as an attribute by default
if (!string.IsNullOrEmpty(fenced.Info))
{
if (string.IsNullOrEmpty(InfoPrefix))
{
fenced.GetAttributes().AddClass(fenced.Info);
}
else
{
fenced.GetAttributes().AddClass(InfoPrefix + fenced.Info);
}
}
// Store the number of matched string into the context
processor.NewBlocks.Push(fenced);
// Discard the current line as it is already parsed
return BlockState.ContinueDiscard;
}
protected abstract T CreateFencedBlock(BlockProcessor processor);
public override BlockState TryContinue(BlockProcessor processor, Block block)
{
var fence = (IFencedBlock)block;
var count = fence.FencedCharCount;
var matchChar = fence.FencedChar;
var c = processor.CurrentChar;
// Match if we have a closing fence
var line = processor.Line;
while (c == matchChar)
{
c = line.NextChar();
count--;
}
// If we have a closing fence, close it and discard the current line
// The line must contain only fence opening character followed only by whitespaces.
if (count <=0 && !processor.IsCodeIndent && (c == '\0' || c.IsWhitespace()) && line.TrimEnd())
{
block.UpdateSpanEnd(line.Start - 1);
// Don't keep the last line
return BlockState.BreakDiscard;
}
// Reset the indentation to the column before the indent
processor.GoToColumn(processor.ColumnBeforeIndent);
return BlockState.Continue;
}
}
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using Markdig.Helpers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
namespace Markdig.Parsers
{
public abstract class FencedBlockParserBase : BlockParser, IAttributesParseable
{
/// <summary>
/// Delegate used to parse the string on the first line after the fenced code block special characters (usually ` or ~)
/// </summary>
/// <param name="state">The parser processor.</param>
/// <param name="line">The being processed line.</param>
/// <param name="fenced">The fenced code block.</param>
/// <param name="openingCharacter">The opening character for the fenced code block (usually ` or ~)</param>
/// <returns><c>true</c> if parsing of the line is successfull; <c>false</c> otherwise</returns>
public delegate bool InfoParserDelegate(BlockProcessor state, ref StringSlice line, IFencedBlock fenced, char openingCharacter);
/// <summary>
/// Gets or sets the information parser.
/// </summary>
public InfoParserDelegate InfoParser { get; set; }
/// <summary>
/// A delegates that allows to process attached attributes
/// </summary>
public TryParseAttributesDelegate TryParseAttributes { get; set; }
}
/// <summary>
/// Base parser for fenced blocks (opened by 3 or more character delimiters on a first line, and closed by at least the same number of delimiters)
/// </summary>
/// <seealso cref="Markdig.Parsers.BlockParser" />
public abstract class FencedBlockParserBase<T> : FencedBlockParserBase where T : Block, IFencedBlock
{
/// <summary>
/// Initializes a new instance of the <see cref="FencedBlockParserBase{T}"/> class.
/// </summary>
protected FencedBlockParserBase()
{
InfoParser = DefaultInfoParser;
MinimumMatchCount = 3;
MaximumMatchCount = Int32.MaxValue;
}
/// <summary>
/// Gets or sets the language prefix (default is "language-")
/// </summary>
public string InfoPrefix { get; set; }
public int MinimumMatchCount { get; set; }
public int MaximumMatchCount { get; set; }
/// <summary>
/// The default parser for the information after the fenced code block special characters (usually ` or ~)
/// </summary>
/// <param name="state">The parser processor.</param>
/// <param name="line">The line.</param>
/// <param name="fenced">The fenced code block.</param>
/// <returns><c>true</c> if parsing of the line is successfull; <c>false</c> otherwise</returns>
public static bool DefaultInfoParser(BlockProcessor state, ref StringSlice line, IFencedBlock fenced, char openingCharacter)
{
string infoString;
string argString = null;
// An info string cannot contain any backticks (unless it is a tilde block)
int firstSpace = -1;
if (openingCharacter == '`')
{
for (int i = line.Start; i <= line.End; i++)
{
char c = line.Text[i];
if (c == '`')
{
return false;
}
if (firstSpace < 0 && c.IsSpaceOrTab())
{
firstSpace = i;
}
}
}
else
{
for (int i = line.Start; i <= line.End; i++)
{
if (line.Text[i].IsSpaceOrTab())
{
firstSpace = i;
break;
}
}
}
if (firstSpace > 0)
{
infoString = line.Text.Substring(line.Start, firstSpace - line.Start).Trim();
// Skip any spaces after info string
firstSpace++;
while (firstSpace <= line.End)
{
char c = line[firstSpace];
if (c.IsSpaceOrTab())
{
firstSpace++;
}
else
{
break;
}
}
argString = line.Text.Substring(firstSpace, line.End - firstSpace + 1).Trim();
}
else
{
infoString = line.ToString().Trim();
}
fenced.Info = HtmlHelper.Unescape(infoString);
fenced.Arguments = HtmlHelper.Unescape(argString);
return true;
}
public override BlockState TryOpen(BlockProcessor processor)
{
// We expect no indentation for a fenced code block.
if (processor.IsCodeIndent)
{
return BlockState.None;
}
var startPosition = processor.Start;
// Match fenced char
int count = 0;
var line = processor.Line;
char c = line.CurrentChar;
var matchChar = c;
while (c != '\0')
{
if (c != matchChar)
{
break;
}
count++;
c = line.NextChar();
}
// A fenced codeblock requires at least 3 opening chars
if (count < MinimumMatchCount || count > MaximumMatchCount)
{
return BlockState.None;
}
// specs spaces: Is space and tabs? or only spaces? Use space and tab for this case
line.TrimStart();
var fenced = CreateFencedBlock(processor);
{
fenced.Column = processor.Column;
fenced.FencedChar = matchChar;
fenced.FencedCharCount = count;
fenced.Span.Start = startPosition;
fenced.Span.End = line.Start;
};
// Try to parse any attached attributes
TryParseAttributes?.Invoke(processor, ref line, fenced);
// If the info parser was not successfull, early exit
if (InfoParser != null && !InfoParser(processor, ref line, fenced, matchChar))
{
return BlockState.None;
}
// Add the language as an attribute by default
if (!string.IsNullOrEmpty(fenced.Info))
{
if (string.IsNullOrEmpty(InfoPrefix))
{
fenced.GetAttributes().AddClass(fenced.Info);
}
else
{
fenced.GetAttributes().AddClass(InfoPrefix + fenced.Info);
}
}
// Store the number of matched string into the context
processor.NewBlocks.Push(fenced);
// Discard the current line as it is already parsed
return BlockState.ContinueDiscard;
}
protected abstract T CreateFencedBlock(BlockProcessor processor);
public override BlockState TryContinue(BlockProcessor processor, Block block)
{
var fence = (IFencedBlock)block;
var count = fence.FencedCharCount;
var matchChar = fence.FencedChar;
var c = processor.CurrentChar;
// Match if we have a closing fence
var line = processor.Line;
while (c == matchChar)
{
c = line.NextChar();
count--;
}
// If we have a closing fence, close it and discard the current line
// The line must contain only fence opening character followed only by whitespaces.
if (count <= 0 && !processor.IsCodeIndent && (c == '\0' || c.IsWhitespace()) && line.TrimEnd())
{
block.UpdateSpanEnd(line.Start - 1);
// Don't keep the last line
return BlockState.BreakDiscard;
}
// Reset the indentation to the column before the indent
processor.GoToColumn(processor.ColumnBeforeIndent);
return BlockState.Continue;
}
}
}

View File

@@ -159,7 +159,7 @@ namespace Markdig.Parsers
}
// Cannot start with </script </pre or </style
if ((tagIndex == 50 || tagIndex == 51 || tagIndex == 54))
if ((tagIndex == 49 || tagIndex == 50 || tagIndex == 53))
{
if (c == '/' || hasLeadingClose)
{
@@ -323,30 +323,29 @@ namespace Markdig.Parsers
"main", // 39
"menu", // 40
"menuitem", // 41
"meta", // 42
"nav", // 43
"noframes", // 44
"ol", // 45
"optgroup", // 46
"option", // 47
"p", // 48
"param", // 49
"pre", // 50 <=== special group 1
"script", // 51 <=== special group 1
"section", // 52
"source", // 53
"style", // 54 <=== special group 1
"summary", // 55
"table", // 56
"tbody", // 57
"td", // 58
"tfoot", // 59
"th", // 60
"thead", // 61
"title", // 62
"tr", // 63
"track", // 64
"ul", // 65
"nav", // 42
"noframes", // 43
"ol", // 44
"optgroup", // 45
"option", // 46
"p", // 47
"param", // 48
"pre", // 49 <=== special group 1
"script", // 50 <=== special group 1
"section", // 51
"source", // 52
"style", // 53 <=== special group 1
"summary", // 54
"table", // 55
"tbody", // 56
"td", // 57
"tfoot", // 58
"th", // 59
"thead", // 60
"title", // 61
"tr", // 62
"track", // 63
"ul", // 64
};
}
}

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Helpers;
@@ -8,7 +8,7 @@ using Markdig.Syntax;
namespace Markdig.Parsers
{
/// <summary>
/// A delegates that allows to porcess attached attributes at <see cref="BlockParser"/> time.
/// A delegates that allows to process attached attributes at <see cref="BlockParser"/> time.
/// </summary>
/// <param name="processor">The processor.</param>
/// <param name="slice">The slice to look for attached attributes.</param>

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Syntax.Inlines;
@@ -18,8 +18,8 @@ namespace Markdig.Parsers
/// <param name="lastChild">The last child.</param>
/// <param name="postInlineProcessorIndex">Index of this delimiter processor.</param>
/// <param name="isFinalProcessing"></param>
/// <returns><c>true</c> to continue to the next delimiter processor;
/// <c>false</c> to stop the process (in case a processor is perfoming sub-sequent processor itself)</returns>
/// <returns><c>true</c> to continue to the next delimiter processor;
/// <c>false</c> to stop the process (in case a processor is performing sub-sequent processor itself)</returns>
bool PostProcess(InlineProcessor state, Inline root, Inline lastChild, int postInlineProcessorIndex, bool isFinalProcessing);
}
}

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using System.Collections.Generic;
@@ -52,7 +52,7 @@ namespace Markdig.Parsers
}
/// <summary>
/// Gets the current block being proessed.
/// Gets the current block being processed.
/// </summary>
public LeafBlock Block { get; private set; }
@@ -143,7 +143,7 @@ namespace Markdig.Parsers
/// <returns>The source position</returns>
public int GetSourcePosition(int sliceOffset, out int lineIndex, out int column)
{
column = 0;
column = 0;
lineIndex = sliceOffset >= previousSliceOffset ? previousLineIndexForSliceOffset : 0;
int position = 0;
if (PreciseSourceLocation)
@@ -153,7 +153,7 @@ namespace Markdig.Parsers
var lineOffset = lineOffsets[lineIndex];
if (sliceOffset <= lineOffset.End)
{
// Use the beginning of the line as a previous slice offset
// Use the beginning of the line as a previous slice offset
// (since it is on the same line)
previousSliceOffset = lineOffsets[lineIndex].Start;
var delta = sliceOffset - previousSliceOffset;

View File

@@ -23,7 +23,6 @@ namespace Markdig.Parsers.Inlines
public override bool Match(InlineProcessor processor, ref StringSlice slice)
{
int openSticks = 0;
var match = slice.CurrentChar;
if (slice.PeekCharExtra(-1) == match)
{
@@ -32,82 +31,84 @@ namespace Markdig.Parsers.Inlines
var startPosition = slice.Start;
int openSticks = 0;
int closeSticks = 0;
// Match the opened sticks
var c = slice.CurrentChar;
char c = slice.CurrentChar;
while (c == match)
{
openSticks++;
c = slice.NextChar();
}
bool isMatching = false;
var builder = processor.StringBuilders.Get();
// A backtick string is a string of one or more backtick characters (`) that is neither preceded nor followed by a backtick.
// A code span begins with a backtick string and ends with a backtick string of equal length.
// The contents of the code span are the characters between the two backtick strings, normalized in the following ways:
// 1. line endings are converted to spaces.
// 2. If the resulting string both begins AND ends with a space character, but does not consist entirely
// of space characters, a single space character is removed from the front and back.
// This allows you to include code that begins or ends with backtick characters, which must be separated by
// whitespace from the opening or closing backtick strings.
var builder = processor.StringBuilders.Get();
int closeSticks = 0;
bool allSpace = true;
// A backtick string is a string of one or more backtick characters (`) that is neither preceded nor followed by a backtick.
// A code span begins with a backtick string and ends with a backtick string of equal length.
// The contents of the code span are the characters between the two backtick strings, with leading and trailing spaces and line endings removed, and whitespace collapsed to single spaces.
var pc = ' ';
int newLinesFound = 0;
while (c != '\0')
{
// Transform '\n' into a single space
if (c == '\n')
{
newLinesFound++;
c = ' ';
}
if (c != match && (c != ' ' || pc != ' '))
{
builder.Append(c);
}
else
{
while (c == match)
if (c == match)
{
do
{
closeSticks++;
pc = c;
c = slice.NextChar();
}
}
while (c == match);
if (openSticks == closeSticks)
{
break;
}
}
if (closeSticks > 0)
{
allSpace = false;
builder.Append(match, closeSticks);
closeSticks = 0;
closeSticks = 0;
}
else
{
pc = c;
c = slice.NextChar();
else
{
builder.Append(c);
if (c != ' ')
{
allSpace = false;
}
c = slice.NextChar();
}
}
bool isMatching = false;
if (closeSticks == openSticks)
{
// Remove trailing space
if (builder.Length > 0)
// Remove one space from front and back if the string is not all spaces
if (!allSpace && builder.Length > 2 && builder[0] == ' ' && builder[builder.Length - 1] == ' ')
{
if (builder[builder.Length - 1].IsWhitespace())
{
builder.Length--;
}
builder.Length--;
builder.Remove(0, 1); // More expensive, alternative is to have a double-pass algorithm
}
int line;
int column;
processor.Inline = new CodeInline()
{
Delimiter = match,
Content = builder.ToString(),
Span = new SourceSpan(processor.GetSourcePosition(startPosition, out line, out column), processor.GetSourcePosition(slice.Start - 1)),
Span = new SourceSpan(processor.GetSourcePosition(startPosition, out int line, out int column), processor.GetSourcePosition(slice.Start - 1)),
Line = line,
Column = column
};

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
@@ -41,7 +41,7 @@ namespace Markdig.Parsers.Inlines
public readonly int MinimumCount;
/// <summary>
/// The maximum number of character this emphasis is expected to have (must be >=1 and >= minumunCount)
/// The maximum number of character this emphasis is expected to have (must be >=1 and >= minimumCount)
/// </summary>
public readonly int MaximumCount;

View File

@@ -244,7 +244,8 @@ namespace Markdig.Parsers.Inlines
var isOddMatch = ((closeDelimiter.Type & DelimiterType.Open) != 0 ||
(previousOpenDelimiter.Type & DelimiterType.Close) != 0) &&
previousOpenDelimiter.DelimiterCount != closeDelimiter.DelimiterCount &&
(previousOpenDelimiter.DelimiterCount + closeDelimiter.DelimiterCount) % 3 == 0;
(previousOpenDelimiter.DelimiterCount + closeDelimiter.DelimiterCount) % 3 == 0 &&
(previousOpenDelimiter.DelimiterCount % 3 != 0 || closeDelimiter.DelimiterCount % 3 != 0);
if (previousOpenDelimiter.DelimiterChar == closeDelimiter.DelimiterChar &&
(previousOpenDelimiter.Type & DelimiterType.Open) != 0 &&

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Helpers;
using Markdig.Syntax;
@@ -190,8 +190,8 @@ namespace Markdig.Parsers.Inlines
if (openParent != null)
{
// If we do find one, but its not active,
// we remove the inactive delimiter from the stack,
// If we do find one, but its not active,
// we remove the inactive delimiter from the stack,
// and return a literal text node ].
if (!openParent.IsActive)
{
@@ -206,10 +206,10 @@ namespace Markdig.Parsers.Inlines
return false;
}
// If we find one and its active,
// then we parse ahead to see if we have
// an inline link/image, reference link/image,
// compact reference link/image,
// If we find one and its active,
// then we parse ahead to see if we have
// an inline link/image, reference link/image,
// compact reference link/image,
// or shortcut reference link/image
var parentDelimiter = openParent.Parent;
var savedText = text;
@@ -243,8 +243,8 @@ namespace Markdig.Parsers.Inlines
// Process emphasis delimiters
inlineState.PostProcessInlines(0, link, null, false);
// If we have a link (and not an image),
// we also set all [ delimiters before the opening delimiter to inactive.
// If we have a link (and not an image),
// we also set all [ delimiters before the opening delimiter to inactive.
// (This will prevent us from getting links within links.)
if (!openParent.IsImage)
{
@@ -309,7 +309,7 @@ namespace Markdig.Parsers.Inlines
// We have a nested [ ]
// firstParent.Remove();
// The opening [ will be transformed to a literal followed by all the childrens of the [
// The opening [ will be transformed to a literal followed by all the children of the [
var literal = new LiteralInline()
{

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Helpers;
@@ -38,7 +38,7 @@ namespace Markdig.Parsers.Inlines
int column;
var startPosition = processor.GetSourcePosition(slice.Start, out line, out column);
// Sligthly faster to perform our own search for opening characters
// Slightly faster to perform our own search for opening characters
var nextStart = processor.Parsers.IndexOfOpeningCharacter(text, slice.Start + 1, slice.End);
//var nextStart = str.IndexOfAny(processor.SpecialCharacters, slice.Start + 1, slice.Length - 1);
int length;

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using System.Collections.Generic;
@@ -97,7 +97,7 @@ namespace Markdig.Parsers
}
}
// 5.2 List items
// 5.2 List items
// TODO: Check with specs, it is not clear that list marker or bullet marker must be followed by at least 1 space
// If we have already a ListItemBlock, we are going to try to append to it
@@ -149,23 +149,23 @@ namespace Markdig.Parsers
// Update list-item source end position
listItem.UpdateSpanEnd(state.Line.End);
return BlockState.Continue;
}
list.CountBlankLinesReset = 0;
int columWidth = listItem.ColumnWidth;
if (columWidth < 0)
int columnWidth = listItem.ColumnWidth;
if (columnWidth < 0)
{
columWidth = -columWidth;
columnWidth = -columnWidth;
}
if (state.Indent >= columWidth)
if (state.Indent >= columnWidth)
{
if (state.Indent > columWidth && state.IsCodeIndent)
if (state.Indent > columnWidth && state.IsCodeIndent)
{
state.GoToColumn(state.ColumnBeforeIndent + columWidth);
state.GoToColumn(state.ColumnBeforeIndent + columnWidth);
}
// Update list-item source end position
@@ -179,15 +179,15 @@ namespace Markdig.Parsers
private BlockState TryParseListItem(BlockProcessor state, Block block)
{
// If we have a code indent and we are not in a ListItem, early exit
if (!(block is ListItemBlock) && state.IsCodeIndent)
var currentListItem = block as ListItemBlock;
var currentParent = block as ListBlock ?? (ListBlock)currentListItem?.Parent;
// We can early exit if we have a code indent and we are either (1) not in a ListItem, (2) preceded by a blank line, (3) in an unordered list
if (state.IsCodeIndent && (currentListItem is null || currentListItem.LastChild is BlankLineBlock || !currentParent.IsOrdered))
{
return BlockState.None;
}
var currentListItem = block as ListItemBlock;
var currentParent = block as ListBlock ?? (ListBlock)currentListItem?.Parent;
var initColumnBeforeIndent = state.ColumnBeforeIndent;
var initColumn = state.Column;
var sourcePosition = state.Start;
@@ -210,7 +210,7 @@ namespace Markdig.Parsers
return BlockState.None;
}
// Gets the current character after a succesfull parsing of the list information
// Gets the current character after a successful parsing of the list information
c = state.CurrentChar;
// Item starting with a blank line

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
namespace Markdig.Parsers
@@ -20,7 +20,7 @@ namespace Markdig.Parsers
/// <param name="state">The block processor</param>
/// <param name="pendingBulletType">The type of the current bullet type</param>
/// <param name="result">The result of parsing</param>
/// <returns><c>true</c> if parsing was sucessfull; <c>false</c> otherwise</returns>
/// <returns><c>true</c> if parsing was successful; <c>false</c> otherwise</returns>
public abstract bool TryParse(BlockProcessor state, char pendingBulletType, out ListInfo result);
}
}

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Helpers;
using Markdig.Syntax;
@@ -12,6 +12,8 @@ namespace Markdig.Parsers
/// <seealso cref="BlockParser" />
public class ParagraphBlockParser : BlockParser
{
public bool ParseSetexHeadings { get; set; } = true;
public override BlockState TryOpen(BlockProcessor processor)
{
if (processor.IsBlankLine)
@@ -35,7 +37,7 @@ namespace Markdig.Parsers
return BlockState.BreakDiscard;
}
if (!processor.IsCodeIndent && !(block.Parent is QuoteBlock))
if (ParseSetexHeadings && !processor.IsCodeIndent && !(block.Parent is QuoteBlock))
{
return TryParseSetexHeading(processor, block);
}
@@ -46,8 +48,7 @@ namespace Markdig.Parsers
public override bool Close(BlockProcessor processor, Block block)
{
var paragraph = block as ParagraphBlock;
if (paragraph != null)
if (block is ParagraphBlock paragraph)
{
TryMatchLinkReferenceDefinition(ref paragraph.Lines, processor);
@@ -115,13 +116,13 @@ namespace Markdig.Parsers
if (headingChar != 0)
{
// We dicard the paragraph that will be transformed to a heading
state.Discard(paragraph);
// If we matched a LinkReferenceDefinition before matching the heading, and the remaining
// If we matched a LinkReferenceDefinition before matching the heading, and the remaining
// lines are empty, we can early exit and remove the paragraph
if (!(TryMatchLinkReferenceDefinition(ref paragraph.Lines, state) && paragraph.Lines.Count == 0))
{
// We discard the paragraph that will be transformed to a heading
state.Discard(paragraph);
var level = headingChar == '=' ? 1 : 2;
var heading = new HeadingBlock(this)
@@ -135,8 +136,9 @@ namespace Markdig.Parsers
// Remove the paragraph as a pending block
state.NewBlocks.Push(heading);
return BlockState.BreakDiscard;
}
return BlockState.BreakDiscard;
}
block.UpdateSpanEnd(state.Line.End);
@@ -153,14 +155,14 @@ namespace Markdig.Parsers
// If we have found a LinkReferenceDefinition, we can discard the previous paragraph
var iterator = lines.ToCharIterator();
if (LinkReferenceDefinition.TryParse(ref iterator, out LinkReferenceDefinition linkReferenceDefinition))
{
{
state.Document.SetLinkReferenceDefinition(linkReferenceDefinition.Label, linkReferenceDefinition);
atLeastOneFound = true;
// Correct the locations of each field
linkReferenceDefinition.Line = lines.Lines[0].Line;
int startPosition = lines.Lines[0].Slice.Start;
linkReferenceDefinition.Line = lines.Lines[0].Line;
int startPosition = lines.Lines[0].Slice.Start;
linkReferenceDefinition.Span = linkReferenceDefinition.Span .MoveForward(startPosition);
linkReferenceDefinition.LabelSpan = linkReferenceDefinition.LabelSpan .MoveForward(startPosition);
linkReferenceDefinition.UrlSpan = linkReferenceDefinition.UrlSpan .MoveForward(startPosition);

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;
@@ -79,26 +79,20 @@ namespace Markdig.Renderers.Html
if (name == null) throw new ArgumentNullException(nameof(name));
if (Properties == null)
{
Properties = new List<KeyValuePair<string, string>>(4) {new KeyValuePair<string, string>(name, value == null ? null : Convert.ToString(value, CultureInfo.InvariantCulture))};
Properties = new List<KeyValuePair<string, string>>(4);
}
else
{
// Check that there is not already a property with the same key
bool copyProp = true;
for (int i = 0; i < Properties.Count; i++)
{
var againstProp = Properties[i];
if (againstProp.Key == name)
if (Properties[i].Key == name)
{
copyProp = false;
break;
return;
}
}
if (copyProp)
{
Properties.Add(new KeyValuePair<string, string>(name, value == null ? null : Convert.ToString(value, CultureInfo.InvariantCulture)));
}
}
Properties.Add(new KeyValuePair<string, string>(name, value == null ? null : Convert.ToString(value, CultureInfo.InvariantCulture)));
}
/// <summary>

View File

@@ -13,7 +13,14 @@ namespace Markdig.Renderers.Html.Inlines
{
protected override void Write(HtmlRenderer renderer, HtmlEntityInline obj)
{
renderer.WriteEscape(obj.Transcoded);
if (renderer.EnableHtmlForInline)
{
renderer.WriteEscape(obj.Transcoded);
}
else
{
renderer.Write(obj.Transcoded);
}
}
}
}
}

View File

@@ -13,7 +13,14 @@ namespace Markdig.Renderers.Html.Inlines
{
protected override void Write(HtmlRenderer renderer, LiteralInline obj)
{
renderer.WriteEscape(ref obj.Content);
if (renderer.EnableHtmlForInline)
{
renderer.WriteEscape(obj.Content);
}
else
{
renderer.Write(obj.Content);
}
}
}
}
}

View File

@@ -2,10 +2,11 @@
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using System;
using System.Globalization;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text;
using Markdig.Helpers;
using Markdig.Renderers.Html;
using Markdig.Renderers.Html.Inlines;
@@ -83,7 +84,7 @@ namespace Markdig.Renderers
/// <summary>
/// Allows links to be rewritten
/// </summary>
public Func<string,string> LinkRewriter { get; set; }
public Func<string, string> LinkRewriter { get; set; }
/// <summary>
/// Writes the content escaped for HTML.
@@ -190,7 +191,9 @@ namespace Markdig.Renderers
Write(content, previousOffset, end - previousOffset);
return this;
}
}
private static readonly IdnMapping IdnMapping = new IdnMapping();
/// <summary>
/// Writes the URL escaped for HTML.
@@ -212,10 +215,57 @@ namespace Markdig.Renderers
content = LinkRewriter(content);
}
int previousPosition = 0;
int length = content.Length;
int previousPosition = 0;
for (var i = 0; i < length; i++)
// ab://c.d = 8 chars
int schemeOffset = content.Length < 8 ? -1 : content.IndexOf("://", 2, StringComparison.Ordinal);
if (schemeOffset != -1) // This is an absolute URL
{
schemeOffset += 3; // skip ://
Write(content, 0, schemeOffset);
bool idnaEncodeDomain = false;
int endOfDomain = schemeOffset;
for (; endOfDomain < content.Length; endOfDomain++)
{
char c = content[endOfDomain];
if (c == '/' || c == '?' || c == '#' || c == ':') // End of domain part
{
break;
}
if (c > 127)
{
idnaEncodeDomain = true;
}
}
if (idnaEncodeDomain)
{
string domainName = IdnMapping.GetAscii(content, schemeOffset, endOfDomain - schemeOffset);
// Escape the characters (see Commonmark example 327 and think of it with a non-ascii symbol)
previousPosition = 0;
for (int i = 0; i < domainName.Length; i++)
{
var escape = HtmlHelper.EscapeUrlCharacter(domainName[i]);
if (escape != null)
{
Write(domainName, previousPosition, i - previousPosition);
previousPosition = i + 1;
Write(escape);
}
}
Write(domainName, previousPosition, domainName.Length - previousPosition);
previousPosition = endOfDomain;
}
else
{
previousPosition = schemeOffset; // Don't write anything as we might need to escape it
}
}
for (var i = previousPosition; i < content.Length; i++)
{
var c = content[i];
@@ -242,7 +292,7 @@ namespace Markdig.Renderers
else
{
byte[] bytes;
if (c >= '\ud800' && c <= '\udfff' && previousPosition < length)
if (c >= '\ud800' && c <= '\udfff' && previousPosition < content.Length)
{
bytes = Encoding.UTF8.GetBytes(new[] { c, content[previousPosition] });
// Skip next char as it is decoded above
@@ -261,7 +311,7 @@ namespace Markdig.Renderers
}
}
Write(content, previousPosition, length - previousPosition);
Write(content, previousPosition, content.Length - previousPosition);
return this;
}
@@ -359,6 +409,6 @@ namespace Markdig.Renderers
}
}
return this;
}
}
}
}
}

View File

@@ -13,11 +13,41 @@ namespace Markdig.Renderers.Normalize.Inlines
{
protected override void Write(NormalizeRenderer renderer, CodeInline obj)
{
var delimiter = obj.Content.Contains(obj.Delimiter + "") ? new string(obj.Delimiter, 2) : obj.Delimiter + "";
var delimiterCount = 0;
for (var i = 0; i < obj.Content.Length; i++)
{
var index = obj.Content.IndexOf(obj.Delimiter, i);
if (index == -1) break;
renderer.Write(delimiter);
renderer.Write(obj.Content);
renderer.Write(delimiter);
var count = 1;
for (i = index + 1; i < obj.Content.Length; i++)
{
if (obj.Content[i] == obj.Delimiter) count++;
else break;
}
if (delimiterCount < count)
delimiterCount = count;
}
var delimiterRun = new string(obj.Delimiter, delimiterCount + 1);
renderer.Write(delimiterRun);
if (obj.Content.Length != 0)
{
if (obj.Content[0] == obj.Delimiter)
{
renderer.Write(' ');
}
renderer.Write(obj.Content);
if (obj.Content[obj.Content.Length - 1] == obj.Delimiter)
{
renderer.Write(' ');
}
}
else
{
renderer.Write(' ');
}
renderer.Write(delimiterRun);
}
}
}

View File

@@ -1,15 +1,15 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Parsers;
namespace Markdig.Syntax
{
/// <summary>
/// Repressents an indented code block.
/// Represents an indented code block.
/// </summary>
/// <remarks>
/// Related to CommonMark spec: 4.4 Indented code blocks
/// Related to CommonMark spec: 4.4 Indented code blocks
/// </remarks>
public class CodeBlock : LeafBlock
{

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.
@@ -98,6 +98,8 @@ namespace Markdig.Syntax
children[i].Parent = null;
children[i] = null;
}
Count = 0;
}
public bool Contains(Block item)

View File

@@ -1,12 +1,12 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Parsers;
namespace Markdig.Syntax
{
/// <summary>
/// Repressents a fenced code block.
/// Represents a fenced code block.
/// </summary>
/// <remarks>
/// Related to CommonMark spec: 4.5 Fenced code blocks
@@ -27,7 +27,7 @@ namespace Markdig.Syntax
}
/// <summary>
/// Gets or sets the language parsed after the first line of
/// Gets or sets the language parsed after the first line of
/// the fenced code block. May be null.
/// </summary>
public string Info { get; set; }
@@ -49,7 +49,7 @@ namespace Markdig.Syntax
public char FencedChar { get; set; }
/// <summary>
/// Gets or sets the indent count when the fenced code block was indented
/// Gets or sets the indent count when the fenced code block was indented
/// and we need to remove up to indent count chars spaces from the begining of a line.
/// </summary>
internal int IndentCount { get; set; }

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System.Diagnostics;
using Markdig.Parsers;
@@ -7,7 +7,7 @@ using Markdig.Parsers;
namespace Markdig.Syntax
{
/// <summary>
/// Repressents a heading.
/// Represents a heading.
/// </summary>
[DebuggerDisplay("{GetType().Name} Line: {Line}, {Lines} Level: {Level}")]
public class HeadingBlock : LeafBlock

View File

@@ -1,5 +1,5 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using System.Collections;
@@ -94,36 +94,36 @@ namespace Markdig.Syntax.Inlines
/// <returns>An enumeration of T</returns>
public IEnumerable<T> FindDescendants<T>() where T : Inline
{
// Fast-path an empty container to avoid allocating a Stack
// Fast-path an empty container to avoid allocating a Stack
if (LastChild == null) yield break;
Stack<Inline> stack = new Stack<Inline>();
var child = LastChild;
while (child != null)
{
stack.Push(child);
child = child.PreviousSibling;
while (child != null)
{
stack.Push(child);
child = child.PreviousSibling;
}
while (stack.Count > 0)
{
child = stack.Pop();
if (child is T childT)
{
yield return childT;
}
if (child is ContainerInline containerInline)
{
child = containerInline.LastChild;
while (child != null)
{
stack.Push(child);
child = child.PreviousSibling;
}
}
while (stack.Count > 0)
{
child = stack.Pop();
if (child is T childT)
{
yield return childT;
}
if (child is ContainerInline containerInline)
{
child = containerInline.LastChild;
while (child != null)
{
stack.Push(child);
child = child.PreviousSibling;
}
}
}
}
@@ -135,14 +135,14 @@ namespace Markdig.Syntax.Inlines
{
if (parent == null) throw new ArgumentNullException(nameof(parent));
var child = FirstChild;
var nextSibliing = parent;
var nextSibling = parent;
while (child != null)
{
var next = child.NextSibling;
// TODO: optimize this
child.Remove();
nextSibliing.InsertAfter(child);
nextSibliing = child;
nextSibling.InsertAfter(child);
nextSibling = child;
child = next;
}
}

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