Compare commits

...

67 Commits

Author SHA1 Message Date
Alexandre Mutel
6dcef438c4 Bump to 0.16.0 2019-02-25 20:20:31 +01:00
Alexandre Mutel
40206c4d76 Merge pull request #311 from RickStrahl/MathExtension_Bracketing
Add Bracketing around Math Expressions for improved MathLib Rendering
2019-02-25 20:10:23 +01:00
Alexandre Mutel
24ecfc93f3 Merge pull request #310 from MihaZupan/master
Improve CompactPrefixTree's memory footprint
2019-02-25 20:08:37 +01:00
Rick Strahl
8eb52c50ec Update Math Specs and ensure tests pass. 2019-02-22 10:29:32 -10:00
Rick Strahl
9965597256 Fix tests to match bracketing. 2019-02-21 17:33:17 -10:00
Rick Strahl
987580e71e Add inline block and block display bracketing tags around rendered mathematics content so it can render 2019-02-21 17:02:39 -10:00
Miha Zupan
bba3ef04ee Improve CompactPrefixTree's memory footprint 2019-02-21 17:29:09 +01:00
Alexandre Mutel
d8a594a151 Merge pull request #309 from yishengjin1413/master
remove lazy initialize
2019-02-20 10:14:36 +01:00
Terry Jin
60c64b67c7 remove lazy initialize 2019-02-20 16:48:26 +08:00
Alexandre Mutel
7f0b39a44a Merge pull request #308 from yishengjin1413/master
fix emoji performance issue when multiple pipeline needed
2019-02-20 09:15:31 +01:00
Terry Jin
57784cedc6 fix emoji performance issue when muti pipeline needed 2019-02-20 15:24:45 +08:00
Alexandre Mutel
80b918e6f5 Merge pull request #301 from MihaZupan/improved-emphasis-parser
Improved emphasis parser
2019-02-09 14:30:02 +01:00
Miha Zupan
64883152c9 Remove HasFlag helper 2019-02-09 12:15:29 +01:00
Miha Zupan
20bd561062 Avoid a possible Null Reference Ex. 2019-02-09 00:35:19 +01:00
Miha Zupan
035ce17386 Add generic TEnum constraint to HasFlag 2019-02-08 19:50:40 +01:00
Alexandre Mutel
bc4dbd1b35 Merge pull request #300 from picrap/tr-writeattributes
HtmlTableRenderer: writing <tr> attributes
2019-02-08 17:44:50 +01:00
Alexandre Mutel
1d8266b86d Merge pull request #305 from MihaZupan/emoji-and-abbreviations-parser
Emoji and abbreviations parser
2019-02-08 17:37:59 +01:00
Miha Zupan
b5293b907f Comment-out TextMatcher test in Benchmarks 2019-02-06 17:27:00 +01:00
Miha Zupan
a11676ed7e Add test case for #296 2019-02-06 17:17:30 +01:00
Miha Zupan
18e9486c95 Remove TextMatchHelper 2019-02-06 16:32:55 +01:00
Miha Zupan
ef452c292c Fix Abbreviations parser's one-char handling 2019-02-06 16:32:37 +01:00
Miha Zupan
325495a367 Improve EmojiParser memory performance 2019-02-06 16:31:57 +01:00
Miha Zupan
d854b0b941 Port CompactPrefixTree to Markdig 2019-02-06 16:31:23 +01:00
Miha Zupan
ca38da576e Cross target NetCore 2.1 2019-02-06 15:46:12 +01:00
Miha Zupan
b15b05015e Allow single-char abbreviations 2019-02-06 15:44:12 +01:00
Miha Zupan
5266ae965b Add extended emphasis tests 2019-01-29 22:10:06 +01:00
Miha Zupan
42b201d8c2 Update internals to renewed Emphasis parser 2019-01-29 22:09:39 +01:00
Miha Zupan
d232e847dd Allow different min/max emphasis delimiter counts 2019-01-29 22:09:05 +01:00
Miha Zupan
1b5d9d46fc Emit correct warning message in tests 2019-01-29 22:07:17 +01:00
Miha Zupan
6b1b5165ca Add HasFlag enum support 2019-01-29 22:06:28 +01:00
Pascal Craponne
7719ef503f HtmlTableRenderer: writing <tr> attributes 2019-01-29 21:39:28 +01:00
Alexandre Mutel
f01b5281fd Merge pull request #295 from TravisEz13/Add_license_to_nupkg
Add the license expression to the `csproj`
2019-01-27 21:32:15 +01:00
Alexandre Mutel
3e4e6d6627 Merge pull request #294 from MihaZupan/alternative-specs-gen
Alternative specs generation
2019-01-27 21:31:11 +01:00
Miha Zupan
16ffc052a3 Update error message for modified spec files 2019-01-27 16:01:25 +01:00
neolithos
22853d9782 Add Neo.Markdig.Xaml (#298)
* Add: Neo.Markdig.Xaml to readme.md
2019-01-27 14:59:58 +01:00
Miha Zupan
8164c1fa8d Test that specs are up to date 2019-01-27 13:13:26 +01:00
Travis Plunk
8a42f74a6c removed URl to fix this error
```
  C:\Program Files\dotnet\sdk\2.2.100\Sdks\NuGet.Build.Tasks.Pack\buildCrossTargeting\NuGet.Build.Tasks.Pack.targets(202,5): error NU5035: The PackageLicenseUrl is being deprecated and cannot be used in conjunction with the PackageLicenseFile or PackageLicenseExpression. [C:\projects\markdig\src\Markdig\Markdig.csproj]
```
2019-01-22 15:26:31 -08:00
Travis Plunk
19a5a07763 Add the license expression to the csproj 2019-01-22 15:14:52 -08:00
Miha Zupan
dd68351235 Fix test path resolution 2019-01-21 16:38:58 +01:00
Miha Zupan
c346e55a46 Remove Specs.tt 2019-01-21 16:11:01 +01:00
Miha Zupan
ffdfd427ce Generate Specs 2019-01-21 16:10:34 +01:00
Miha Zupan
edbf832e0a Fail CI if specs are not synced 2019-01-21 16:00:18 +01:00
Miha Zupan
7887cf7348 Generate Specs with C# 2019-01-21 16:00:05 +01:00
Alexandre Mutel
e6432038b5 Merge pull request #288 from MihaZupan/rewrite-descendants-search
Rewrite Descendants iteratively
2019-01-19 19:27:26 +01:00
Alexandre Mutel
f8df3c80e1 Merge pull request #285 from patriksvensson/feature/GH-284
Adds markdown parser context
2019-01-19 18:04:26 +01:00
Alexandre Mutel
664c824f8b Use Dictionary and removes setter 2019-01-19 18:03:42 +01:00
Miha Zupan
95cbf0e6d2 Cache Schema markdown in Tests 2019-01-19 11:35:01 +01:00
Miha Zupan
b2a542b43e Simplify push condition 2019-01-18 20:23:17 +01:00
Miha Zupan
f1b736f918 Add Test for Descendants order 2019-01-18 19:19:42 +01:00
Miha Zupan
282da043b7 Rewrite Descendants(ContainerInline) iteratively 2019-01-18 18:52:48 +01:00
Miha Zupan
4519d3820a Rewrite Descendants(ContainerBlock) iteratively 2019-01-18 18:52:30 +01:00
Miha Zupan
37325f1bdc Use existing Descantants<T>(ContainerInline) implementation 2019-01-18 18:27:58 +01:00
Miha Zupan
1bbe20dca1 Rewrite Descendants(MarkdownObject) iteratively 2019-01-18 18:22:28 +01:00
Alexandre Mutel
b237d70298 Merge pull request #287 from MihaZupan/emphasis-patch
Emphasis patch
2019-01-15 16:31:42 +01:00
Miha Zupan
632f400a74 Add tests for EmphasisExtraOptions 2019-01-15 16:24:19 +01:00
Miha Zupan
a302db679c Respect EmphasisExtraOptions 2019-01-15 16:23:06 +01:00
Patrik Svensson
653972847c Adds markdown parser context
Closes #284
2019-01-12 22:59:41 +01:00
Alexandre Mutel
832f86cf5f Bump to 0.15.7 2019-01-11 15:52:56 +01:00
Alexandre Mutel
103434eb9b Merge pull request #283 from OsmondJiang/configurable_leading_#_count
make the max leading # count in heading block parser configurable
2019-01-07 10:50:15 +01:00
OsmondJiang
f9dd201563 move heading block extension outside 2019-01-07 17:18:52 +08:00
OsmondJiang
9b9243a61f add heading block parser to able to config max leading count of # 2019-01-07 17:03:02 +08:00
Alexandre Mutel
91dbfce18e Merge pull request #281 from FranklinWhale/bool-attr
Render XML well-formed boolean attribute
2019-01-04 13:47:45 +01:00
Franklin Tse
57692a557b controls attribute is also changed 2019-01-04 00:48:10 +08:00
Franklin Tse
02bd8149ac Use empty string instead of attribute name 2019-01-04 00:23:19 +08:00
Franklin Tse
0920001f45 Remove the unnecessary EOL at the end 2019-01-03 23:51:04 +08:00
Franklin Tse
cde939f79c Update spec and test cases 2019-01-03 23:49:38 +08:00
Franklin Tse
f932b3c87a Use attribute name as value if value is null 2019-01-03 23:43:54 +08:00
77 changed files with 32298 additions and 22991 deletions

View File

@@ -28,6 +28,8 @@ install:
build:
project: src/Markdig.sln
verbosity: minimal
after_build:
- dotnet run --project SpecFileGen/SpecFileGen.csproj -c Release
before_package:
- cmd: >-
msbuild /t:pack /p:VersionSuffix="%MARKDIG_VERSION_SUFFIX%" /p:Configuration=Release Markdig/Markdig.csproj

View File

@@ -1,5 +1,17 @@
# Changelog
## 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))
- Improve emphasis parser to allow to match more than 2 characters ([(PR #301)](https://github.com/lunet-io/markdig/pull/301))
- Output attached attributes to a `<tr>` from a table row ([(PR #300)](https://github.com/lunet-io/markdig/pull/300))
- Improve MarkdownObject.Descendants() search ([(PR #288)](https://github.com/lunet-io/markdig/pull/288))
- Allow to pass a `MarkdownParserContext` ([(PR #285)](https://github.com/lunet-io/markdig/pull/285))
## 0.15.7 (11 Jan 2019)
- Add configurable leading count for ATX headers ([(PR #282)](https://github.com/lunet-io/markdig/pull/282))
- Render XML well-formed boolean attribute ([(PR #281)](https://github.com/lunet-io/markdig/pull/281))
## 0.15.6 (28 Dec 2018)
- Fix potential hang when parsing LinkReferenceDefinition #278
- Fix parsing of an invalid html entity (#277)

View File

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

View File

@@ -55,6 +55,7 @@ You can **try Markdig online** and compare it to other implementations on [babel
### Third Party Extensions
- [**WPF/XAML Markdown Renderer**: `markdig.wpf`](https://github.com/Kryptos-FR/markdig.wpf)
- [**WPF/XAML Markdown Renderer**: `Neo.Markdig.Xaml`](https://github.com/neolithos/NeoMarkdigXaml)
- [**Syntax highlighting**: `Markdig.SyntaxHighlighting`](https://github.com/RichardSlater/Markdig.SyntaxHighlighting)
- [**Embedded C# scripting**: `Markdig.Extensions.ScriptCs`](https://github.com/macaba/Markdig.Extensions.ScriptCs)

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.
@@ -52,7 +52,7 @@ namespace Testamina.Markdig.Benchmarks
}
}
/*
public class TestMatchPerf
{
private readonly TextMatchHelper matcher;
@@ -82,4 +82,5 @@ namespace Testamina.Markdig.Benchmarks
}
}
}
*/
}

View File

@@ -1,136 +1,154 @@
<?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')" />
<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 />
</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" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Specs\Specs.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Specs.tt</DependentUpon>
</Compile>
<Compile Include="Specs\TestEmphasisPlus.cs" />
<Compile Include="TestConfigureNewLine.cs" />
<Compile Include="TestHtmlAttributes.cs" />
<Compile Include="TestHtmlHelper.cs" />
<Compile Include="TestLineReader.cs" />
<Compile Include="TestLinkHelper.cs" />
<Compile Include="TestNormalize.cs" />
<Compile Include="TestOrderedList.cs" />
<Compile Include="TestPlainText.cs" />
<Compile Include="TestPragmaLines.cs" />
<Compile Include="TestLinkRewriter.cs" />
<Compile Include="TestRelativeUrlReplacement.cs" />
<Compile Include="TestSourcePosition.cs" />
<Compile Include="TestStringSliceList.cs" />
<Compile Include="TestPlayParser.cs" />
<Compile Include="TextAssert.cs" />
<Compile Include="TestParser.cs" />
</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\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>
<Content Include="Specs\Specs.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Specs.cs</LastGenOutput>
</Content>
</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" />
<?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')" />
<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 />
</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" />
</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" />
</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,236 @@
// Generated: 6. 02. 2019 16:15:54
// --------------------------------
// Abbreviations
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.Abbreviations
{
[TestFixture]
public class TestExtensionsAbbreviation
{
// # Extensions
//
// This section describes the different extensions supported:
//
// ## Abbreviation
//
// Abbreviation can be declared by using the `*[Abbreviation Label]: Abbreviation description`
//
// Abbreviation definition will be removed from the original document. Any Abbreviation label found in literals will be replaced by the abbreviation description:
[Test]
public void ExtensionsAbbreviation_Example001()
{
// Example 1
// Section: Extensions / Abbreviation
//
// The following Markdown:
// *[HTML]: Hypertext Markup Language
//
// Later in a text we are using HTML and it becomes an abbr tag HTML
//
// Should be rendered as:
// <p>Later in a text we are using <abbr title="Hypertext Markup Language">HTML</abbr> and it becomes an abbr tag <abbr title="Hypertext Markup Language">HTML</abbr></p>
Console.WriteLine("Example 1\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[HTML]: Hypertext Markup Language\n\nLater in a text we are using HTML and it becomes an abbr tag HTML", "<p>Later in a text we are using <abbr title=\"Hypertext Markup Language\">HTML</abbr> and it becomes an abbr tag <abbr title=\"Hypertext Markup Language\">HTML</abbr></p>", "abbreviations|advanced");
}
// An abbreviation definition can be indented at most 3 spaces
[Test]
public void ExtensionsAbbreviation_Example002()
{
// Example 2
// Section: Extensions / Abbreviation
//
// The following Markdown:
// *[HTML]: Hypertext Markup Language
// *[This]: is not an abbreviation
//
// Should be rendered as:
// <pre><code>*[This]: is not an abbreviation
// </code></pre>
Console.WriteLine("Example 2\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[HTML]: Hypertext Markup Language\n *[This]: is not an abbreviation", "<pre><code>*[This]: is not an abbreviation\n</code></pre>", "abbreviations|advanced");
}
// An abbreviation may contain spaces:
[Test]
public void ExtensionsAbbreviation_Example003()
{
// Example 3
// Section: Extensions / Abbreviation
//
// The following Markdown:
// *[SUPER HTML]: Super Hypertext Markup Language
//
// This is a SUPER HTML document
//
// Should be rendered as:
// <p>This is a <abbr title="Super Hypertext Markup Language">SUPER HTML</abbr> document</p>
Console.WriteLine("Example 3\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[SUPER HTML]: Super Hypertext Markup Language\n\nThis is a SUPER HTML document ", "<p>This is a <abbr title=\"Super Hypertext Markup Language\">SUPER HTML</abbr> document</p>", "abbreviations|advanced");
}
// Abbreviation may contain any unicode characters:
[Test]
public void ExtensionsAbbreviation_Example004()
{
// Example 4
// Section: Extensions / Abbreviation
//
// The following Markdown:
// *[😃 HTML]: Hypertext Markup Language
//
// This is a 😃 HTML document
//
// Should be rendered as:
// <p>This is a <abbr title="Hypertext Markup Language">😃 HTML</abbr> document</p>
Console.WriteLine("Example 4\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[😃 HTML]: Hypertext Markup Language\n\nThis is a 😃 HTML document ", "<p>This is a <abbr title=\"Hypertext Markup Language\">😃 HTML</abbr> document</p>", "abbreviations|advanced");
}
// Abbreviations may be similar:
[Test]
public void ExtensionsAbbreviation_Example005()
{
// Example 5
// Section: Extensions / Abbreviation
//
// The following Markdown:
// *[1A]: First
// *[1A1]: Second
// *[1A2]: Third
//
// We can abbreviate 1A, 1A1 and 1A2!
//
// Should be rendered as:
// <p>We can abbreviate <abbr title="First">1A</abbr>, <abbr title="Second">1A1</abbr> and <abbr title="Third">1A2</abbr>!</p>
Console.WriteLine("Example 5\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[1A]: First\n*[1A1]: Second\n*[1A2]: Third\n\nWe can abbreviate 1A, 1A1 and 1A2!", "<p>We can abbreviate <abbr title=\"First\">1A</abbr>, <abbr title=\"Second\">1A1</abbr> and <abbr title=\"Third\">1A2</abbr>!</p>", "abbreviations|advanced");
}
// Abbreviations should match whole word only:
[Test]
public void ExtensionsAbbreviation_Example006()
{
// Example 6
// Section: Extensions / Abbreviation
//
// The following Markdown:
// *[1A]: First
//
// We should not abbreviate 1.1A or 11A!
//
// Should be rendered as:
// <p>We should not abbreviate 1.1A or 11A!</p>
Console.WriteLine("Example 6\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[1A]: First\n\nWe should not abbreviate 1.1A or 11A!", "<p>We should not abbreviate 1.1A or 11A!</p>", "abbreviations|advanced");
}
// Abbreviations should match whole word only, even if the word is the entire content:
[Test]
public void ExtensionsAbbreviation_Example007()
{
// Example 7
// Section: Extensions / Abbreviation
//
// The following Markdown:
// *[1A]: First
//
// 1.1A
//
// Should be rendered as:
// <p>1.1A</p>
Console.WriteLine("Example 7\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[1A]: First\n\n1.1A", "<p>1.1A</p>", "abbreviations|advanced");
}
// Abbreviations should match whole word only, even if there is another glossary term:
[Test]
public void ExtensionsAbbreviation_Example008()
{
// Example 8
// Section: Extensions / Abbreviation
//
// The following Markdown:
// *[SCO]: First
// *[SCOM]: Second
//
// SCOM
//
// Should be rendered as:
// <p><abbr title="Second">SCOM</abbr></p>
Console.WriteLine("Example 8\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[SCO]: First\n*[SCOM]: Second\n\nSCOM", "<p><abbr title=\"Second\">SCOM</abbr></p>", "abbreviations|advanced");
}
// Abbreviations should only match when surrounded by whitespace:
[Test]
public void ExtensionsAbbreviation_Example009()
{
// Example 9
// Section: Extensions / Abbreviation
//
// The following Markdown:
// *[PR]: Pull Request
//
// PRAA
//
// Should be rendered as:
// <p>PRAA</p>
Console.WriteLine("Example 9\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[PR]: Pull Request\n\nPRAA", "<p>PRAA</p>", "abbreviations|advanced");
}
// Single character abbreviations should be matched
[Test]
public void ExtensionsAbbreviation_Example010()
{
// Example 10
// Section: Extensions / Abbreviation
//
// The following Markdown:
// *[A]: Foo
//
// A
//
// Should be rendered as:
// <p><abbr title="Foo">A</abbr></p>
Console.WriteLine("Example 10\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[A]: Foo\n\nA", "<p><abbr title=\"Foo\">A</abbr></p>", "abbreviations|advanced");
}
// The longest matching abbreviation should be used
[Test]
public void ExtensionsAbbreviation_Example011()
{
// Example 11
// Section: Extensions / Abbreviation
//
// The following Markdown:
// *[Foo]: foo
// *[Foo Bar]: foobar
//
// Foo B
//
// Should be rendered as:
// <p><abbr title="foo">Foo</abbr> B</p>
Console.WriteLine("Example 11\nSection Extensions / Abbreviation\n");
TestParser.TestSpec("*[Foo]: foo\n*[Foo Bar]: foobar\n\nFoo B", "<p><abbr title=\"foo\">Foo</abbr> B</p>", "abbreviations|advanced");
}
}
}

View File

@@ -97,4 +97,25 @@ Abbreviations should only match when surrounded by whitespace:
PRAA
.
<p>PRAA</p>
````````````````````````````````
Single character abbreviations should be matched
```````````````````````````````` example
*[A]: Foo
A
.
<p><abbr title="Foo">A</abbr></p>
````````````````````````````````
The longest matching abbreviation should be used
```````````````````````````````` example
*[Foo]: foo
*[Foo Bar]: foobar
Foo B
.
<p><abbr title="foo">Foo</abbr> B</p>
````````````````````````````````

View File

@@ -0,0 +1,224 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Auto Identifiers
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.AutoIdentifiers
{
[TestFixture]
public class TestExtensionsHeadingAutoIdentifiers
{
// # Extensions
//
// This section describes the auto identifier extension
//
// ## Heading Auto Identifiers
//
// Allows to automatically creates an identifier for a heading:
[Test]
public void ExtensionsHeadingAutoIdentifiers_Example001()
{
// Example 1
// Section: Extensions / Heading Auto Identifiers
//
// The following Markdown:
// # This is a heading
//
// Should be rendered as:
// <h1 id="this-is-a-heading">This is a heading</h1>
Console.WriteLine("Example 1\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# This is a heading", "<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced");
}
// Only punctuation `-`, `_` and `.` is kept, all other non letter characters are discarded.
// Consecutive same character `-`, `_` or `.` are rendered into a single one
// Characters `-`, `_` and `.` at the end of the string are also discarded.
[Test]
public void ExtensionsHeadingAutoIdentifiers_Example002()
{
// Example 2
// Section: Extensions / Heading Auto Identifiers
//
// The following Markdown:
// # This - is a &@! heading _ with . and ! -
//
// Should be rendered as:
// <h1 id="this-is-a-heading_with.and">This - is a &amp;@! heading _ with . and ! -</h1>
Console.WriteLine("Example 2\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# This - is a &@! heading _ with . and ! -", "<h1 id=\"this-is-a-heading_with.and\">This - is a &amp;@! heading _ with . and ! -</h1>", "autoidentifiers|advanced");
}
// Formatting (emphasis) are also discarded:
[Test]
public void ExtensionsHeadingAutoIdentifiers_Example003()
{
// Example 3
// Section: Extensions / Heading Auto Identifiers
//
// The following Markdown:
// # This is a *heading*
//
// Should be rendered as:
// <h1 id="this-is-a-heading">This is a <em>heading</em></h1>
Console.WriteLine("Example 3\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# This is a *heading*", "<h1 id=\"this-is-a-heading\">This is a <em>heading</em></h1>", "autoidentifiers|advanced");
}
// Links are also removed:
[Test]
public void ExtensionsHeadingAutoIdentifiers_Example004()
{
// Example 4
// Section: Extensions / Heading Auto Identifiers
//
// The following Markdown:
// # This is a [heading](/url)
//
// Should be rendered as:
// <h1 id="this-is-a-heading">This is a <a href="/url">heading</a></h1>
Console.WriteLine("Example 4\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# This is a [heading](/url)", "<h1 id=\"this-is-a-heading\">This is a <a href=\"/url\">heading</a></h1>", "autoidentifiers|advanced");
}
// If multiple heading have the same text, -1, -2...-n will be postfix to the header id.
[Test]
public void ExtensionsHeadingAutoIdentifiers_Example005()
{
// Example 5
// Section: Extensions / Heading Auto Identifiers
//
// The following Markdown:
// # This is a heading
// # This is a heading
//
// Should be rendered as:
// <h1 id="this-is-a-heading">This is a heading</h1>
// <h1 id="this-is-a-heading-1">This is a heading</h1>
Console.WriteLine("Example 5\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# This is a heading\n# This is a heading", "<h1 id=\"this-is-a-heading\">This is a heading</h1>\n<h1 id=\"this-is-a-heading-1\">This is a heading</h1>", "autoidentifiers|advanced");
}
// The heading Id will start on the first letter character of the heading, all previous characters will be discarded:
[Test]
public void ExtensionsHeadingAutoIdentifiers_Example006()
{
// Example 6
// Section: Extensions / Heading Auto Identifiers
//
// The following Markdown:
// # 1.0 This is a heading
//
// Should be rendered as:
// <h1 id="this-is-a-heading">1.0 This is a heading</h1>
Console.WriteLine("Example 6\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# 1.0 This is a heading", "<h1 id=\"this-is-a-heading\">1.0 This is a heading</h1>", "autoidentifiers|advanced");
}
// If the heading is all stripped by the previous rules, the id `section` will be used instead:
[Test]
public void ExtensionsHeadingAutoIdentifiers_Example007()
{
// Example 7
// Section: Extensions / Heading Auto Identifiers
//
// The following Markdown:
// # 1.0 & ^ % *
// # 1.0 & ^ % *
//
// Should be rendered as:
// <h1 id="section">1.0 &amp; ^ % *</h1>
// <h1 id="section-1">1.0 &amp; ^ % *</h1>
Console.WriteLine("Example 7\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# 1.0 & ^ % *\n# 1.0 & ^ % *", "<h1 id=\"section\">1.0 &amp; ^ % *</h1>\n<h1 id=\"section-1\">1.0 &amp; ^ % *</h1>", "autoidentifiers|advanced");
}
// When the options "AutoLink" is setup, it is possible to link to an existing heading by using the
// exact same Label text as the heading:
[Test]
public void ExtensionsHeadingAutoIdentifiers_Example008()
{
// Example 8
// Section: Extensions / Heading Auto Identifiers
//
// The following Markdown:
// # This is a heading
// [This is a heading]
//
// Should be rendered as:
// <h1 id="this-is-a-heading">This is a heading</h1>
// <p><a href="#this-is-a-heading">This is a heading</a></p>
Console.WriteLine("Example 8\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("# This is a heading\n[This is a heading]", "<h1 id=\"this-is-a-heading\">This is a heading</h1>\n<p><a href=\"#this-is-a-heading\">This is a heading</a></p>", "autoidentifiers|advanced");
}
// Links before the heading are also working:
[Test]
public void ExtensionsHeadingAutoIdentifiers_Example009()
{
// Example 9
// Section: Extensions / Heading Auto Identifiers
//
// The following Markdown:
// [This is a heading]
// # This is a heading
//
// Should be rendered as:
// <p><a href="#this-is-a-heading">This is a heading</a></p>
// <h1 id="this-is-a-heading">This is a heading</h1>
Console.WriteLine("Example 9\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("[This is a heading]\n# This is a heading", "<p><a href=\"#this-is-a-heading\">This is a heading</a></p>\n<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced");
}
// The text of the link can be changed:
[Test]
public void ExtensionsHeadingAutoIdentifiers_Example010()
{
// Example 10
// Section: Extensions / Heading Auto Identifiers
//
// The following Markdown:
// [With a new text][This is a heading]
// # This is a heading
//
// Should be rendered as:
// <p><a href="#this-is-a-heading">With a new text</a></p>
// <h1 id="this-is-a-heading">This is a heading</h1>
Console.WriteLine("Example 10\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("[With a new text][This is a heading]\n# This is a heading", "<p><a href=\"#this-is-a-heading\">With a new text</a></p>\n<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced");
}
// An autoidentifier should not conflict with an existing link:
[Test]
public void ExtensionsHeadingAutoIdentifiers_Example011()
{
// Example 11
// Section: Extensions / Heading Auto Identifiers
//
// The following Markdown:
// ![scenario image][scenario]
// ## Scenario
// [scenario]: ./scenario.png
//
// Should be rendered as:
// <p><img src="./scenario.png" alt="scenario image" /></p>
// <h2 id="scenario">Scenario</h2>
Console.WriteLine("Example 11\nSection Extensions / Heading Auto Identifiers\n");
TestParser.TestSpec("![scenario image][scenario]\n## Scenario\n[scenario]: ./scenario.png", "<p><img src=\"./scenario.png\" alt=\"scenario image\" /></p>\n<h2 id=\"scenario\">Scenario</h2>", "autoidentifiers|advanced");
}
}
}

View File

@@ -0,0 +1,449 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Auto Links
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.AutoLinks
{
[TestFixture]
public class TestExtensionsAutoLinks
{
// # Extensions
//
// This section describes the different extensions supported:
//
// ## AutoLinks
//
// Autolinks will format as a HTML link any string that starts by:
//
// - `http://` or `https://`
// - `ftp://`
// - `mailto:`
// - `www.`
[Test]
public void ExtensionsAutoLinks_Example001()
{
// Example 1
// Section: Extensions / AutoLinks
//
// The following Markdown:
// This is a http://www.google.com URL and https://www.google.com
// This is a ftp://test.com
// And a mailto:email@toto.com
// And a plain www.google.com
//
// Should be rendered as:
// <p>This is a <a href="http://www.google.com">http://www.google.com</a> URL and <a href="https://www.google.com">https://www.google.com</a>
// This is a <a href="ftp://test.com">ftp://test.com</a>
// And a <a href="mailto:email@toto.com">email@toto.com</a>
// And a plain <a href="http://www.google.com">www.google.com</a></p>
Console.WriteLine("Example 1\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("This is a http://www.google.com URL and https://www.google.com\nThis is a ftp://test.com\nAnd a mailto:email@toto.com\nAnd a plain www.google.com", "<p>This is a <a href=\"http://www.google.com\">http://www.google.com</a> URL and <a href=\"https://www.google.com\">https://www.google.com</a>\nThis is a <a href=\"ftp://test.com\">ftp://test.com</a>\nAnd a <a href=\"mailto:email@toto.com\">email@toto.com</a>\nAnd a plain <a href=\"http://www.google.com\">www.google.com</a></p>", "autolinks|advanced");
}
// But incomplete links will not be matched:
[Test]
public void ExtensionsAutoLinks_Example002()
{
// Example 2
// Section: Extensions / AutoLinks
//
// The following Markdown:
// This is not a http:/www.google.com URL and https:/www.google.com
// This is not a ftp:/test.com
// And not a mailto:emailtoto.com
// And not a plain www. or a www.x
//
// Should be rendered as:
// <p>This is not a http:/www.google.com URL and https:/www.google.com
// This is not a ftp:/test.com
// And not a mailto:emailtoto.com
// And not a plain www. or a www.x</p>
Console.WriteLine("Example 2\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("This is not a http:/www.google.com URL and https:/www.google.com\nThis is not a ftp:/test.com\nAnd not a mailto:emailtoto.com\nAnd not a plain www. or a www.x ", "<p>This is not a http:/www.google.com URL and https:/www.google.com\nThis is not a ftp:/test.com\nAnd not a mailto:emailtoto.com\nAnd not a plain www. or a www.x</p>", "autolinks|advanced");
}
// Previous character must be a punctuation or a valid space (tab, space, new line):
[Test]
public void ExtensionsAutoLinks_Example003()
{
// Example 3
// Section: Extensions / AutoLinks
//
// The following Markdown:
// This is not a nhttp://www.google.com URL but this is (https://www.google.com)
//
// Should be rendered as:
// <p>This is not a nhttp://www.google.com URL but this is (<a href="https://www.google.com">https://www.google.com</a>)</p>
Console.WriteLine("Example 3\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("This is not a nhttp://www.google.com URL but this is (https://www.google.com)", "<p>This is not a nhttp://www.google.com URL but this is (<a href=\"https://www.google.com\">https://www.google.com</a>)</p>", "autolinks|advanced");
}
// An autolink should not interfere with an `<a>` HTML inline:
[Test]
public void ExtensionsAutoLinks_Example004()
{
// Example 4
// Section: Extensions / AutoLinks
//
// The following Markdown:
// This is an HTML <a href="http://www.google.com">http://www.google.com</a> link
//
// Should be rendered as:
// <p>This is an HTML <a href="http://www.google.com">http://www.google.com</a> link</p>
Console.WriteLine("Example 4\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link", "<p>This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link</p>", "autolinks|advanced");
}
// or even within emphasis:
[Test]
public void ExtensionsAutoLinks_Example005()
{
// Example 5
// Section: Extensions / AutoLinks
//
// The following Markdown:
// This is an HTML <a href="http://www.google.com"> **http://www.google.com** </a> link
//
// Should be rendered as:
// <p>This is an HTML <a href="http://www.google.com"> <strong>http://www.google.com</strong> </a> link</p>
Console.WriteLine("Example 5\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("This is an HTML <a href=\"http://www.google.com\"> **http://www.google.com** </a> link", "<p>This is an HTML <a href=\"http://www.google.com\"> <strong>http://www.google.com</strong> </a> link</p>", "autolinks|advanced");
}
// An autolink should not interfere with a markdown link:
[Test]
public void ExtensionsAutoLinks_Example006()
{
// Example 6
// Section: Extensions / AutoLinks
//
// The following Markdown:
// This is an HTML [http://www.google.com](http://www.google.com) link
//
// Should be rendered as:
// <p>This is an HTML <a href="http://www.google.com">http://www.google.com</a> link</p>
Console.WriteLine("Example 6\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("This is an HTML [http://www.google.com](http://www.google.com) link", "<p>This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link</p>", "autolinks|advanced");
}
// A link embraced by pending emphasis should let the emphasis takes precedence if characters are placed at the end of the matched link:
[Test]
public void ExtensionsAutoLinks_Example007()
{
// Example 7
// Section: Extensions / AutoLinks
//
// The following Markdown:
// Check **http://www.a.com** or __http://www.b.com__
//
// Should be rendered as:
// <p>Check <strong><a href="http://www.a.com">http://www.a.com</a></strong> or <strong><a href="http://www.b.com">http://www.b.com</a></strong></p>
Console.WriteLine("Example 7\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("Check **http://www.a.com** or __http://www.b.com__", "<p>Check <strong><a href=\"http://www.a.com\">http://www.a.com</a></strong> or <strong><a href=\"http://www.b.com\">http://www.b.com</a></strong></p>", "autolinks|advanced");
}
// It is not mentioned by the spec, but empty emails won't be matched (only a subset of [RFC2368](https://tools.ietf.org/html/rfc2368) is supported by auto links):
[Test]
public void ExtensionsAutoLinks_Example008()
{
// Example 8
// Section: Extensions / AutoLinks
//
// The following Markdown:
// mailto:email@test.com is okay, but mailto:@test.com is not
//
// Should be rendered as:
// <p><a href="mailto:email@test.com">email@test.com</a> is okay, but mailto:@test.com is not</p>
Console.WriteLine("Example 8\nSection Extensions / AutoLinks\n");
TestParser.TestSpec("mailto:email@test.com is okay, but mailto:@test.com is not", "<p><a href=\"mailto:email@test.com\">email@test.com</a> is okay, but mailto:@test.com is not</p>", "autolinks|advanced");
}
}
[TestFixture]
public class TestExtensionsAutoLinksGFMSupport
{
// ### GFM Support
//
// Extract from [GFM Autolinks extensions specs](https://github.github.com/gfm/#autolinks-extension-)
[Test]
public void ExtensionsAutoLinksGFMSupport_Example009()
{
// Example 9
// Section: Extensions / AutoLinks / GFM Support
//
// The following Markdown:
// www.commonmark.org
//
// Should be rendered as:
// <p><a href="http://www.commonmark.org">www.commonmark.org</a></p>
Console.WriteLine("Example 9\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("www.commonmark.org", "<p><a href=\"http://www.commonmark.org\">www.commonmark.org</a></p>", "autolinks|advanced");
}
[Test]
public void ExtensionsAutoLinksGFMSupport_Example010()
{
// Example 10
// Section: Extensions / AutoLinks / GFM Support
//
// The following Markdown:
// Visit www.commonmark.org/help for more information.
//
// Should be rendered as:
// <p>Visit <a href="http://www.commonmark.org/help">www.commonmark.org/help</a> for more information.</p>
Console.WriteLine("Example 10\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("Visit www.commonmark.org/help for more information.", "<p>Visit <a href=\"http://www.commonmark.org/help\">www.commonmark.org/help</a> for more information.</p>", "autolinks|advanced");
}
[Test]
public void ExtensionsAutoLinksGFMSupport_Example011()
{
// Example 11
// Section: Extensions / AutoLinks / GFM Support
//
// The following Markdown:
// Visit www.commonmark.org.
//
// Visit www.commonmark.org/a.b.
//
// Should be rendered as:
// <p>Visit <a href="http://www.commonmark.org">www.commonmark.org</a>.</p>
// <p>Visit <a href="http://www.commonmark.org/a.b">www.commonmark.org/a.b</a>.</p>
Console.WriteLine("Example 11\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("Visit www.commonmark.org.\n\nVisit www.commonmark.org/a.b.", "<p>Visit <a href=\"http://www.commonmark.org\">www.commonmark.org</a>.</p>\n<p>Visit <a href=\"http://www.commonmark.org/a.b\">www.commonmark.org/a.b</a>.</p>", "autolinks|advanced");
}
[Test]
public void ExtensionsAutoLinksGFMSupport_Example012()
{
// Example 12
// Section: Extensions / AutoLinks / GFM Support
//
// The following Markdown:
// www.google.com/search?q=Markup+(business)
//
// (www.google.com/search?q=Markup+(business))
//
// Should be rendered as:
// <p><a href="http://www.google.com/search?q=Markup+(business)">www.google.com/search?q=Markup+(business)</a></p>
// <p>(<a href="http://www.google.com/search?q=Markup+(business)">www.google.com/search?q=Markup+(business)</a>)</p>
Console.WriteLine("Example 12\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("www.google.com/search?q=Markup+(business)\n\n(www.google.com/search?q=Markup+(business))", "<p><a href=\"http://www.google.com/search?q=Markup+(business)\">www.google.com/search?q=Markup+(business)</a></p>\n<p>(<a href=\"http://www.google.com/search?q=Markup+(business)\">www.google.com/search?q=Markup+(business)</a>)</p>", "autolinks|advanced");
}
[Test]
public void ExtensionsAutoLinksGFMSupport_Example013()
{
// Example 13
// Section: Extensions / AutoLinks / GFM Support
//
// The following Markdown:
// www.google.com/search?q=commonmark&hl=en
//
// www.google.com/search?q=commonmark&hl;
//
// Should be rendered as:
// <p><a href="http://www.google.com/search?q=commonmark&amp;hl=en">www.google.com/search?q=commonmark&amp;hl=en</a></p>
// <p><a href="http://www.google.com/search?q=commonmark">www.google.com/search?q=commonmark</a>&amp;hl;</p>
Console.WriteLine("Example 13\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("www.google.com/search?q=commonmark&hl=en\n\nwww.google.com/search?q=commonmark&hl;", "<p><a href=\"http://www.google.com/search?q=commonmark&amp;hl=en\">www.google.com/search?q=commonmark&amp;hl=en</a></p>\n<p><a href=\"http://www.google.com/search?q=commonmark\">www.google.com/search?q=commonmark</a>&amp;hl;</p>", "autolinks|advanced");
}
[Test]
public void ExtensionsAutoLinksGFMSupport_Example014()
{
// Example 14
// Section: Extensions / AutoLinks / GFM Support
//
// The following Markdown:
// www.commonmark.org/he<lp
//
// Should be rendered as:
// <p><a href="http://www.commonmark.org/he">www.commonmark.org/he</a>&lt;lp</p>
Console.WriteLine("Example 14\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("www.commonmark.org/he<lp", "<p><a href=\"http://www.commonmark.org/he\">www.commonmark.org/he</a>&lt;lp</p>", "autolinks|advanced");
}
[Test]
public void ExtensionsAutoLinksGFMSupport_Example015()
{
// Example 15
// Section: Extensions / AutoLinks / GFM Support
//
// The following Markdown:
// http://commonmark.org
//
// (Visit https://encrypted.google.com/search?q=Markup+(business))
//
// Anonymous FTP is available at ftp://foo.bar.baz.
//
// Should be rendered as:
// <p><a href="http://commonmark.org">http://commonmark.org</a></p>
// <p>(Visit <a href="https://encrypted.google.com/search?q=Markup+(business)">https://encrypted.google.com/search?q=Markup+(business)</a>)</p>
// <p>Anonymous FTP is available at <a href="ftp://foo.bar.baz">ftp://foo.bar.baz</a>.</p>
Console.WriteLine("Example 15\nSection Extensions / AutoLinks / GFM Support\n");
TestParser.TestSpec("http://commonmark.org\n\n(Visit https://encrypted.google.com/search?q=Markup+(business))\n\nAnonymous FTP is available at ftp://foo.bar.baz.", "<p><a href=\"http://commonmark.org\">http://commonmark.org</a></p>\n<p>(Visit <a href=\"https://encrypted.google.com/search?q=Markup+(business)\">https://encrypted.google.com/search?q=Markup+(business)</a>)</p>\n<p>Anonymous FTP is available at <a href=\"ftp://foo.bar.baz\">ftp://foo.bar.baz</a>.</p>", "autolinks|advanced");
}
}
[TestFixture]
public class TestExtensionsAutoLinksValidDomainTests
{
// ### Valid Domain Tests
//
// Domain names that have empty segments won't be matched
[Test]
public void ExtensionsAutoLinksValidDomainTests_Example016()
{
// Example 16
// Section: Extensions / AutoLinks / Valid Domain Tests
//
// The following Markdown:
// www..
// www..com
// http://test.
// http://.test
// http://.
// http://..
// ftp://test.
// ftp://.test
// mailto:email@test.
// mailto:email@.test
//
// Should be rendered as:
// <p>www..
// www..com
// http://test.
// http://.test
// http://.
// http://..
// ftp://test.
// ftp://.test
// mailto:email@test.
// mailto:email@.test</p>
Console.WriteLine("Example 16\nSection Extensions / AutoLinks / Valid Domain Tests\n");
TestParser.TestSpec("www..\nwww..com\nhttp://test.\nhttp://.test\nhttp://.\nhttp://..\nftp://test.\nftp://.test\nmailto:email@test.\nmailto:email@.test", "<p>www..\nwww..com\nhttp://test.\nhttp://.test\nhttp://.\nhttp://..\nftp://test.\nftp://.test\nmailto:email@test.\nmailto:email@.test</p>", "autolinks|advanced");
}
// Domain names with too few segments won't be matched
[Test]
public void ExtensionsAutoLinksValidDomainTests_Example017()
{
// Example 17
// Section: Extensions / AutoLinks / Valid Domain Tests
//
// The following Markdown:
// www
// www.com
// http://test
// ftp://test
// mailto:email@test
//
// Should be rendered as:
// <p>www
// www.com
// http://test
// ftp://test
// mailto:email@test</p>
Console.WriteLine("Example 17\nSection Extensions / AutoLinks / Valid Domain Tests\n");
TestParser.TestSpec("www\nwww.com\nhttp://test\nftp://test\nmailto:email@test", "<p>www\nwww.com\nhttp://test\nftp://test\nmailto:email@test</p>", "autolinks|advanced");
}
// Domain names that contain an underscores in the last two segments won't be matched
[Test]
public void ExtensionsAutoLinksValidDomainTests_Example018()
{
// Example 18
// Section: Extensions / AutoLinks / Valid Domain Tests
//
// The following Markdown:
// www._test.foo.bar is okay, but www._test.foo is not
//
// http://te_st.foo.bar is okay, as is http://test.foo_.bar.foo
//
// But http://te_st.foo, http://test.foo_.bar and http://test._foo are not
//
// ftp://test_.foo.bar is okay, but ftp://test.fo_o is not
//
// mailto:email@_test.foo.bar is okay, but mailto:email@_test.foo is not
//
// Should be rendered as:
// <p><a href="http://www._test.foo.bar">www._test.foo.bar</a> is okay, but www._test.foo is not</p>
// <p><a href="http://te_st.foo.bar">http://te_st.foo.bar</a> is okay, as is <a href="http://test.foo_.bar.foo">http://test.foo_.bar.foo</a></p>
// <p>But http://te_st.foo, http://test.foo_.bar and http://test._foo are not</p>
// <p><a href="ftp://test_.foo.bar">ftp://test_.foo.bar</a> is okay, but ftp://test.fo_o is not</p>
// <p><a href="mailto:email@_test.foo.bar">email@_test.foo.bar</a> is okay, but mailto:email@_test.foo is not</p>
Console.WriteLine("Example 18\nSection Extensions / AutoLinks / Valid Domain Tests\n");
TestParser.TestSpec("www._test.foo.bar is okay, but www._test.foo is not\n\nhttp://te_st.foo.bar is okay, as is http://test.foo_.bar.foo\n\nBut http://te_st.foo, http://test.foo_.bar and http://test._foo are not\n\nftp://test_.foo.bar is okay, but ftp://test.fo_o is not\n\nmailto:email@_test.foo.bar is okay, but mailto:email@_test.foo is not", "<p><a href=\"http://www._test.foo.bar\">www._test.foo.bar</a> is okay, but www._test.foo is not</p>\n<p><a href=\"http://te_st.foo.bar\">http://te_st.foo.bar</a> is okay, as is <a href=\"http://test.foo_.bar.foo\">http://test.foo_.bar.foo</a></p>\n<p>But http://te_st.foo, http://test.foo_.bar and http://test._foo are not</p>\n<p><a href=\"ftp://test_.foo.bar\">ftp://test_.foo.bar</a> is okay, but ftp://test.fo_o is not</p>\n<p><a href=\"mailto:email@_test.foo.bar\">email@_test.foo.bar</a> is okay, but mailto:email@_test.foo is not</p>", "autolinks|advanced");
}
// Domain names that contain invalid characters (not AlphaNumberic, -, _ or .) won't be matched
[Test]
public void ExtensionsAutoLinksValidDomainTests_Example019()
{
// Example 19
// Section: Extensions / AutoLinks / Valid Domain Tests
//
// The following Markdown:
// https://[your-domain]/api
//
// Should be rendered as:
// <p>https://[your-domain]/api</p>
Console.WriteLine("Example 19\nSection Extensions / AutoLinks / Valid Domain Tests\n");
TestParser.TestSpec("https://[your-domain]/api", "<p>https://[your-domain]/api</p>", "autolinks|advanced");
}
// Domain names followed by ?, : or # instead of / are matched
[Test]
public void ExtensionsAutoLinksValidDomainTests_Example020()
{
// Example 20
// Section: Extensions / AutoLinks / Valid Domain Tests
//
// The following Markdown:
// https://github.com?
//
// https://github.com?a
//
// https://github.com#a
//
// https://github.com:
//
// https://github.com:443
//
// Should be rendered as:
// <p><a href="https://github.com">https://github.com</a>?</p>
// <p><a href="https://github.com?a">https://github.com?a</a></p>
// <p><a href="https://github.com#a">https://github.com#a</a></p>
// <p><a href="https://github.com">https://github.com</a>:</p>
// <p><a href="https://github.com:443">https://github.com:443</a></p>
Console.WriteLine("Example 20\nSection Extensions / AutoLinks / Valid Domain Tests\n");
TestParser.TestSpec("https://github.com?\n\nhttps://github.com?a\n\nhttps://github.com#a\n\nhttps://github.com:\n\nhttps://github.com:443", "<p><a href=\"https://github.com\">https://github.com</a>?</p>\n<p><a href=\"https://github.com?a\">https://github.com?a</a></p>\n<p><a href=\"https://github.com#a\">https://github.com#a</a></p>\n<p><a href=\"https://github.com\">https://github.com</a>:</p>\n<p><a href=\"https://github.com:443\">https://github.com:443</a></p>", "autolinks|advanced");
}
}
}

View File

@@ -0,0 +1,111 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Bootstrap
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.Bootstrap
{
[TestFixture]
public class TestExtensionsBootstrap
{
// # Extensions
//
// Adds support for outputing bootstrap ready tags:
//
// ## Bootstrap
//
// Adds bootstrap `.table` class to `<table>`:
[Test]
public void ExtensionsBootstrap_Example001()
{
// Example 1
// Section: Extensions / Bootstrap
//
// The following Markdown:
// Name | Value
// -----| -----
// Abc | 16
//
// Should be rendered as:
// <table class="table">
// <thead>
// <tr>
// <th>Name</th>
// <th>Value</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>Abc</td>
// <td>16</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 1\nSection Extensions / Bootstrap\n");
TestParser.TestSpec("Name | Value\n-----| -----\nAbc | 16", "<table class=\"table\">\n<thead>\n<tr>\n<th>Name</th>\n<th>Value</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Abc</td>\n<td>16</td>\n</tr>\n</tbody>\n</table>", "bootstrap+pipetables+figures+attributes");
}
// Adds bootstrap `.blockquote` class to `<blockquote>`:
[Test]
public void ExtensionsBootstrap_Example002()
{
// Example 2
// Section: Extensions / Bootstrap
//
// The following Markdown:
// > This is a blockquote
//
// Should be rendered as:
// <blockquote class="blockquote">
// <p>This is a blockquote</p>
// </blockquote>
Console.WriteLine("Example 2\nSection Extensions / Bootstrap\n");
TestParser.TestSpec("> This is a blockquote", "<blockquote class=\"blockquote\">\n<p>This is a blockquote</p>\n</blockquote>", "bootstrap+pipetables+figures+attributes");
}
// Adds bootstrap `.figure` class to `<figure>` and `.figure-caption` to `<figcaption>`
[Test]
public void ExtensionsBootstrap_Example003()
{
// Example 3
// Section: Extensions / Bootstrap
//
// The following Markdown:
// ^^^
// This is a text in a caption
// ^^^ This is the caption
//
// Should be rendered as:
// <figure class="figure">
// <p>This is a text in a caption</p>
// <figcaption class="figure-caption">This is the caption</figcaption>
// </figure>
Console.WriteLine("Example 3\nSection Extensions / Bootstrap\n");
TestParser.TestSpec("^^^\nThis is a text in a caption\n^^^ This is the caption", "<figure class=\"figure\">\n<p>This is a text in a caption</p>\n<figcaption class=\"figure-caption\">This is the caption</figcaption>\n</figure>", "bootstrap+pipetables+figures+attributes");
}
// Adds the `.img-fluid` class to all image links `<img>`
[Test]
public void ExtensionsBootstrap_Example004()
{
// Example 4
// Section: Extensions / Bootstrap
//
// The following Markdown:
// ![Image Link](/url)
//
// Should be rendered as:
// <p><img src="/url" class="img-fluid" alt="Image Link" /></p>
Console.WriteLine("Example 4\nSection Extensions / Bootstrap\n");
TestParser.TestSpec("![Image Link](/url)", "<p><img src=\"/url\" class=\"img-fluid\" alt=\"Image Link\" /></p>", "bootstrap+pipetables+figures+attributes");
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,211 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Custom Containers
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.CustomContainers
{
[TestFixture]
public class TestExtensionsCustomContainer
{
// # Extensions
//
// This section describes the different extensions supported:
//
// ## Custom Container
//
// A custom container is similar to a fenced code block, but it is using the character `:` to declare a block (with at least 3 characters), and instead of generating a `<pre><code>...</code></pre>` it will generate a `<div>...</div>` block.
[Test]
public void ExtensionsCustomContainer_Example001()
{
// Example 1
// Section: Extensions / Custom Container
//
// The following Markdown:
// :::spoiler
// This is a *spoiler*
// :::
//
// Should be rendered as:
// <div class="spoiler"><p>This is a <em>spoiler</em></p>
// </div>
Console.WriteLine("Example 1\nSection Extensions / Custom Container\n");
TestParser.TestSpec(":::spoiler\nThis is a *spoiler*\n:::", "<div class=\"spoiler\"><p>This is a <em>spoiler</em></p>\n</div>", "customcontainers+attributes|advanced");
}
// The text following the opened custom container is optional:
[Test]
public void ExtensionsCustomContainer_Example002()
{
// Example 2
// Section: Extensions / Custom Container
//
// The following Markdown:
// :::
// This is a regular div
// :::
//
// Should be rendered as:
// <div><p>This is a regular div</p>
// </div>
Console.WriteLine("Example 2\nSection Extensions / Custom Container\n");
TestParser.TestSpec(":::\nThis is a regular div\n:::", "<div><p>This is a regular div</p>\n</div>", "customcontainers+attributes|advanced");
}
// Like for fenced code block, you can use more than 3 `:` characters as long as the closing has the same number of characters:
[Test]
public void ExtensionsCustomContainer_Example003()
{
// Example 3
// Section: Extensions / Custom Container
//
// The following Markdown:
// ::::::::::::spoiler
// This is a spoiler
// ::::::::::::
//
// Should be rendered as:
// <div class="spoiler"><p>This is a spoiler</p>
// </div>
Console.WriteLine("Example 3\nSection Extensions / Custom Container\n");
TestParser.TestSpec("::::::::::::spoiler\nThis is a spoiler\n::::::::::::", "<div class=\"spoiler\"><p>This is a spoiler</p>\n</div>", "customcontainers+attributes|advanced");
}
// Like for fenced code block, a custom container can span over multiple empty lines in a list block:
[Test]
public void ExtensionsCustomContainer_Example004()
{
// Example 4
// Section: Extensions / Custom Container
//
// The following Markdown:
// - This is a list
// :::spoiler
// This is a spoiler
// - item1
// - item2
// :::
// - A second item in the list
//
// Should be rendered as:
// <ul>
// <li>This is a list
// <div class="spoiler">This is a spoiler
// <ul>
// <li>item1</li>
// <li>item2</li>
// </ul>
// </div>
// </li>
// <li>A second item in the list</li>
// </ul>
Console.WriteLine("Example 4\nSection Extensions / Custom Container\n");
TestParser.TestSpec("- This is a list\n :::spoiler\n This is a spoiler\n - item1\n - item2\n :::\n- A second item in the list", "<ul>\n<li>This is a list\n<div class=\"spoiler\">This is a spoiler\n<ul>\n<li>item1</li>\n<li>item2</li>\n</ul>\n</div>\n</li>\n<li>A second item in the list</li>\n</ul>", "customcontainers+attributes|advanced");
}
// Attributes extension is also supported for Custom Container, as long as the Attributes extension is activated after the CustomContainer extension (`.UseCustomContainer().UseAttributes()`)
[Test]
public void ExtensionsCustomContainer_Example005()
{
// Example 5
// Section: Extensions / Custom Container
//
// The following Markdown:
// :::spoiler {#myspoiler myprop=yes}
// This is a spoiler
// :::
//
// Should be rendered as:
// <div id="myspoiler" class="spoiler" myprop="yes"><p>This is a spoiler</p>
// </div>
Console.WriteLine("Example 5\nSection Extensions / Custom Container\n");
TestParser.TestSpec(":::spoiler {#myspoiler myprop=yes}\nThis is a spoiler\n:::", "<div id=\"myspoiler\" class=\"spoiler\" myprop=\"yes\"><p>This is a spoiler</p>\n</div>", "customcontainers+attributes|advanced");
}
// The content of a custom container can contain any blocks:
[Test]
public void ExtensionsCustomContainer_Example006()
{
// Example 6
// Section: Extensions / Custom Container
//
// The following Markdown:
// :::mycontainer
// <p>This is a raw spoiler</p>
// :::
//
// Should be rendered as:
// <div class="mycontainer"><p>This is a raw spoiler</p>
// </div>
Console.WriteLine("Example 6\nSection Extensions / Custom Container\n");
TestParser.TestSpec(":::mycontainer\n<p>This is a raw spoiler</p>\n:::", "<div class=\"mycontainer\"><p>This is a raw spoiler</p>\n</div>", "customcontainers+attributes|advanced");
}
}
[TestFixture]
public class TestExtensionsInlineCustomContainer
{
// ## Inline Custom Container
//
// A custom container can also be used within an inline container (e.g: paragraph, heading...) by enclosing a text by a new emphasis `::`
[Test]
public void ExtensionsInlineCustomContainer_Example007()
{
// Example 7
// Section: Extensions / Inline Custom Container
//
// The following Markdown:
// This is a text ::with special emphasis::
//
// Should be rendered as:
// <p>This is a text <span>with special emphasis</span></p>
Console.WriteLine("Example 7\nSection Extensions / Inline Custom Container \n");
TestParser.TestSpec("This is a text ::with special emphasis::", "<p>This is a text <span>with special emphasis</span></p>", "customcontainers+attributes|advanced");
}
// Any other emphasis inline can be used within this emphasis inline container:
[Test]
public void ExtensionsInlineCustomContainer_Example008()
{
// Example 8
// Section: Extensions / Inline Custom Container
//
// The following Markdown:
// This is a text ::with special *emphasis*::
//
// Should be rendered as:
// <p>This is a text <span>with special <em>emphasis</em></span></p>
Console.WriteLine("Example 8\nSection Extensions / Inline Custom Container \n");
TestParser.TestSpec("This is a text ::with special *emphasis*::", "<p>This is a text <span>with special <em>emphasis</em></span></p>", "customcontainers+attributes|advanced");
}
// Attributes can be attached to a inline custom container:
[Test]
public void ExtensionsInlineCustomContainer_Example009()
{
// Example 9
// Section: Extensions / Inline Custom Container
//
// The following Markdown:
// This is a text ::with special emphasis::{#myId .myemphasis}
//
// Should be rendered as:
// <p>This is a text <span id="myId" class="myemphasis">with special emphasis</span></p>
Console.WriteLine("Example 9\nSection Extensions / Inline Custom Container \n");
TestParser.TestSpec("This is a text ::with special emphasis::{#myId .myemphasis}", "<p>This is a text <span id=\"myId\" class=\"myemphasis\">with special emphasis</span></p>", "customcontainers+attributes|advanced");
}
}
}

View File

@@ -0,0 +1,202 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Definition Lists
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.DefinitionLists
{
[TestFixture]
public class TestExtensionsDefinitionLists
{
// # Extensions
//
// 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.
[Test]
public void ExtensionsDefinitionLists_Example001()
{
// Example 1
// Section: Extensions / Definition lists
//
// The following Markdown:
//
// Term 1
// : This is a definition item
// With a paragraph
// > This is a block quote
//
// - This is a list
// - with an item2
//
// ```java
// Test
//
//
// ```
//
// And a last line
// : This ia another definition item
//
// Term2
// Term3 *with some inline*
// : This is another definition for term2
//
// Should be rendered as:
// <dl>
// <dt>Term 1</dt>
// <dd><p>This is a definition item
// With a paragraph</p>
// <blockquote>
// <p>This is a block quote</p>
// </blockquote>
// <ul>
// <li>This is a list</li>
// <li>with an item2</li>
// </ul>
// <pre><code class="language-java">Test
//
//
// </code></pre>
// <p>And a last line</p>
// </dd>
// <dd>This ia another definition item</dd>
// <dt>Term2</dt>
// <dt>Term3 <em>with some inline</em></dt>
// <dd>This is another definition for term2</dd>
// </dl>
Console.WriteLine("Example 1\nSection Extensions / Definition lists\n");
TestParser.TestSpec("\nTerm 1\n: This is a definition item\n With a paragraph\n > This is a block quote\n\n - This is a list\n - with an item2\n\n ```java\n Test\n\n\n ```\n\n And a last line\n: This ia another definition item\n\nTerm2\nTerm3 *with some inline*\n: This is another definition for term2", "<dl>\n<dt>Term 1</dt>\n<dd><p>This is a definition item\nWith a paragraph</p>\n<blockquote>\n<p>This is a block quote</p>\n</blockquote>\n<ul>\n<li>This is a list</li>\n<li>with an item2</li>\n</ul>\n<pre><code class=\"language-java\">Test\n\n\n</code></pre>\n<p>And a last line</p>\n</dd>\n<dd>This ia another definition item</dd>\n<dt>Term2</dt>\n<dt>Term3 <em>with some inline</em></dt>\n<dd>This is another definition for term2</dd>\n</dl>", "definitionlists+attributes|advanced");
}
// A definition term can be followed at most by one blank line. Lazy continuations are supported for definitions:
[Test]
public void ExtensionsDefinitionLists_Example002()
{
// Example 2
// Section: Extensions / Definition lists
//
// The following Markdown:
// Term 1
//
// : Definition
// with lazy continuation.
//
// Second paragraph of the definition.
//
// Should be rendered as:
// <dl>
// <dt>Term 1</dt>
// <dd><p>Definition
// with lazy continuation.</p>
// <p>Second paragraph of the definition.</p>
// </dd>
// </dl>
Console.WriteLine("Example 2\nSection Extensions / Definition lists\n");
TestParser.TestSpec("Term 1\n\n: Definition\nwith lazy continuation.\n\n Second paragraph of the definition.", "<dl>\n<dt>Term 1</dt>\n<dd><p>Definition\nwith lazy continuation.</p>\n<p>Second paragraph of the definition.</p>\n</dd>\n</dl>", "definitionlists+attributes|advanced");
}
// The definition must be indented to 4 characters including the `:`.
[Test]
public void ExtensionsDefinitionLists_Example003()
{
// Example 3
// Section: Extensions / Definition lists
//
// The following Markdown:
// Term 1
// : Invalid with less than 3 characters
//
// Should be rendered as:
// <p>Term 1
// : Invalid with less than 3 characters</p>
Console.WriteLine("Example 3\nSection Extensions / Definition lists\n");
TestParser.TestSpec("Term 1\n: Invalid with less than 3 characters", "<p>Term 1\n: Invalid with less than 3 characters</p>", "definitionlists+attributes|advanced");
}
// The `:` can be indented up to 3 spaces:
[Test]
public void ExtensionsDefinitionLists_Example004()
{
// Example 4
// Section: Extensions / Definition lists
//
// The following Markdown:
// Term 1
// : Valid even if `:` starts at most 3 spaces
//
// Should be rendered as:
// <dl>
// <dt>Term 1</dt>
// <dd>Valid even if <code>:</code> starts at most 3 spaces</dd>
// </dl>
Console.WriteLine("Example 4\nSection Extensions / Definition lists\n");
TestParser.TestSpec("Term 1\n : Valid even if `:` starts at most 3 spaces", "<dl>\n<dt>Term 1</dt>\n<dd>Valid even if <code>:</code> starts at most 3 spaces</dd>\n</dl>", "definitionlists+attributes|advanced");
}
// But more than 3 spaces before `:` will trigger an indented code block:
[Test]
public void ExtensionsDefinitionLists_Example005()
{
// Example 5
// Section: Extensions / Definition lists
//
// The following Markdown:
// Term 1
//
// : Not valid
//
// Should be rendered as:
// <p>Term 1</p>
// <pre><code>: Not valid
// </code></pre>
Console.WriteLine("Example 5\nSection Extensions / Definition lists\n");
TestParser.TestSpec("Term 1\n\n : Not valid", "<p>Term 1</p>\n<pre><code>: Not valid\n</code></pre>", "definitionlists+attributes|advanced");
}
// Definition lists can be nested inside list items
[Test]
public void ExtensionsDefinitionLists_Example006()
{
// Example 6
// Section: Extensions / Definition lists
//
// The following Markdown:
// 1. First
//
// 2. Second
//
// Term 1
// : Definition
//
// Term 2
// : Second Definition
//
// Should be rendered as:
// <ol>
// <li><p>First</p></li>
// <li><p>Second</p>
// <dl>
// <dt>Term 1</dt>
// <dd>Definition</dd>
// <dt>Term 2</dt>
// <dd>Second Definition</dd>
// </dl></li>
// </ol>
Console.WriteLine("Example 6\nSection Extensions / Definition lists\n");
TestParser.TestSpec("1. First\n \n2. Second\n \n Term 1\n : Definition\n \n Term 2\n : Second Definition", "<ol>\n<li><p>First</p></li>\n<li><p>Second</p>\n<dl>\n<dt>Term 1</dt>\n<dd>Definition</dd>\n<dt>Term 2</dt>\n<dd>Second Definition</dd>\n</dl></li>\n</ol>", "definitionlists+attributes|advanced");
}
}
}

View File

@@ -0,0 +1,94 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Diagrams
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.Diagrams
{
[TestFixture]
public class TestExtensionsMermaidDiagrams
{
// # Extensions
//
// Adds support for diagrams extension:
//
// ## Mermaid diagrams
//
// Using a fenced code block with the `mermaid` language info will output a `<div class='mermaid'>` instead of a `pre/code` block:
[Test]
public void ExtensionsMermaidDiagrams_Example001()
{
// Example 1
// Section: Extensions / Mermaid diagrams
//
// The following Markdown:
// ```mermaid
// graph TD;
// A-->B;
// A-->C;
// B-->D;
// C-->D;
// ```
//
// Should be rendered as:
// <div class="mermaid">graph TD;
// A-->B;
// A-->C;
// B-->D;
// C-->D;
// </div>
Console.WriteLine("Example 1\nSection Extensions / Mermaid diagrams\n");
TestParser.TestSpec("```mermaid\ngraph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n```", "<div class=\"mermaid\">graph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n</div>", "diagrams|advanced");
}
}
[TestFixture]
public class TestExtensionsNomnomlDiagrams
{
// ## nomnoml diagrams
//
// Using a fenced code block with the `nomnoml` language info will output a `<div class='nomnoml'>` instead of a `pre/code` block:
[Test]
public void ExtensionsNomnomlDiagrams_Example002()
{
// Example 2
// Section: Extensions / nomnoml diagrams
//
// The following Markdown:
// ```nomnoml
// [example|
// propertyA: Int
// propertyB: string
// |
// methodA()
// methodB()
// |
// [subA]--[subB]
// [subA]-:>[sub C]
// ]
// ```
//
// Should be rendered as:
// <div class="nomnoml">[example|
// propertyA: Int
// propertyB: string
// |
// methodA()
// methodB()
// |
// [subA]--[subB]
// [subA]-:>[sub C]
// ]
// </div>
Console.WriteLine("Example 2\nSection Extensions / nomnoml diagrams\n");
TestParser.TestSpec("```nomnoml\n[example|\n propertyA: Int\n propertyB: string\n|\n methodA()\n methodB()\n|\n [subA]--[subB]\n [subA]-:>[sub C]\n]\n```", "<div class=\"nomnoml\">[example|\n propertyA: Int\n propertyB: string\n|\n methodA()\n methodB()\n|\n [subA]--[subB]\n [subA]-:>[sub C]\n]\n</div>", "diagrams|advanced");
}
// TODO: Add other text diagram languages
}
}

View File

@@ -0,0 +1,91 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Emoji
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.Emoji
{
[TestFixture]
public class TestExtensionsEmoji
{
// # Extensions
//
// This section describes the different extensions supported:
//
// ## Emoji
//
// Emoji and smiley can be converted to their respective unicode characters:
[Test]
public void ExtensionsEmoji_Example001()
{
// Example 1
// Section: Extensions / Emoji
//
// The following Markdown:
// This is a test with a :) and a :angry: smiley
//
// Should be rendered as:
// <p>This is a test with a 😃 and a 😠 smiley</p>
Console.WriteLine("Example 1\nSection Extensions / Emoji\n");
TestParser.TestSpec("This is a test with a :) and a :angry: smiley", "<p>This is a test with a 😃 and a 😠 smiley</p>", "emojis|advanced+emojis");
}
// An emoji needs to be preceded by a space:
[Test]
public void ExtensionsEmoji_Example002()
{
// Example 2
// Section: Extensions / Emoji
//
// The following Markdown:
// These are not:) an emoji with a:) x:angry:x
//
// Should be rendered as:
// <p>These are not:) an emoji with a:) x:angry:x</p>
Console.WriteLine("Example 2\nSection Extensions / Emoji\n");
TestParser.TestSpec("These are not:) an emoji with a:) x:angry:x", "<p>These are not:) an emoji with a:) x:angry:x</p>", "emojis|advanced+emojis");
}
// Emoji can be followed by close ponctuation (or any other characters):
[Test]
public void ExtensionsEmoji_Example003()
{
// Example 3
// Section: Extensions / Emoji
//
// The following Markdown:
// We all need :), it makes us :muscle:. (and :ok_hand:).
//
// Should be rendered as:
// <p>We all need 😃, it makes us 💪. (and 👌).</p>
Console.WriteLine("Example 3\nSection Extensions / Emoji\n");
TestParser.TestSpec("We all need :), it makes us :muscle:. (and :ok_hand:).", "<p>We all need 😃, it makes us 💪. (and 👌).</p>", "emojis|advanced+emojis");
}
// Sentences can end with Emoji:
[Test]
public void ExtensionsEmoji_Example004()
{
// Example 4
// Section: Extensions / Emoji
//
// The following Markdown:
// This is a sentence :ok_hand:
// and keeps going to the next line :)
//
// Should be rendered as:
// <p>This is a sentence 👌
// and keeps going to the next line 😃</p>
Console.WriteLine("Example 4\nSection Extensions / Emoji\n");
TestParser.TestSpec("This is a sentence :ok_hand:\nand keeps going to the next line :)", "<p>This is a sentence 👌\nand keeps going to the next line 😃</p>", "emojis|advanced+emojis");
}
}
}

View File

@@ -0,0 +1,152 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Emphasis Extra
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.EmphasisExtra
{
[TestFixture]
public class TestExtensionsStrikethrough
{
// # Extensions
//
// The following additional emphasis are supported:
//
// ## Strikethrough
//
// Allows to strikethrough a span of text by surrounding it by `~~`. The semantic used for the generated HTML is the tag `<del>`.
[Test]
public void ExtensionsStrikethrough_Example001()
{
// Example 1
// Section: Extensions / Strikethrough
//
// The following Markdown:
// The following text ~~is deleted~~
//
// Should be rendered as:
// <p>The following text <del>is deleted</del></p>
Console.WriteLine("Example 1\nSection Extensions / Strikethrough\n");
TestParser.TestSpec("The following text ~~is deleted~~", "<p>The following text <del>is deleted</del></p>", "emphasisextras|advanced");
}
}
[TestFixture]
public class TestExtensionsSuperscriptAndSubscript
{
// ## Superscript and Subscript
//
// Superscripts can be written by surrounding a text by ^ characters; subscripts can be written by surrounding the subscripted text by ~ characters
[Test]
public void ExtensionsSuperscriptAndSubscript_Example002()
{
// Example 2
// Section: Extensions / Superscript and Subscript
//
// The following Markdown:
// H~2~O is a liquid. 2^10^ is 1024
//
// Should be rendered as:
// <p>H<sub>2</sub>O is a liquid. 2<sup>10</sup> is 1024</p>
Console.WriteLine("Example 2\nSection Extensions / Superscript and Subscript\n");
TestParser.TestSpec("H~2~O is a liquid. 2^10^ is 1024", "<p>H<sub>2</sub>O is a liquid. 2<sup>10</sup> is 1024</p>", "emphasisextras|advanced");
}
// Certain punctuation characters are exempted from the rule forbidding them within inline delimiters
[Test]
public void ExtensionsSuperscriptAndSubscript_Example003()
{
// Example 3
// Section: Extensions / Superscript and Subscript
//
// The following Markdown:
// One quintillionth can be expressed as 10^-18^
//
// Daggers^†^ and double-daggers^‡^ can be used to denote notes.
//
// Should be rendered as:
// <p>One quintillionth can be expressed as 10<sup>-18</sup></p>
// <p>Daggers<sup>†</sup> and double-daggers<sup>‡</sup> can be used to denote notes.</p>
Console.WriteLine("Example 3\nSection Extensions / Superscript and Subscript\n");
TestParser.TestSpec("One quintillionth can be expressed as 10^-18^\n\nDaggers^†^ and double-daggers^‡^ can be used to denote notes.", "<p>One quintillionth can be expressed as 10<sup>-18</sup></p>\n<p>Daggers<sup>†</sup> and double-daggers<sup>‡</sup> can be used to denote notes.</p>", "emphasisextras|advanced");
}
}
[TestFixture]
public class TestExtensionsInserted
{
// ## Inserted
//
// Inserted text can be used to specify that a text has been added to a document. The semantic used for the generated HTML is the tag `<ins>`.
[Test]
public void ExtensionsInserted_Example004()
{
// Example 4
// Section: Extensions / Inserted
//
// The following Markdown:
// ++Inserted text++
//
// Should be rendered as:
// <p><ins>Inserted text</ins></p>
Console.WriteLine("Example 4\nSection Extensions / Inserted\n");
TestParser.TestSpec("++Inserted text++", "<p><ins>Inserted text</ins></p>", "emphasisextras|advanced");
}
}
[TestFixture]
public class TestExtensionsMarked
{
// ## Marked
//
// Marked text can be used to specify that a text has been marked in a document. The semantic used for the generated HTML is the tag `<mark>`.
[Test]
public void ExtensionsMarked_Example005()
{
// Example 5
// Section: Extensions / Marked
//
// The following Markdown:
// ==Marked text==
//
// Should be rendered as:
// <p><mark>Marked text</mark></p>
Console.WriteLine("Example 5\nSection Extensions / Marked\n");
TestParser.TestSpec("==Marked text==", "<p><mark>Marked text</mark></p>", "emphasisextras|advanced");
}
}
[TestFixture]
public class TestExtensionsEmphasisOnHtmlEntities
{
// ## Emphasis on Html Entities
[Test]
public void ExtensionsEmphasisOnHtmlEntities_Example006()
{
// Example 6
// Section: Extensions / Emphasis on Html Entities
//
// The following Markdown:
// This is text MyBrand ^&reg;^ and MyTrademark ^&trade;^
// This is text MyBrand^&reg;^ and MyTrademark^&trade;^
// This is text MyBrand~&reg;~ and MyCopyright^&copy;^
//
// Should be rendered as:
// <p>This is text MyBrand <sup>®</sup> and MyTrademark <sup>TM</sup>
// This is text MyBrand<sup>®</sup> and MyTrademark<sup>TM</sup>
// This is text MyBrand<sub>®</sub> and MyCopyright<sup>©</sup></p>
Console.WriteLine("Example 6\nSection Extensions / Emphasis on Html Entities\n");
TestParser.TestSpec("This is text MyBrand ^&reg;^ and MyTrademark ^&trade;^\nThis is text MyBrand^&reg;^ and MyTrademark^&trade;^\nThis is text MyBrand~&reg;~ and MyCopyright^&copy;^", "<p>This is text MyBrand <sup>®</sup> and MyTrademark <sup>TM</sup>\nThis is text MyBrand<sup>®</sup> and MyTrademark<sup>TM</sup>\nThis is text MyBrand<sub>®</sub> and MyCopyright<sup>©</sup></p>", "emphasisextras|advanced");
}
}
}

View File

@@ -0,0 +1,91 @@
// Generated: 21. 01. 2019 14:42:25
// --------------------------------
// Figures, Footers and Cites
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.FiguresFootersAndCites
{
[TestFixture]
public class TestExtensionsFigures
{
// # Extensions
//
// The following the figure extension:
//
// ## Figures
//
// A figure can be defined by using a pattern equivalent to a fenced code block but with the character `^`
[Test]
public void ExtensionsFigures_Example001()
{
// Example 1
// Section: Extensions / Figures
//
// The following Markdown:
// ^^^
// This is a figure
// ^^^ This is a *caption*
//
// Should be rendered as:
// <figure>
// <p>This is a figure</p>
// <figcaption>This is a <em>caption</em></figcaption>
// </figure>
Console.WriteLine("Example 1\nSection Extensions / Figures\n");
TestParser.TestSpec("^^^\nThis is a figure\n^^^ This is a *caption*", "<figure>\n<p>This is a figure</p>\n<figcaption>This is a <em>caption</em></figcaption>\n</figure>", "figures+footers+citations|advanced");
}
}
[TestFixture]
public class TestExtensionsFooters
{
// ## Footers
//
// A footer equivalent to a block quote parsing but starts with double character ^^
[Test]
public void ExtensionsFooters_Example002()
{
// Example 2
// Section: Extensions / Footers
//
// The following Markdown:
// ^^ This is a footer
// ^^ multi-line
//
// Should be rendered as:
// <footer>This is a footer
// multi-line</footer>
Console.WriteLine("Example 2\nSection Extensions / Footers\n");
TestParser.TestSpec("^^ This is a footer\n^^ multi-line", "<footer>This is a footer\nmulti-line</footer>", "figures+footers+citations|advanced");
}
}
[TestFixture]
public class TestExtensionsCite
{
// ## Cite
//
// A cite is working like an emphasis but using the double character ""
[Test]
public void ExtensionsCite_Example003()
{
// Example 3
// Section: Extensions / Cite
//
// The following Markdown:
// This is a ""citation of someone""
//
// Should be rendered as:
// <p>This is a <cite>citation of someone</cite></p>
Console.WriteLine("Example 3\nSection Extensions / Cite\n");
TestParser.TestSpec("This is a \"\"citation of someone\"\"", "<p>This is a <cite>citation of someone</cite></p>", "figures+footers+citations|advanced");
}
}
}

View File

@@ -0,0 +1,193 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Footnotes
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.Footnotes
{
[TestFixture]
public class TestExtensionsFootnotes
{
// # Extensions
//
// This section describes the different extensions supported:
//
// ## Footnotes
//
// Allows footnotes using the following syntax (taken from pandoc example):
[Test]
public void ExtensionsFootnotes_Example001()
{
// Example 1
// Section: Extensions / Footnotes
//
// The following Markdown:
// Here is a footnote reference,[^1] and another.[^longnote]
//
// This is another reference to [^1]
//
// [^1]: Here is the footnote.
//
// And another reference to [^longnote]
//
// [^longnote]: Here's one with multiple blocks.
//
// Subsequent paragraphs are indented to show that they
// belong to the previous footnote.
//
// > This is a block quote
// > Inside a footnote
//
// { some.code }
//
// The whole paragraph can be indented, or just the first
// line. In this way, multi-paragraph footnotes work like
// multi-paragraph list items.
//
// This paragraph won't be part of the note, because it
// isn't indented.
//
// Should be rendered as:
// <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>
// <p>This is another reference to <a id="fnref:2" href="#fn:1" class="footnote-ref"><sup>1</sup></a></p>
// <p>And another reference to <a id="fnref:4" href="#fn:2" class="footnote-ref"><sup>2</sup></a></p>
// <p>This paragraph won't be part of the note, because it
// isn't indented.</p>
// <div class="footnotes">
// <hr />
// <ol>
// <li id="fn:1">
// <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>
// </li>
// <li id="fn:2">
// <p>Here's one with multiple blocks.</p>
// <p>Subsequent paragraphs are indented to show that they
// belong to the previous footnote.</p>
// <blockquote>
// <p>This is a block quote
// Inside a footnote</p>
// </blockquote>
// <pre><code>{ some.code }
// </code></pre>
// <p>The whole paragraph can be indented, or just the first
// line. In this way, multi-paragraph footnotes work like
// multi-paragraph list items.<a href="#fnref:3" class="footnote-back-ref">&#8617;</a><a href="#fnref:4" class="footnote-back-ref">&#8617;</a></p>
// </li>
// </ol>
// </div>
Console.WriteLine("Example 1\nSection Extensions / Footnotes\n");
TestParser.TestSpec("Here is a footnote reference,[^1] and another.[^longnote]\n\nThis is another reference to [^1]\n\n[^1]: Here is the footnote.\n\nAnd another reference to [^longnote]\n\n[^longnote]: Here's one with multiple blocks.\n\n Subsequent paragraphs are indented to show that they\nbelong to the previous footnote.\n\n > This is a block quote\n > Inside a footnote\n\n { some.code }\n\n The whole paragraph can be indented, or just the first\n line. In this way, multi-paragraph footnotes work like\n multi-paragraph list items.\n\nThis paragraph won't be part of the note, because it\nisn't indented.", "<p>Here is a footnote reference,<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a> and another.<a id=\"fnref:3\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a></p>\n<p>This is another reference to <a id=\"fnref:2\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a></p>\n<p>And another reference to <a id=\"fnref:4\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a></p>\n<p>This paragraph won't be part of the note, because it\nisn't indented.</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Here is the footnote.<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a><a href=\"#fnref:2\" class=\"footnote-back-ref\">&#8617;</a></p>\n</li>\n<li id=\"fn:2\">\n<p>Here's one with multiple blocks.</p>\n<p>Subsequent paragraphs are indented to show that they\nbelong to the previous footnote.</p>\n<blockquote>\n<p>This is a block quote\nInside a footnote</p>\n</blockquote>\n<pre><code>{ some.code }\n</code></pre>\n<p>The whole paragraph can be indented, or just the first\nline. In this way, multi-paragraph footnotes work like\nmulti-paragraph list items.<a href=\"#fnref:3\" class=\"footnote-back-ref\">&#8617;</a><a href=\"#fnref:4\" class=\"footnote-back-ref\">&#8617;</a></p>\n</li>\n</ol>\n</div>", "footnotes|advanced");
}
// Check with mulitple consecutive footnotes:
[Test]
public void ExtensionsFootnotes_Example002()
{
// Example 2
// Section: Extensions / Footnotes
//
// The following Markdown:
// Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].
//
// [^1]: Footnote 1 text
//
// [^2]: Footnote 2 text
//
// a
//
// [^3]: Footnote 3 text
//
// [^4]: Footnote 4 text
//
// Should be rendered as:
// <p>Here is a footnote<a id="fnref:1" href="#fn:1" class="footnote-ref"><sup>1</sup></a>. And another one<a id="fnref:2" href="#fn:2" class="footnote-ref"><sup>2</sup></a>. And a third one<a id="fnref:3" href="#fn:3" class="footnote-ref"><sup>3</sup></a>. And a fourth<a id="fnref:4" href="#fn:4" class="footnote-ref"><sup>4</sup></a>.</p>
// <p>a</p>
// <div class="footnotes">
// <hr />
// <ol>
// <li id="fn:1">
// <p>Footnote 1 text<a href="#fnref:1" class="footnote-back-ref">&#8617;</a></p></li>
// <li id="fn:2">
// <p>Footnote 2 text<a href="#fnref:2" class="footnote-back-ref">&#8617;</a></p></li>
// <li id="fn:3">
// <p>Footnote 3 text<a href="#fnref:3" class="footnote-back-ref">&#8617;</a></p></li>
// <li id="fn:4">
// <p>Footnote 4 text<a href="#fnref:4" class="footnote-back-ref">&#8617;</a></p></li>
// </ol>
// </div>
Console.WriteLine("Example 2\nSection Extensions / Footnotes\n");
TestParser.TestSpec("Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].\n\n[^1]: Footnote 1 text\n\n[^2]: Footnote 2 text\n\na\n\n[^3]: Footnote 3 text\n\n[^4]: Footnote 4 text", "<p>Here is a footnote<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a>. And another one<a id=\"fnref:2\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a>. And a third one<a id=\"fnref:3\" href=\"#fn:3\" class=\"footnote-ref\"><sup>3</sup></a>. And a fourth<a id=\"fnref:4\" href=\"#fn:4\" class=\"footnote-ref\"><sup>4</sup></a>.</p>\n<p>a</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Footnote 1 text<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:2\">\n<p>Footnote 2 text<a href=\"#fnref:2\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:3\">\n<p>Footnote 3 text<a href=\"#fnref:3\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:4\">\n<p>Footnote 4 text<a href=\"#fnref:4\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n</ol>\n</div>", "footnotes|advanced");
}
// Another test with consecutive footnotes without a blank line separator:
[Test]
public void ExtensionsFootnotes_Example003()
{
// Example 3
// Section: Extensions / Footnotes
//
// The following Markdown:
// Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].
//
// [^1]: Footnote 1 text
// [^2]: Footnote 2 text
// [^3]: Footnote 3 text
// [^4]: Footnote 4 text
//
// Should be rendered as:
// <p>Here is a footnote<a id="fnref:1" href="#fn:1" class="footnote-ref"><sup>1</sup></a>. And another one<a id="fnref:2" href="#fn:2" class="footnote-ref"><sup>2</sup></a>. And a third one<a id="fnref:3" href="#fn:3" class="footnote-ref"><sup>3</sup></a>. And a fourth<a id="fnref:4" href="#fn:4" class="footnote-ref"><sup>4</sup></a>.</p>
// <div class="footnotes">
// <hr />
// <ol>
// <li id="fn:1">
// <p>Footnote 1 text<a href="#fnref:1" class="footnote-back-ref">&#8617;</a></p></li>
// <li id="fn:2">
// <p>Footnote 2 text<a href="#fnref:2" class="footnote-back-ref">&#8617;</a></p></li>
// <li id="fn:3">
// <p>Footnote 3 text<a href="#fnref:3" class="footnote-back-ref">&#8617;</a></p></li>
// <li id="fn:4">
// <p>Footnote 4 text<a href="#fnref:4" class="footnote-back-ref">&#8617;</a></p></li>
// </ol>
// </div>
Console.WriteLine("Example 3\nSection Extensions / Footnotes\n");
TestParser.TestSpec("Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].\n\n[^1]: Footnote 1 text\n[^2]: Footnote 2 text\n[^3]: Footnote 3 text\n[^4]: Footnote 4 text", "<p>Here is a footnote<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a>. And another one<a id=\"fnref:2\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a>. And a third one<a id=\"fnref:3\" href=\"#fn:3\" class=\"footnote-ref\"><sup>3</sup></a>. And a fourth<a id=\"fnref:4\" href=\"#fn:4\" class=\"footnote-ref\"><sup>4</sup></a>.</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Footnote 1 text<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:2\">\n<p>Footnote 2 text<a href=\"#fnref:2\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:3\">\n<p>Footnote 3 text<a href=\"#fnref:3\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:4\">\n<p>Footnote 4 text<a href=\"#fnref:4\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n</ol>\n</div>", "footnotes|advanced");
}
// A footnote link inside a list should work as well:
[Test]
public void ExtensionsFootnotes_Example004()
{
// Example 4
// Section: Extensions / Footnotes
//
// The following Markdown:
// - abc
// - def[^1]
//
// [^1]: Here is the footnote.
//
// Should be rendered as:
// <ul>
// <li>abc</li>
// <li>def<a id="fnref:1" href="#fn:1" class="footnote-ref"><sup>1</sup></a></li>
// </ul>
// <div class="footnotes">
// <hr />
// <ol>
// <li id="fn:1">
// <p>Here is the footnote.<a href="#fnref:1" class="footnote-back-ref">&#8617;</a></p></li>
// </ol>
// </div>
Console.WriteLine("Example 4\nSection Extensions / Footnotes\n");
TestParser.TestSpec("- abc\n- def[^1]\n\n[^1]: Here is the footnote.", "<ul>\n<li>abc</li>\n<li>def<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a></li>\n</ul>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Here is the footnote.<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n</ol>\n</div>", "footnotes|advanced");
}
}
}

View File

@@ -0,0 +1,83 @@
// Generated: 21. 01. 2019 15:02:43
// --------------------------------
// Generic Attributes
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.GenericAttributes
{
[TestFixture]
public class TestExtensionsGenericAttributes
{
// # Extensions
//
// This section describes the different extensions supported:
//
// ## Generic Attributes
//
// Attributes can be attached to:
// - The previous inline element if the previous element is not a literal
// - The next block if the current block is a paragraph and the attributes is the only inline present in the paragraph
// - Or the current block
//
// Attributes can be of 3 kinds:
//
// - An id element, starting by `#` that will be used to set the `id` property of the HTML element
// - A class element, starting by `.` that will be appended to the CSS class property of the HTML element
// - a `name=value` or `name="value"` that will be appended as an attribute of the HTML element
//
// The following shows that attributes is attached to the current block or the previous inline:
[Test]
public void ExtensionsGenericAttributes_Example001()
{
// Example 1
// Section: Extensions / Generic Attributes
//
// The following Markdown:
// # This is a heading with an an attribute{#heading-link}
//
// # This is a heading # {#heading-link2}
//
// [This is a link](http://google.com){#a-link .myclass data-lang=fr data-value="This is a value"}
//
// This is a heading{#heading-link2}
// -----------------
//
// This is a paragraph with an attached attributes {#myparagraph attached-bool-property attached-bool-property2}
//
// Should be rendered as:
// <h1 id="heading-link">This is a heading with an an attribute</h1>
// <h1 id="heading-link2">This is a heading</h1>
// <p><a href="http://google.com" id="a-link" class="myclass" data-lang="fr" data-value="This is a value">This is a link</a></p>
// <h2 id="heading-link2">This is a heading</h2>
// <p id="myparagraph" attached-bool-property="" attached-bool-property2="">This is a paragraph with an attached attributes </p>
Console.WriteLine("Example 1\nSection Extensions / Generic Attributes\n");
TestParser.TestSpec("# This is a heading with an an attribute{#heading-link}\n\n# This is a heading # {#heading-link2}\n\n[This is a link](http://google.com){#a-link .myclass data-lang=fr data-value=\"This is a value\"}\n\nThis is a heading{#heading-link2}\n-----------------\n\nThis is a paragraph with an attached attributes {#myparagraph attached-bool-property attached-bool-property2}", "<h1 id=\"heading-link\">This is a heading with an an attribute</h1>\n<h1 id=\"heading-link2\">This is a heading</h1>\n<p><a href=\"http://google.com\" id=\"a-link\" class=\"myclass\" data-lang=\"fr\" data-value=\"This is a value\">This is a link</a></p>\n<h2 id=\"heading-link2\">This is a heading</h2>\n<p id=\"myparagraph\" attached-bool-property=\"\" attached-bool-property2=\"\">This is a paragraph with an attached attributes </p>", "attributes|advanced");
}
// The following shows that attributes can be attached to the next block if they are used inside a single line just preceding the block (and preceded by a blank line or beginning of a block container):
[Test]
public void ExtensionsGenericAttributes_Example002()
{
// Example 2
// Section: Extensions / Generic Attributes
//
// The following Markdown:
// {#fenced-id .fenced-class}
// ~~~
// This is a fenced with attached attributes
// ~~~
//
// Should be rendered as:
// <pre><code id="fenced-id" class="fenced-class">This is a fenced with attached attributes
// </code></pre>
Console.WriteLine("Example 2\nSection Extensions / Generic Attributes\n");
TestParser.TestSpec("{#fenced-id .fenced-class}\n~~~\nThis is a fenced with attached attributes\n~~~ ", "<pre><code id=\"fenced-id\" class=\"fenced-class\">This is a fenced with attached attributes\n</code></pre>", "attributes|advanced");
}
}
}

View File

@@ -33,7 +33,7 @@ This is a paragraph with an attached attributes {#myparagraph attached-bool-prop
<h1 id="heading-link2">This is a heading</h1>
<p><a href="http://google.com" id="a-link" class="myclass" data-lang="fr" data-value="This is a value">This is a link</a></p>
<h2 id="heading-link2">This is a heading</h2>
<p id="myparagraph" attached-bool-property attached-bool-property2>This is a paragraph with an attached attributes </p>
<p id="myparagraph" attached-bool-property="" attached-bool-property2="">This is a paragraph with an attached attributes </p>
````````````````````````````````
The following shows that attributes can be attached to the next block if they are used inside a single line just preceding the block (and preceded by a blank line or beginning of a block container):

View File

@@ -0,0 +1,234 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Globalization
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.Globalization
{
[TestFixture]
public class TestExtensionsGlobalization
{
// # Extensions
//
// This section describes the different extensions supported:
//
// ## Globalization
// Adds support for RTL content by adding `dir="rtl"` and `align="right` attributes to the appropriate html elements. Left to right text is not affected by this extension.
//
// Whether a markdown block is marked as RTL or not is determined by the [first strong character](https://en.wikipedia.org/wiki/Bi-directional_text#Strong_characters) of the block.
//
// **Note**: You might need to add `<meta charset="UTF-8">` to the head of the html file to be able to see the result correctly.
//
// Headings and block quotes:
[Test]
public void ExtensionsGlobalization_Example001()
{
// Example 1
// Section: Extensions / Globalization
//
// The following Markdown:
// # Fruits
// In botany, a [fruit](https://en.wikipedia.org/wiki/Fruit) is the seed-bearing structure in flowering plants (also known as angiosperms) formed from the ovary after flowering.
//
// > Fruits are good for health
// -- Anonymous
//
// # میوە
// [میوە](https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95) یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو
//
// > میوە بۆ تەندروستی باشە
// -- نەزانراو
//
// Should be rendered as:
// <h1 id="fruits">Fruits</h1>
// <p>In botany, a <a href="https://en.wikipedia.org/wiki/Fruit">fruit</a> is the seed-bearing structure in flowering plants (also known as angiosperms) formed from the ovary after flowering.</p>
// <blockquote>
// <p>Fruits are good for health
// -- Anonymous</p>
// </blockquote>
// <h1 id="section" dir="rtl">میوە</h1>
// <p dir="rtl"><a href="https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95" dir="rtl">میوە</a> یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو</p>
// <blockquote dir="rtl">
// <p dir="rtl">میوە بۆ تەندروستی باشە
// -- نەزانراو</p>
// </blockquote>
Console.WriteLine("Example 1\nSection Extensions / Globalization\n");
TestParser.TestSpec("# Fruits\nIn botany, a [fruit](https://en.wikipedia.org/wiki/Fruit) is the seed-bearing structure in flowering plants (also known as angiosperms) formed from the ovary after flowering.\n\n> Fruits are good for health\n-- Anonymous\n\n# میوە\n[میوە](https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95) یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو\n\n> میوە بۆ تەندروستی باشە\n-- نەزانراو", "<h1 id=\"fruits\">Fruits</h1>\n<p>In botany, a <a href=\"https://en.wikipedia.org/wiki/Fruit\">fruit</a> is the seed-bearing structure in flowering plants (also known as angiosperms) formed from the ovary after flowering.</p>\n<blockquote>\n<p>Fruits are good for health\n-- Anonymous</p>\n</blockquote>\n<h1 id=\"section\" dir=\"rtl\">میوە</h1>\n<p dir=\"rtl\"><a href=\"https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95\" dir=\"rtl\">میوە</a> یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو</p>\n<blockquote dir=\"rtl\">\n<p dir=\"rtl\">میوە بۆ تەندروستی باشە\n-- نەزانراو</p>\n</blockquote>", "globalization+advanced+emojis");
}
// Lists:
[Test]
public void ExtensionsGlobalization_Example002()
{
// Example 2
// Section: Extensions / Globalization
//
// The following Markdown:
// ## Types of fruits
// - Berries
// - Strawberry
// - kiwifruit
// - Citrus
// - Orange
// - Lemon
//
// ## Examples of fruits :yum:
// 1. Apple
// 2. Banana
// 3. Orange
//
// ## Grocery List
// - [X] 􏿽 Watermelon
// - [X] Apricot
// - [ ] Fig
//
// ## نموونەی میوە :yum:
// 1. ? سێو
// 2. 5 مۆز
// 3. 􏿽 پرتەقاڵ
//
// ## جۆرەکانی میوە
// - توو
// - فڕاولە
// - کیوی
// - مزرەمەنی
// - پڕتەقاڵ
// - لیمۆ
//
// ## لیستی کڕین
// - [X] شووتی
// - [X] قەیسی
// - [ ] هەنجیر
//
// Should be rendered as:
// <h2 id="types-of-fruits">Types of fruits</h2>
// <ul>
// <li>Berries
// <ul>
// <li>Strawberry</li>
// <li>kiwifruit</li>
// </ul>
// </li>
// <li>Citrus
// <ul>
// <li>Orange</li>
// <li>Lemon</li>
// </ul>
// </li>
// </ul>
// <h2 id="examples-of-fruits">Examples of fruits 😋</h2>
// <ol>
// <li>Apple</li>
// <li>Banana</li>
// <li>Orange</li>
// </ol>
// <h2 id="grocery-list">Grocery List</h2>
// <ul class="contains-task-list">
// <li class="task-list-item"><input disabled="disabled" type="checkbox" checked="checked" /> 􏿽 Watermelon</li>
// <li class="task-list-item"><input disabled="disabled" type="checkbox" checked="checked" /> Apricot</li>
// <li class="task-list-item"><input disabled="disabled" type="checkbox" /> Fig</li>
// </ul>
// <h2 id="section" dir="rtl">نموونەی میوە 😋</h2>
// <ol dir="rtl">
// <li>? سێو</li>
// <li>5 مۆز</li>
// <li>􏿽 پرتەقاڵ</li>
// </ol>
// <h2 id="section-1" dir="rtl">جۆرەکانی میوە</h2>
// <ul dir="rtl">
// <li>توو
// <ul dir="rtl">
// <li>فڕاولە</li>
// <li>کیوی</li>
// </ul>
// </li>
// <li>مزرەمەنی
// <ul dir="rtl">
// <li>پڕتەقاڵ</li>
// <li>لیمۆ</li>
// </ul>
// </li>
// </ul>
// <h2 id="section-2" dir="rtl">لیستی کڕین</h2>
// <ul class="contains-task-list" dir="rtl">
// <li class="task-list-item"><input disabled="disabled" type="checkbox" checked="checked" /> شووتی</li>
// <li class="task-list-item"><input disabled="disabled" type="checkbox" checked="checked" /> قەیسی</li>
// <li class="task-list-item"><input disabled="disabled" type="checkbox" /> هەنجیر</li>
// </ul>
Console.WriteLine("Example 2\nSection Extensions / Globalization\n");
TestParser.TestSpec("## Types of fruits\n- Berries\n - Strawberry\n - kiwifruit\n- Citrus\n - Orange\n - Lemon\n\n## Examples of fruits :yum:\n1. Apple\n2. Banana\n3. Orange\n\n## Grocery List\n- [X] 􏿽 Watermelon\n- [X] Apricot\n- [ ] Fig \n\n## نموونەی میوە :yum:\n1. ? سێو\n2. 5 مۆز \n3. 􏿽 پرتەقاڵ\n\n## جۆرەکانی میوە\n- توو\n - فڕاولە\n - کیوی\n- مزرەمەنی\n - پڕتەقاڵ\n - لیمۆ\n\n## لیستی کڕین\n- [X] شووتی\n- [X] قەیسی\n- [ ] هەنجیر", "<h2 id=\"types-of-fruits\">Types of fruits</h2>\n<ul>\n<li>Berries\n<ul>\n<li>Strawberry</li>\n<li>kiwifruit</li>\n</ul>\n</li>\n<li>Citrus\n<ul>\n<li>Orange</li>\n<li>Lemon</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"examples-of-fruits\">Examples of fruits 😋</h2>\n<ol>\n<li>Apple</li>\n<li>Banana</li>\n<li>Orange</li>\n</ol>\n<h2 id=\"grocery-list\">Grocery List</h2>\n<ul class=\"contains-task-list\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> 􏿽 Watermelon</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> Apricot</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> Fig</li>\n</ul>\n<h2 id=\"section\" dir=\"rtl\">نموونەی میوە 😋</h2>\n<ol dir=\"rtl\">\n<li>? سێو</li>\n<li>5 مۆز</li>\n<li>􏿽 پرتەقاڵ</li>\n</ol>\n<h2 id=\"section-1\" dir=\"rtl\">جۆرەکانی میوە</h2>\n<ul dir=\"rtl\">\n<li>توو\n<ul dir=\"rtl\">\n<li>فڕاولە</li>\n<li>کیوی</li>\n</ul>\n</li>\n<li>مزرەمەنی\n<ul dir=\"rtl\">\n<li>پڕتەقاڵ</li>\n<li>لیمۆ</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"section-2\" dir=\"rtl\">لیستی کڕین</h2>\n<ul class=\"contains-task-list\" dir=\"rtl\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> شووتی</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> قەیسی</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> هەنجیر</li>\n</ul>", "globalization+advanced+emojis");
}
// Tables:
[Test]
public void ExtensionsGlobalization_Example003()
{
// Example 3
// Section: Extensions / Globalization
//
// The following Markdown:
// Nuitrion |Apple | Oranges
// --|-- | --
// Calories|52|47
// Sugar|10g|9g
//
// پێکهاتە |سێو | پڕتەقاڵ
// --|-- | --
// کالۆری|٥٢|٤٧
// شەکر| ١٠گ|٩گ
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>Nuitrion</th>
// <th>Apple</th>
// <th>Oranges</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>Calories</td>
// <td>52</td>
// <td>47</td>
// </tr>
// <tr>
// <td>Sugar</td>
// <td>10g</td>
// <td>9g</td>
// </tr>
// </tbody>
// </table>
// <table dir="rtl" align="right">
// <thead>
// <tr>
// <th>پێکهاتە</th>
// <th>سێو</th>
// <th>پڕتەقاڵ</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>کالۆری</td>
// <td>٥٢</td>
// <td>٤٧</td>
// </tr>
// <tr>
// <td>شەکر</td>
// <td>١٠گ</td>
// <td>٩گ</td>
// </tr>
// </tbody>
// </table>
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");
}
}
}

View File

@@ -0,0 +1,399 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Grid Tables
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.GridTables
{
[TestFixture]
public class TestExtensionsGridTable
{
// # Extensions
//
// This section describes the different extensions supported:
//
// ## Grid Table
//
// A grid table allows to have multiple lines per cells and allows to span cells over multiple columns. The following shows a simple grid table
//
// ```
// +---------+---------+
// | Header | Header |
// | Column1 | Column2 |
// +=========+=========+
// | 1. ab | > This is a quote
// | 2. cde | > For the second column
// | 3. f |
// +---------+---------+
// | Second row spanning
// | on two columns
// +---------+---------+
// | Back | |
// | to | |
// | one | |
// | column | |
// ```
//
// **Rule #1**
// The first line of a grid table must a **row separator**. It must start with the column separator character `+` used to separate columns in a row separator. Each column separator is:
// - starting by optional spaces
// - followed by an optional `:` to specify left align, followed by optional spaces
// - followed by a sequence of at least one `-` character, followed by optional spaces
// - followed by an optional `:` to specify right align (or center align if left align is also defined)
// - ending by optional spaces
//
// The first row separator must be followed by a *regular row*. A regular row must start with the character `|` that is starting at the same position than the column separator `+` of the first row separator.
//
//
// The following is a valid row separator
[Test]
public void ExtensionsGridTable_Example001()
{
// Example 1
// Section: Extensions / Grid Table
//
// The following Markdown:
// +---------+---------+
// | This is | a table |
//
// Should be rendered as:
// <table>
// <col style="width:50%">
// <col style="width:50%">
// <tbody>
// <tr>
// <td>This is</td>
// <td>a table</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 1\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---------+---------+\n| This is | a table |", "<table>\n<col style=\"width:50%\">\n<col style=\"width:50%\">\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
}
// The following is not a valid row separator
[Test]
public void ExtensionsGridTable_Example002()
{
// Example 2
// Section: Extensions / Grid Table
//
// The following Markdown:
// |-----xxx----+---------+
// | This is | not a table
//
// Should be rendered as:
// <p>|-----xxx----+---------+
// | This is | not a table</p>
Console.WriteLine("Example 2\nSection Extensions / Grid Table\n");
TestParser.TestSpec("|-----xxx----+---------+\n| This is | not a table", "<p>|-----xxx----+---------+\n| This is | not a table</p>", "gridtables|advanced");
}
// **Rule #2**
// A regular row can continue a previous regular row when column separator `|` are positioned at the same position than the previous line. If they are positioned at the same location, the column may span over multiple columns:
[Test]
public void ExtensionsGridTable_Example003()
{
// Example 3
// Section: Extensions / Grid Table
//
// The following Markdown:
// +---------+---------+---------+
// | Col1 | Col2 | Col3 |
// | Col1a | Col2a | Col3a |
// | Col1b | Col3b |
// | Col1c |
//
// Should be rendered as:
// <table>
// <col style="width:33.33%">
// <col style="width:33.33%">
// <col style="width:33.33%">
// <tbody>
// <tr>
// <td>Col1
// Col1a</td>
// <td>Col2
// Col2a</td>
// <td>Col3
// Col3a</td>
// </tr>
// <tr>
// <td colspan="2">Col1b</td>
// <td>Col3b</td>
// </tr>
// <tr>
// <td colspan="3">Col1c</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 3\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---------+---------+---------+\n| Col1 | Col2 | Col3 |\n| Col1a | Col2a | Col3a |\n| Col1b | Col3b |\n| Col1c |", "<table>\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<tbody>\n<tr>\n<td>Col1\nCol1a</td>\n<td>Col2\nCol2a</td>\n<td>Col3\nCol3a</td>\n</tr>\n<tr>\n<td colspan=\"2\">Col1b</td>\n<td>Col3b</td>\n</tr>\n<tr>\n<td colspan=\"3\">Col1c</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
}
// A row header is separated using `+========+` instead of `+---------+`:
[Test]
public void ExtensionsGridTable_Example004()
{
// Example 4
// Section: Extensions / Grid Table
//
// The following Markdown:
// +---------+---------+
// | This is | a table |
// +=========+=========+
//
// Should be rendered as:
// <table>
// <col style="width:50%">
// <col style="width:50%">
// <thead>
// <tr>
// <th>This is</th>
// <th>a table</th>
// </tr>
// </thead>
// </table>
Console.WriteLine("Example 4\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---------+---------+\n| This is | a table |\n+=========+=========+", "<table>\n<col style=\"width:50%\">\n<col style=\"width:50%\">\n<thead>\n<tr>\n<th>This is</th>\n<th>a table</th>\n</tr>\n</thead>\n</table>", "gridtables|advanced");
}
// The last column separator `|` may be omitted:
[Test]
public void ExtensionsGridTable_Example005()
{
// Example 5
// Section: Extensions / Grid Table
//
// The following Markdown:
// +---------+---------+
// | This is | a table with a longer text in the second column
//
// Should be rendered as:
// <table>
// <col style="width:50%">
// <col style="width:50%">
// <tbody>
// <tr>
// <td>This is</td>
// <td>a table with a longer text in the second column</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 5\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---------+---------+\n| This is | a table with a longer text in the second column", "<table>\n<col style=\"width:50%\">\n<col style=\"width:50%\">\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table with a longer text in the second column</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
}
// The respective width of the columns are calculated from the ratio between the total size of the first table row without counting the `+`: `+----+--------+----+` would be divided between:
//
// Total size is : 16
//
// - `----` -> 4
// - `--------` -> 8
// - `----` -> 4
//
// So the width would be 4/16 = 25%, 8/16 = 50%, 4/16 = 25%
[Test]
public void ExtensionsGridTable_Example006()
{
// Example 6
// Section: Extensions / Grid Table
//
// The following Markdown:
// +----+--------+----+
// | A | B C D | E |
// +----+--------+----+
//
// Should be rendered as:
// <table>
// <col style="width:25%">
// <col style="width:50%">
// <col style="width:25%">
// <tbody>
// <tr>
// <td>A</td>
// <td>B C D</td>
// <td>E</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 6\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+----+--------+----+\n| A | B C D | E |\n+----+--------+----+", "<table>\n<col style=\"width:25%\">\n<col style=\"width:50%\">\n<col style=\"width:25%\">\n<tbody>\n<tr>\n<td>A</td>\n<td>B C D</td>\n<td>E</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
}
// Alignment might be specified on the first row using the character `:`:
[Test]
public void ExtensionsGridTable_Example007()
{
// Example 7
// Section: Extensions / Grid Table
//
// The following Markdown:
// +-----+:---:+-----+
// | A | B | C |
// +-----+-----+-----+
//
// Should be rendered as:
// <table>
// <col style="width:33.33%">
// <col style="width:33.33%">
// <col style="width:33.33%">
// <tbody>
// <tr>
// <td>A</td>
// <td style="text-align: center;">B</td>
// <td>C</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 7\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+-----+:---:+-----+\n| A | B | C |\n+-----+-----+-----+", "<table>\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<tbody>\n<tr>\n<td>A</td>\n<td style=\"text-align: center;\">B</td>\n<td>C</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
}
// A grid table may have cells spanning both columns and rows:
[Test]
public void ExtensionsGridTable_Example008()
{
// Example 8
// Section: Extensions / Grid Table
//
// The following Markdown:
// +---+---+---+
// | AAAAA | B |
// +---+---+ B +
// | D | E | B |
// + D +---+---+
// | D | CCCCC |
// +---+---+---+
//
// Should be rendered as:
// <table>
// <col style="width:33.33%">
// <col style="width:33.33%">
// <col style="width:33.33%">
// <tbody>
// <tr>
// <td colspan="2">AAAAA</td>
// <td rowspan="2">B
// B
// B</td>
// </tr>
// <tr>
// <td rowspan="2">D
// D
// D</td>
// <td>E</td>
// </tr>
// <tr>
// <td colspan="2">CCCCC</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 8\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+---+---+ B +\n| D | E | B |\n+ D +---+---+\n| D | CCCCC |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<tbody>\n<tr>\n<td colspan=\"2\">AAAAA</td>\n<td rowspan=\"2\">B\nB\nB</td>\n</tr>\n<tr>\n<td rowspan=\"2\">D\nD\nD</td>\n<td>E</td>\n</tr>\n<tr>\n<td colspan=\"2\">CCCCC</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
}
// A grid table may have cells with both colspan and rowspan:
[Test]
public void ExtensionsGridTable_Example009()
{
// Example 9
// Section: Extensions / Grid Table
//
// The following Markdown:
// +---+---+---+
// | AAAAA | B |
// + AAAAA +---+
// | AAAAA | C |
// +---+---+---+
// | D | E | F |
// +---+---+---+
//
// Should be rendered as:
// <table>
// <col style="width:33.33%">
// <col style="width:33.33%">
// <col style="width:33.33%">
// <tbody>
// <tr>
// <td colspan="2" rowspan="2">AAAAA
// AAAAA
// AAAAA</td>
// <td>B</td>
// </tr>
// <tr>
// <td>C</td>
// </tr>
// <tr>
// <td>D</td>
// <td>E</td>
// <td>F</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 9\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+ AAAAA +---+\n| AAAAA | C |\n+---+---+---+\n| D | E | F |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<tbody>\n<tr>\n<td colspan=\"2\" rowspan=\"2\">AAAAA\nAAAAA\nAAAAA</td>\n<td>B</td>\n</tr>\n<tr>\n<td>C</td>\n</tr>\n<tr>\n<td>D</td>\n<td>E</td>\n<td>F</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
}
// A grid table may not have irregularly shaped cells:
[Test]
public void ExtensionsGridTable_Example010()
{
// Example 10
// Section: Extensions / Grid Table
//
// The following Markdown:
// +---+---+---+
// | AAAAA | B |
// + A +---+ B +
// | A | C | B |
// +---+---+---+
// | DDDDD | E |
// +---+---+---+
//
// Should be rendered as:
// <p>+---+---+---+
// | AAAAA | B |
// + A +---+ B +
// | A | C | B |
// +---+---+---+
// | DDDDD | E |
// +---+---+---+</p>
Console.WriteLine("Example 10\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+ A +---+ B +\n| A | C | B |\n+---+---+---+\n| DDDDD | E |\n+---+---+---+", "<p>+---+---+---+\n| AAAAA | B |\n+ A +---+ B +\n| A | C | B |\n+---+---+---+\n| DDDDD | E |\n+---+---+---+</p>", "gridtables|advanced");
}
// An empty `+` on a line should result in a simple empty list output:
[Test]
public void ExtensionsGridTable_Example011()
{
// Example 11
// Section: Extensions / Grid Table
//
// The following Markdown:
// +
//
// Should be rendered as:
// <ul>
// <li></li>
// </ul>
Console.WriteLine("Example 11\nSection Extensions / Grid Table\n");
TestParser.TestSpec("+", "<ul>\n<li></li>\n</ul>", "gridtables|advanced");
}
}
}

View File

@@ -0,0 +1,40 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Hardline Breaks
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.HardlineBreaks
{
[TestFixture]
public class TestExtensionsHardlineBreak
{
// # Extensions
//
// This section describes the different extensions supported:
//
// ## Hardline break
//
// When this extension is used, a new line in a paragraph block will result in a hardline break `<br>`:
[Test]
public void ExtensionsHardlineBreak_Example001()
{
// Example 1
// Section: Extensions / Hardline break
//
// The following Markdown:
// This is a paragraph
// with a break inside
//
// Should be rendered as:
// <p>This is a paragraph<br />
// with a break inside</p>
Console.WriteLine("Example 1\nSection Extensions / Hardline break\n");
TestParser.TestSpec("This is a paragraph\nwith a break inside", "<p>This is a paragraph<br />\nwith a break inside</p>", "hardlinebreak|advanced+hardlinebreak");
}
}
}

View File

@@ -0,0 +1,180 @@
// Generated: 21. 01. 2019 14:22:45
// --------------------------------
// Jira Links
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.JiraLinks
{
[TestFixture]
public class TestJiraLinks
{
// ## Jira Links
//
// The JiraLinks extension will automatically add links to JIRA issue items within your markdown, e.g. XX-1234. For this to happen, you must configure the extension when adding to the pipeline, e.g.
//
// ```
// var pipeline = new MarkdownPipelineBuilder()
// .UseJiraLinks(new JiraLinkOptions("http://your.company.abc"))
// .Build();
// ```
//
// The rules for detecting a link are:
//
// - The project key must be composed of onre or more capitalised ASCII letter `[A-Z]+`
// - A single hypen `-` must separate the project key and issue number.
// - The issue number is composed of 1 or more digits `[0, 9]+`
// - The reference must be preceeded by either `(` or whitespace or EOF.
// - The reference must be followed by either `)` or whitespace or EOF.
//
// The following are valid examples:
[Test]
public void JiraLinks_Example001()
{
// Example 1
// Section: Jira Links
//
// The following Markdown:
// This is a ABCD-123 issue
//
// Should be rendered as:
// <p>This is a <a href="http://your.company.abc/browse/ABCD-123" target="blank">ABCD-123</a> issue</p>
Console.WriteLine("Example 1\nSection Jira Links\n");
TestParser.TestSpec("This is a ABCD-123 issue", "<p>This is a <a href=\"http://your.company.abc/browse/ABCD-123\" target=\"blank\">ABCD-123</a> issue</p>", "jiralinks");
}
[Test]
public void JiraLinks_Example002()
{
// Example 2
// Section: Jira Links
//
// The following Markdown:
// This is a KIRA-1 issue
//
// Should be rendered as:
// <p>This is a <a href="http://your.company.abc/browse/KIRA-1" target="blank">KIRA-1</a> issue</p>
Console.WriteLine("Example 2\nSection Jira Links\n");
TestParser.TestSpec("This is a KIRA-1 issue", "<p>This is a <a href=\"http://your.company.abc/browse/KIRA-1\" target=\"blank\">KIRA-1</a> issue</p>", "jiralinks");
}
[Test]
public void JiraLinks_Example003()
{
// Example 3
// Section: Jira Links
//
// The following Markdown:
// This is a Z-1 issue
//
// Should be rendered as:
// <p>This is a <a href="http://your.company.abc/browse/Z-1" target="blank">Z-1</a> issue</p>
Console.WriteLine("Example 3\nSection Jira Links\n");
TestParser.TestSpec("This is a Z-1 issue", "<p>This is a <a href=\"http://your.company.abc/browse/Z-1\" target=\"blank\">Z-1</a> issue</p>", "jiralinks");
}
// These are also valid links with `(` and `)`:
[Test]
public void JiraLinks_Example004()
{
// Example 4
// Section: Jira Links
//
// The following Markdown:
// This is a (ABCD-123) issue
//
// Should be rendered as:
// <p>This is a (<a href="http://your.company.abc/browse/ABCD-123" target="blank">ABCD-123</a>) issue</p>
Console.WriteLine("Example 4\nSection Jira Links\n");
TestParser.TestSpec("This is a (ABCD-123) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/ABCD-123\" target=\"blank\">ABCD-123</a>) issue</p>", "jiralinks");
}
[Test]
public void JiraLinks_Example005()
{
// Example 5
// Section: Jira Links
//
// The following Markdown:
// This is a (KIRA-1) issue
//
// Should be rendered as:
// <p>This is a (<a href="http://your.company.abc/browse/KIRA-1" target="blank">KIRA-1</a>) issue</p>
Console.WriteLine("Example 5\nSection Jira Links\n");
TestParser.TestSpec("This is a (KIRA-1) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/KIRA-1\" target=\"blank\">KIRA-1</a>) issue</p>", "jiralinks");
}
[Test]
public void JiraLinks_Example006()
{
// Example 6
// Section: Jira Links
//
// The following Markdown:
// This is a (Z-1) issue
//
// Should be rendered as:
// <p>This is a (<a href="http://your.company.abc/browse/Z-1" target="blank">Z-1</a>) issue</p>
Console.WriteLine("Example 6\nSection Jira Links\n");
TestParser.TestSpec("This is a (Z-1) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/Z-1\" target=\"blank\">Z-1</a>) issue</p>", "jiralinks");
}
// These are not valid links:
[Test]
public void JiraLinks_Example007()
{
// Example 7
// Section: Jira Links
//
// The following Markdown:
// This is not aJIRA-123 issue
//
// Should be rendered as:
// <p>This is not aJIRA-123 issue</p>
Console.WriteLine("Example 7\nSection Jira Links\n");
TestParser.TestSpec("This is not aJIRA-123 issue", "<p>This is not aJIRA-123 issue</p>", "jiralinks");
}
[Test]
public void JiraLinks_Example008()
{
// Example 8
// Section: Jira Links
//
// The following Markdown:
// This is not JIRA-123a issue
//
// Should be rendered as:
// <p>This is not JIRA-123a issue</p>
Console.WriteLine("Example 8\nSection Jira Links\n");
TestParser.TestSpec("This is not JIRA-123a issue", "<p>This is not JIRA-123a issue</p>", "jiralinks");
}
[Test]
public void JiraLinks_Example009()
{
// Example 9
// Section: Jira Links
//
// The following Markdown:
// This is not JIRA- issue
//
// Should be rendered as:
// <p>This is not JIRA- issue</p>
Console.WriteLine("Example 9\nSection Jira Links\n");
TestParser.TestSpec("This is not JIRA- issue", "<p>This is not JIRA- issue</p>", "jiralinks");
}
}
}

View File

@@ -0,0 +1,217 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// List Extras
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.ListExtras
{
[TestFixture]
public class TestExtensionsOrderedListWithAlphaLetter
{
// # Extensions
//
// The following additional list items are supported:
//
// ## Ordered list with alpha letter
//
// Allows to use a list using an alpha letter instead of a number
[Test]
public void ExtensionsOrderedListWithAlphaLetter_Example001()
{
// Example 1
// Section: Extensions / Ordered list with alpha letter
//
// The following Markdown:
// a. First item
// b. Second item
// c. Last item
//
// Should be rendered as:
// <ol type="a">
// <li>First item</li>
// <li>Second item</li>
// <li>Last item</li>
// </ol>
Console.WriteLine("Example 1\nSection Extensions / Ordered list with alpha letter\n");
TestParser.TestSpec("a. First item\nb. Second item\nc. Last item", "<ol type=\"a\">\n<li>First item</li>\n<li>Second item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced");
}
// It works also for uppercase alpha:
[Test]
public void ExtensionsOrderedListWithAlphaLetter_Example002()
{
// Example 2
// Section: Extensions / Ordered list with alpha letter
//
// The following Markdown:
// A. First item
// B. Second item
// C. Last item
//
// Should be rendered as:
// <ol type="A">
// <li>First item</li>
// <li>Second item</li>
// <li>Last item</li>
// </ol>
Console.WriteLine("Example 2\nSection Extensions / Ordered list with alpha letter\n");
TestParser.TestSpec("A. First item\nB. Second item\nC. Last item", "<ol type=\"A\">\n<li>First item</li>\n<li>Second item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced");
}
// Like for numbered list, a list can start with a different letter
[Test]
public void ExtensionsOrderedListWithAlphaLetter_Example003()
{
// Example 3
// Section: Extensions / Ordered list with alpha letter
//
// The following Markdown:
// b. First item
// c. Second item
//
// Should be rendered as:
// <ol type="a" start="2">
// <li>First item</li>
// <li>Second item</li>
// </ol>
Console.WriteLine("Example 3\nSection Extensions / Ordered list with alpha letter\n");
TestParser.TestSpec("b. First item\nc. Second item", "<ol type=\"a\" start=\"2\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "listextras|advanced");
}
// A different type of list will break the existing list:
[Test]
public void ExtensionsOrderedListWithAlphaLetter_Example004()
{
// Example 4
// Section: Extensions / Ordered list with alpha letter
//
// The following Markdown:
// a. First item1
// b. Second item
// A. First item2
//
// Should be rendered as:
// <ol type="a">
// <li>First item1</li>
// <li>Second item</li>
// </ol>
// <ol type="A">
// <li>First item2</li>
// </ol>
Console.WriteLine("Example 4\nSection Extensions / Ordered list with alpha letter\n");
TestParser.TestSpec("a. First item1\nb. Second item\nA. First item2", "<ol type=\"a\">\n<li>First item1</li>\n<li>Second item</li>\n</ol>\n<ol type=\"A\">\n<li>First item2</li>\n</ol>", "listextras|advanced");
}
}
[TestFixture]
public class TestExtensionsOrderedListWithRomanLetter
{
// ## Ordered list with roman letter
//
// Allows to use a list using a roman number instead of a number.
[Test]
public void ExtensionsOrderedListWithRomanLetter_Example005()
{
// Example 5
// Section: Extensions / Ordered list with roman letter
//
// The following Markdown:
// i. First item
// ii. Second item
// iii. Third item
// iv. Last item
//
// Should be rendered as:
// <ol type="i">
// <li>First item</li>
// <li>Second item</li>
// <li>Third item</li>
// <li>Last item</li>
// </ol>
Console.WriteLine("Example 5\nSection Extensions / Ordered list with roman letter\n");
TestParser.TestSpec("i. First item\nii. Second item\niii. Third item\niv. Last item", "<ol type=\"i\">\n<li>First item</li>\n<li>Second item</li>\n<li>Third item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced");
}
// It works also for uppercase alpha:
[Test]
public void ExtensionsOrderedListWithRomanLetter_Example006()
{
// Example 6
// Section: Extensions / Ordered list with roman letter
//
// The following Markdown:
// I. First item
// II. Second item
// III. Third item
// IV. Last item
//
// Should be rendered as:
// <ol type="I">
// <li>First item</li>
// <li>Second item</li>
// <li>Third item</li>
// <li>Last item</li>
// </ol>
Console.WriteLine("Example 6\nSection Extensions / Ordered list with roman letter\n");
TestParser.TestSpec("I. First item\nII. Second item\nIII. Third item\nIV. Last item", "<ol type=\"I\">\n<li>First item</li>\n<li>Second item</li>\n<li>Third item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced");
}
// Like for numbered list, a list can start with a different letter
[Test]
public void ExtensionsOrderedListWithRomanLetter_Example007()
{
// Example 7
// Section: Extensions / Ordered list with roman letter
//
// The following Markdown:
// ii. First item
// iii. Second item
//
// Should be rendered as:
// <ol type="i" start="2">
// <li>First item</li>
// <li>Second item</li>
// </ol>
Console.WriteLine("Example 7\nSection Extensions / Ordered list with roman letter\n");
TestParser.TestSpec("ii. First item\niii. Second item", "<ol type=\"i\" start=\"2\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "listextras|advanced");
}
// Lists can be restarted, specifying the start point.
[Test]
public void ExtensionsOrderedListWithRomanLetter_Example008()
{
// Example 8
// Section: Extensions / Ordered list with roman letter
//
// The following Markdown:
// 1. First item
//
// Some text
//
// 2. Second item
//
// Should be rendered as:
// <ol>
// <li>First item</li>
// </ol>
// <p>Some text</p>
// <ol start="2">
// <li>Second item</li>
// </ol>
Console.WriteLine("Example 8\nSection Extensions / Ordered list with roman letter\n");
TestParser.TestSpec("1. First item\n\nSome text\n\n2. Second item", "<ol>\n<li>First item</li>\n</ol>\n<p>Some text</p>\n<ol start=\"2\">\n<li>Second item</li>\n</ol>", "listextras|advanced");
}
}
}

View File

@@ -0,0 +1,328 @@
// Generated: 2/22/2019 8:27:26 PM
// --------------------------------
// Math
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.Math
{
[TestFixture]
public class TestExtensionsMathInline
{
// # Extensions
//
// Adds support for mathematics spans:
//
// ## Math Inline
//
// Allows to define a mathematic block embraced by `$...$`
[Test]
public void ExtensionsMathInline_Example001()
{
// Example 1
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $math block$
//
// Should be rendered as:
// <p>This is a <span class="math">\(math block\)</span></p>
Console.WriteLine("Example 1\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $math block$", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
}
// Or by `$$...$$` embracing it by:
[Test]
public void ExtensionsMathInline_Example002()
{
// Example 2
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $$math block$$
//
// Should be rendered as:
// <p>This is a <span class="math">\(math block\)</span></p>
Console.WriteLine("Example 2\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $$math block$$", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
}
// Newlines inside an inline math are not allowed:
[Test]
public void ExtensionsMathInline_Example003()
{
// Example 3
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is not a $$math
// block$$ and? this is a $$math block$$
//
// Should be rendered as:
// <p>This is not a $$math
// block$$ and? this is a <span class="math">\(math block\)</span></p>
Console.WriteLine("Example 3\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is not a $$math \nblock$$ and? this is a $$math block$$", "<p>This is not a $$math\nblock$$ and? this is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
}
[Test]
public void ExtensionsMathInline_Example004()
{
// Example 4
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is not a $math
// block$ and? this is a $math block$
//
// Should be rendered as:
// <p>This is not a $math
// block$ and? this is a <span class="math">\(math block\)</span></p>
Console.WriteLine("Example 4\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is not a $math \nblock$ and? this is a $math block$", "<p>This is not a $math\nblock$ and? this is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
}
// An opening `$` can be followed by a space if the closing is also preceded by a space `$`:
[Test]
public void ExtensionsMathInline_Example005()
{
// Example 5
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $ math block $
//
// Should be rendered as:
// <p>This is a <span class="math">\(math block\)</span></p>
Console.WriteLine("Example 5\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $ math block $", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
}
[Test]
public void ExtensionsMathInline_Example006()
{
// Example 6
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $ math block $ after
//
// Should be rendered as:
// <p>This is a <span class="math">\(math block\)</span> after</p>
Console.WriteLine("Example 6\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $ math block $ after", "<p>This is a <span class=\"math\">\\(math block\\)</span> after</p>", "mathematics|advanced");
}
[Test]
public void ExtensionsMathInline_Example007()
{
// Example 7
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $$ math block $$ after
//
// Should be rendered as:
// <p>This is a <span class="math">\(math block\)</span> after</p>
Console.WriteLine("Example 7\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $$ math block $$ after", "<p>This is a <span class=\"math\">\\(math block\\)</span> after</p>", "mathematics|advanced");
}
[Test]
public void ExtensionsMathInline_Example008()
{
// Example 8
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a not $ math block$ because there is not a whitespace before the closing
//
// Should be rendered as:
// <p>This is a not $ math block$ because there is not a whitespace before the closing</p>
Console.WriteLine("Example 8\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a not $ math block$ because there is not a whitespace before the closing", "<p>This is a not $ math block$ because there is not a whitespace before the closing</p>", "mathematics|advanced");
}
// For the opening `$` it requires a space or a punctuation before (but cannot be used within a word):
[Test]
public void ExtensionsMathInline_Example009()
{
// Example 9
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is not a m$ath block$
//
// Should be rendered as:
// <p>This is not a m$ath block$</p>
Console.WriteLine("Example 9\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is not a m$ath block$", "<p>This is not a m$ath block$</p>", "mathematics|advanced");
}
// For the closing `$` it requires a space after or a punctuation (but cannot be preceded by a space and cannot be used within a word):
[Test]
public void ExtensionsMathInline_Example010()
{
// Example 10
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is not a $math bloc$k
//
// Should be rendered as:
// <p>This is not a $math bloc$k</p>
Console.WriteLine("Example 10\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is not a $math bloc$k", "<p>This is not a $math bloc$k</p>", "mathematics|advanced");
}
// For the closing `$` it requires a space after or a punctuation (but cannot be preceded by a space and cannot be used within a word):
[Test]
public void ExtensionsMathInline_Example011()
{
// Example 11
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is should not match a 16$ or a $15
//
// Should be rendered as:
// <p>This is should not match a 16$ or a $15</p>
Console.WriteLine("Example 11\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is should not match a 16$ or a $15", "<p>This is should not match a 16$ or a $15</p>", "mathematics|advanced");
}
// A `$` can be escaped between a math inline block by using the escape `\\`
[Test]
public void ExtensionsMathInline_Example012()
{
// Example 12
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $math \$ block$
//
// Should be rendered as:
// <p>This is a <span class="math">\(math \$ block\)</span></p>
Console.WriteLine("Example 12\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $math \\$ block$", "<p>This is a <span class=\"math\">\\(math \\$ block\\)</span></p>", "mathematics|advanced");
}
// At most, two `$` will be matched for the opening and closing:
[Test]
public void ExtensionsMathInline_Example013()
{
// Example 13
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $$$math block$$$
//
// Should be rendered as:
// <p>This is a <span class="math">\($math block$\)</span></p>
Console.WriteLine("Example 13\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $$$math block$$$", "<p>This is a <span class=\"math\">\\($math block$\\)</span></p>", "mathematics|advanced");
}
// Regular text can come both before and after the math inline
[Test]
public void ExtensionsMathInline_Example014()
{
// Example 14
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is a $math block$ with text on both sides.
//
// Should be rendered as:
// <p>This is a <span class="math">\(math block\)</span> with text on both sides.</p>
Console.WriteLine("Example 14\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is a $math block$ with text on both sides.", "<p>This is a <span class=\"math\">\\(math block\\)</span> with text on both sides.</p>", "mathematics|advanced");
}
// A mathematic block takes precedence over standard emphasis `*` `_`:
[Test]
public void ExtensionsMathInline_Example015()
{
// Example 15
// Section: Extensions / Math Inline
//
// The following Markdown:
// This is *a $math* block$
//
// Should be rendered as:
// <p>This is *a <span class="math">\(math* block\)</span></p>
Console.WriteLine("Example 15\nSection Extensions / Math Inline\n");
TestParser.TestSpec("This is *a $math* block$", "<p>This is *a <span class=\"math\">\\(math* block\\)</span></p>", "mathematics|advanced");
}
// An opening $$ at the beginning of a line should not be interpreted as a Math block:
[Test]
public void ExtensionsMathInline_Example016()
{
// Example 16
// Section: Extensions / Math Inline
//
// The following Markdown:
// $$ math $$ starting at a line
//
// Should be rendered as:
// <p><span class="math">\(math\)</span> starting at a line</p>
Console.WriteLine("Example 16\nSection Extensions / Math Inline\n");
TestParser.TestSpec("$$ math $$ starting at a line", "<p><span class=\"math\">\\(math\\)</span> starting at a line</p>", "mathematics|advanced");
}
}
[TestFixture]
public class TestExtensionsMathBlock
{
// ## Math Block
//
// The match block can spawn on multiple lines by having a $$ starting on a line.
// It is working as a fenced code block.
[Test]
public void ExtensionsMathBlock_Example017()
{
// Example 17
// Section: Extensions / Math Block
//
// The following Markdown:
// $$
// \begin{equation}
// \int_0^\infty \frac{x^3}{e^x-1}\,dx = \frac{\pi^4}{15}
// \label{eq:sample}
// \end{equation}
// $$
//
// Should be rendered as:
// <div class="math">
// \[
// \begin{equation}
// \int_0^\infty \frac{x^3}{e^x-1}\,dx = \frac{\pi^4}{15}
// \label{eq:sample}
// \end{equation}
// \]</div>
Console.WriteLine("Example 17\nSection Extensions / Math Block\n");
TestParser.TestSpec("$$\n\\begin{equation}\n \\int_0^\\infty \\frac{x^3}{e^x-1}\\,dx = \\frac{\\pi^4}{15}\n \\label{eq:sample}\n\\end{equation}\n$$", "<div class=\"math\">\n\\[\n\\begin{equation}\n \\int_0^\\infty \\frac{x^3}{e^x-1}\\,dx = \\frac{\\pi^4}{15}\n \\label{eq:sample}\n\\end{equation}\n\\]</div>", "mathematics|advanced");
}
}
}

View File

@@ -9,7 +9,7 @@ Allows to define a mathematic block embraced by `$...$`
```````````````````````````````` example
This is a $math block$
.
<p>This is a <span class="math">math block</span></p>
<p>This is a <span class="math">\(math block\)</span></p>
````````````````````````````````
Or by `$$...$$` embracing it by:
@@ -17,7 +17,7 @@ Or by `$$...$$` embracing it by:
```````````````````````````````` example
This is a $$math block$$
.
<p>This is a <span class="math">math block</span></p>
<p>This is a <span class="math">\(math block\)</span></p>
````````````````````````````````
Newlines inside an inline math are not allowed:
@@ -27,7 +27,7 @@ This is not a $$math
block$$ and? this is a $$math block$$
.
<p>This is not a $$math
block$$ and? this is a <span class="math">math block</span></p>
block$$ and? this is a <span class="math">\(math block\)</span></p>
````````````````````````````````
```````````````````````````````` example
@@ -35,26 +35,26 @@ This is not a $math
block$ and? this is a $math block$
.
<p>This is not a $math
block$ and? this is a <span class="math">math block</span></p>
block$ and? this is a <span class="math">\(math block\)</span></p>
````````````````````````````````
An opening `$` can be followed by a space if the closing is also preceded by a space `$`:
```````````````````````````````` example
This is a $ math block $
.
<p>This is a <span class="math">math block</span></p>
<p>This is a <span class="math">\(math block\)</span></p>
````````````````````````````````
```````````````````````````````` example
This is a $ math block $ after
.
<p>This is a <span class="math">math block</span> after</p>
<p>This is a <span class="math">\(math block\)</span> after</p>
````````````````````````````````
```````````````````````````````` example
This is a $$ math block $$ after
.
<p>This is a <span class="math">math block</span> after</p>
<p>This is a <span class="math">\(math block\)</span> after</p>
````````````````````````````````
```````````````````````````````` example
@@ -92,7 +92,7 @@ A `$` can be escaped between a math inline block by using the escape `\\`
```````````````````````````````` example
This is a $math \$ block$
.
<p>This is a <span class="math">math \$ block</span></p>
<p>This is a <span class="math">\(math \$ block\)</span></p>
````````````````````````````````
At most, two `$` will be matched for the opening and closing:
@@ -100,7 +100,7 @@ At most, two `$` will be matched for the opening and closing:
```````````````````````````````` example
This is a $$$math block$$$
.
<p>This is a <span class="math">$math block$</span></p>
<p>This is a <span class="math">\($math block$\)</span></p>
````````````````````````````````
Regular text can come both before and after the math inline
@@ -108,21 +108,21 @@ Regular text can come both before and after the math inline
```````````````````````````````` example
This is a $math block$ with text on both sides.
.
<p>This is a <span class="math">math block</span> with text on both sides.</p>
<p>This is a <span class="math">\(math block\)</span> with text on both sides.</p>
````````````````````````````````
A mathematic block takes precedence over standard emphasis `*` `_`:
```````````````````````````````` example
This is *a $math* block$
.
<p>This is *a <span class="math">math* block</span></p>
<p>This is *a <span class="math">\(math* block\)</span></p>
````````````````````````````````
An opening $$ at the beginning of a line should not be interpreted as a Math block:
```````````````````````````````` example
$$ math $$ starting at a line
.
<p><span class="math">math</span> starting at a line</p>
<p><span class="math">\(math\)</span> starting at a line</p>
````````````````````````````````
## Math Block
@@ -138,9 +138,11 @@ $$
\end{equation}
$$
.
<div class="math">\begin{equation}
<div class="math">
\[
\begin{equation}
\int_0^\infty \frac{x^3}{e^x-1}\,dx = \frac{\pi^4}{15}
\label{eq:sample}
\end{equation}
</div>
\]</div>
````````````````````````````````

View File

@@ -0,0 +1,50 @@
// 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

@@ -17,9 +17,9 @@ Allows to embed audio/video links to popular website:
![Video5](https://ok.ru/video/26870090463)
.
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" width="500" height="281" frameborder="0" allowfullscreen></iframe></p>
<p><iframe src="https://player.vimeo.com/video/8607834" width="500" height="281" frameborder="0" allowfullscreen></iframe></p>
<p><video width="500" height="281" controls><source type="video/mp4" src="https://sample.com/video.mp4"></source></video></p>
<p><iframe src="https://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>
<p><iframe src="https://ok.ru/videoembed/26870090463" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
````````````````````````````````

View File

@@ -0,0 +1,59 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// No Html
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.NoHtml
{
[TestFixture]
public class TestExtensionsNoHTML
{
// # Extensions
//
// ## NoHTML
//
// The extension DisableHtml allows to disable the parsing of HTML:
//
// For inline HTML:
[Test]
public void ExtensionsNoHTML_Example001()
{
// Example 1
// Section: Extensions / NoHTML
//
// The following Markdown:
// this is some text</td></tr>
//
// Should be rendered as:
// <p>this is some text&lt;/td&gt;&lt;/tr&gt;</p>
Console.WriteLine("Example 1\nSection Extensions / NoHTML\n");
TestParser.TestSpec("this is some text</td></tr>", "<p>this is some text&lt;/td&gt;&lt;/tr&gt;</p>", "nohtml");
}
// For Block HTML:
[Test]
public void ExtensionsNoHTML_Example002()
{
// Example 2
// Section: Extensions / NoHTML
//
// The following Markdown:
// <div>
// this is some text
// </div>
//
// Should be rendered as:
// <p>&lt;div&gt;
// this is some text
// &lt;/div&gt;</p>
Console.WriteLine("Example 2\nSection Extensions / NoHTML\n");
TestParser.TestSpec("<div>\nthis is some text\n</div>", "<p>&lt;div&gt;\nthis is some text\n&lt;/div&gt;</p>", "nohtml");
}
}
}

View File

@@ -0,0 +1,855 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Pipe Tables
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.PipeTables
{
[TestFixture]
public class TestExtensionsPipeTable
{
// # Extensions
//
// This section describes the different extensions supported:
//
// ## Pipe Table
//
// 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.
// - 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
// - followed by a sequence of at least one `-` character
// - followed by an optional `:` to specify right align (or center align if left align is also defined)
// - ending by optional spaces
//
// Because a list has a higher precedence than a pipe table, a table header row separator requires at least 2 dashes `--` to start a header row:
[Test]
public void ExtensionsPipeTable_Example001()
{
// Example 1
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b
// -- | -
// 0 | 1
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 1\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a | b\n-- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// The following is also considered as a table, even if the second line starts like a list:
[Test]
public void ExtensionsPipeTable_Example002()
{
// Example 2
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b
// - | -
// 0 | 1
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 2\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a | b\n- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// A pipe table with only one header row is allowed:
[Test]
public void ExtensionsPipeTable_Example003()
{
// Example 3
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b
// -- | --
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// </table>
Console.WriteLine("Example 3\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "pipetables|advanced");
}
// After a row separator header, they will be interpreted as plain column:
[Test]
public void ExtensionsPipeTable_Example004()
{
// Example 4
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b
// -- | --
// -- | --
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>--</td>
// <td>--</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 4\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a | b\n-- | --\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>--</td>\n<td>--</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// But if a table doesn't start with a column delimiter, it is not interpreted as a table, even if following lines have a column delimiter
[Test]
public void ExtensionsPipeTable_Example005()
{
// Example 5
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a b
// c | d
// e | f
//
// Should be rendered as:
// <p>a b
// c | d
// e | f</p>
Console.WriteLine("Example 5\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a b\nc | d\ne | f", "<p>a b\nc | d\ne | f</p>", "pipetables|advanced");
}
// If a line doesn't have a column delimiter `|` the table is not detected
[Test]
public void ExtensionsPipeTable_Example006()
{
// Example 6
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b
// c no d
//
// Should be rendered as:
// <p>a | b
// c no d</p>
Console.WriteLine("Example 6\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a | b\nc no d", "<p>a | b\nc no d</p>", "pipetables|advanced");
}
// If a row contains more column than the header row, it will still be added as a column:
[Test]
public void ExtensionsPipeTable_Example007()
{
// Example 7
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b
// -- | --
// 0 | 1 | 2
// 3 | 4
// 5 |
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// <th></th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// <td>2</td>
// </tr>
// <tr>
// <td>3</td>
// <td>4</td>
// <td></td>
// </tr>
// <tr>
// <td>5</td>
// <td></td>
// <td></td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 7\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a | b \n-- | --\n0 | 1 | 2\n3 | 4\n5 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n<th></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n<td>2</td>\n</tr>\n<tr>\n<td>3</td>\n<td>4</td>\n<td></td>\n</tr>\n<tr>\n<td>5</td>\n<td></td>\n<td></td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// **Rule #2**
// A pipe table ends after a blank line or the end of the file.
//
// **Rule #3**
// A cell content is trimmed (start and end) from white-spaces.
[Test]
public void ExtensionsPipeTable_Example008()
{
// Example 8
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b |
// -- | --
// 0 | 1 |
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 8\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a | b |\n-- | --\n0 | 1 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// **Rule #4**
// Column delimiters `|` at the very beginning of a line or just before a line ending with only spaces and/or terminated by a newline can be omitted
[Test]
public void ExtensionsPipeTable_Example009()
{
// Example 9
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b |
// -- | --
// | 0 | 1
// | 2 | 3 |
// 4 | 5
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// </tr>
// <tr>
// <td>2</td>
// <td>3</td>
// </tr>
// <tr>
// <td>4</td>
// <td>5</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 9\nSection Extensions / Pipe Table\n");
TestParser.TestSpec(" a | b |\n-- | --\n| 0 | 1\n| 2 | 3 |\n 4 | 5 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n<tr>\n<td>4</td>\n<td>5</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// A pipe may be present at both the beginning/ending of each line:
[Test]
public void ExtensionsPipeTable_Example010()
{
// Example 10
// Section: Extensions / Pipe Table
//
// The following Markdown:
// |a|b|
// |-|-|
// |0|1|
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 10\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("|a|b|\n|-|-|\n|0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// Or may be ommitted on one side:
[Test]
public void ExtensionsPipeTable_Example011()
{
// Example 11
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a|b|
// -|-|
// 0|1|
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 11\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a|b|\n-|-|\n0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
[Test]
public void ExtensionsPipeTable_Example012()
{
// Example 12
// Section: Extensions / Pipe Table
//
// The following Markdown:
// |a|b
// |-|-
// |0|1
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 12\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("|a|b\n|-|-\n|0|1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// Single column table can be declared with lines starting only by a column delimiter:
[Test]
public void ExtensionsPipeTable_Example013()
{
// Example 13
// Section: Extensions / Pipe Table
//
// The following Markdown:
// | a
// | --
// | b
// | c
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>b</td>
// </tr>
// <tr>
// <td>c</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 13\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("| a\n| --\n| b\n| c ", "<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>b</td>\n</tr>\n<tr>\n<td>c</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// **Rule #5**
//
// The first row is considered as a **header row** if it is separated from the regular rows by a row containing a **header column separator** for each column. A header column separator is:
//
// - starting by optional spaces
// - followed by an optional `:` to specify left align
// - followed by a sequence of at least one `-` character
// - followed by an optional `:` to specify right align (or center align if left align is also defined)
// - ending by optional spaces
[Test]
public void ExtensionsPipeTable_Example014()
{
// Example 14
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b
// -------|-------
// 0 | 1
// 2 | 3
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// </tr>
// <tr>
// <td>2</td>
// <td>3</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 14\nSection Extensions / Pipe Table\n");
TestParser.TestSpec(" a | b \n-------|-------\n 0 | 1 \n 2 | 3 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// The text alignment is defined by default to be center for header and left for cells. If the left alignment is applied, it will force the column heading to be left aligned.
// There is no way to define a different alignment for heading and cells (apart from the default).
// The text alignment can be changed by using the character `:` with the header column separator:
[Test]
public void ExtensionsPipeTable_Example015()
{
// Example 15
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b | c
// :------|:-------:| ----:
// 0 | 1 | 2
// 3 | 4 | 5
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th style="text-align: left;">a</th>
// <th style="text-align: center;">b</th>
// <th style="text-align: right;">c</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td style="text-align: left;">0</td>
// <td style="text-align: center;">1</td>
// <td style="text-align: right;">2</td>
// </tr>
// <tr>
// <td style="text-align: left;">3</td>
// <td style="text-align: center;">4</td>
// <td style="text-align: right;">5</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 15\nSection Extensions / Pipe Table\n");
TestParser.TestSpec(" a | b | c \n:------|:-------:| ----:\n 0 | 1 | 2 \n 3 | 4 | 5 ", "<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">a</th>\n<th style=\"text-align: center;\">b</th>\n<th style=\"text-align: right;\">c</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\">0</td>\n<td style=\"text-align: center;\">1</td>\n<td style=\"text-align: right;\">2</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">3</td>\n<td style=\"text-align: center;\">4</td>\n<td style=\"text-align: right;\">5</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// Test alignment with starting and ending pipes:
[Test]
public void ExtensionsPipeTable_Example016()
{
// Example 16
// Section: Extensions / Pipe Table
//
// The following Markdown:
// | abc | def | ghi |
// |:---:|-----|----:|
// | 1 | 2 | 3 |
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th style="text-align: center;">abc</th>
// <th>def</th>
// <th style="text-align: right;">ghi</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td style="text-align: center;">1</td>
// <td>2</td>
// <td style="text-align: right;">3</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 16\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("| abc | def | ghi |\n|:---:|-----|----:|\n| 1 | 2 | 3 |", "<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">abc</th>\n<th>def</th>\n<th style=\"text-align: right;\">ghi</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\">1</td>\n<td>2</td>\n<td style=\"text-align: right;\">3</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// The following example shows a non matching header column separator:
[Test]
public void ExtensionsPipeTable_Example017()
{
// Example 17
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b
// -------|---x---
// 0 | 1
// 2 | 3
//
// Should be rendered as:
// <p>a | b
// -------|---x---
// 0 | 1
// 2 | 3</p>
Console.WriteLine("Example 17\nSection Extensions / Pipe Table\n");
TestParser.TestSpec(" a | b\n-------|---x---\n 0 | 1\n 2 | 3 ", "<p>a | b\n-------|---x---\n0 | 1\n2 | 3</p> ", "pipetables|advanced");
}
// **Rule #6**
//
// A column delimiter has a higher priority than emphasis delimiter
[Test]
public void ExtensionsPipeTable_Example018()
{
// Example 18
// Section: Extensions / Pipe Table
//
// The following Markdown:
// *a* | b
// ----- |-----
// 0 | _1_
// _2 | 3*
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th><em>a</em></th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td><em>1</em></td>
// </tr>
// <tr>
// <td>_2</td>
// <td>3*</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 18\nSection Extensions / Pipe Table\n");
TestParser.TestSpec(" *a* | b\n----- |-----\n 0 | _1_\n _2 | 3* ", "<table>\n<thead>\n<tr>\n<th><em>a</em></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td><em>1</em></td>\n</tr>\n<tr>\n<td>_2</td>\n<td>3*</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// **Rule #7**
//
// A backstick/code delimiter has a higher precedence than a column delimiter `|`:
[Test]
public void ExtensionsPipeTable_Example019()
{
// Example 19
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b `
// 0 | `
//
// Should be rendered as:
// <p>a | b <code>0 |</code></p>
Console.WriteLine("Example 19\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a | b `\n0 | ` ", "<p>a | b <code>0 |</code></p> ", "pipetables|advanced");
}
// **Rule #7**
//
// A HTML inline has a higher precedence than a column delimiter `|`:
[Test]
public void ExtensionsPipeTable_Example020()
{
// Example 20
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a <a href="" title="|"></a> | b
// -- | --
// 0 | 1
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a <a href="" title="|"></a></th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 20\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a <a href=\"\" title=\"|\"></a> | b\n-- | --\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a <a href=\"\" title=\"|\"></a></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// **Rule #8**
//
// Links have a higher precedence than the column delimiter character `|`:
[Test]
public void ExtensionsPipeTable_Example021()
{
// Example 21
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b
// -- | --
// [This is a link with a | inside the label](http://google.com) | 1
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td><a href="http://google.com">This is a link with a | inside the label</a></td>
// <td>1</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 21\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a | b\n-- | --\n[This is a link with a | inside the label](http://google.com) | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"http://google.com\">This is a link with a | inside the label</a></td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// ** Rule #9**
//
// It is possible to have a single row header only:
[Test]
public void ExtensionsPipeTable_Example022()
{
// Example 22
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b
// -- | --
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// </table>
Console.WriteLine("Example 22\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "pipetables|advanced");
}
[Test]
public void ExtensionsPipeTable_Example023()
{
// Example 23
// Section: Extensions / Pipe Table
//
// The following Markdown:
// |a|b|c
// |---|---|---|
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// <th>c</th>
// </tr>
// </thead>
// </table>
Console.WriteLine("Example 23\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("|a|b|c\n|---|---|---|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n<th>c</th>\n</tr>\n</thead>\n</table>", "pipetables|advanced");
}
// ** Tests **
//
// Tests trailing spaces after pipes
[Test]
public void ExtensionsPipeTable_Example024()
{
// Example 24
// Section: Extensions / Pipe Table
//
// The following Markdown:
// | abc | def |
// |---|---|
// | cde| ddd|
// | eee| fff|
// | fff | fffff |
// |gggg | ffff |
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>abc</th>
// <th>def</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>cde</td>
// <td>ddd</td>
// </tr>
// <tr>
// <td>eee</td>
// <td>fff</td>
// </tr>
// <tr>
// <td>fff</td>
// <td>fffff</td>
// </tr>
// <tr>
// <td>gggg</td>
// <td>ffff</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 24\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("| abc | def | \n|---|---|\n| cde| ddd| \n| eee| fff|\n| fff | fffff | \n|gggg | ffff | ", "<table>\n<thead>\n<tr>\n<th>abc</th>\n<th>def</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>cde</td>\n<td>ddd</td>\n</tr>\n<tr>\n<td>eee</td>\n<td>fff</td>\n</tr>\n<tr>\n<td>fff</td>\n<td>fffff</td>\n</tr>\n<tr>\n<td>gggg</td>\n<td>ffff</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
// ** Normalized columns count **
//
// The tables are normalized to the maximum number of columns found in a table
[Test]
public void ExtensionsPipeTable_Example025()
{
// Example 25
// Section: Extensions / Pipe Table
//
// The following Markdown:
// a | b
// -- | -
// 0 | 1 | 2
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// <th></th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// <td>2</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 25\nSection Extensions / Pipe Table\n");
TestParser.TestSpec("a | b\n-- | - \n0 | 1 | 2", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n<th></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n<td>2</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
}
}
}

View File

@@ -0,0 +1,336 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Smarty Pants
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.SmartyPants
{
[TestFixture]
public class TestExtensionsSmartyPantsQuotes
{
// # Extensions
//
// Adds support for smarty pants:
//
// ## SmartyPants Quotes
//
// Converts the following character to smarty pants:
[Test]
public void ExtensionsSmartyPantsQuotes_Example001()
{
// Example 1
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// This is a "text"
//
// Should be rendered as:
// <p>This is a &ldquo;text&rdquo;</p>
Console.WriteLine("Example 1\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("This is a \"text\"", "<p>This is a &ldquo;text&rdquo;</p>", "pipetables+smartypants|advanced+smartypants");
}
[Test]
public void ExtensionsSmartyPantsQuotes_Example002()
{
// Example 2
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// This is a 'text'
//
// Should be rendered as:
// <p>This is a &lsquo;text&rsquo;</p>
Console.WriteLine("Example 2\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("This is a 'text'", "<p>This is a &lsquo;text&rsquo;</p>", "pipetables+smartypants|advanced+smartypants");
}
[Test]
public void ExtensionsSmartyPantsQuotes_Example003()
{
// Example 3
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// This is a <<text>>
//
// Should be rendered as:
// <p>This is a &laquo;text&raquo;</p>
Console.WriteLine("Example 3\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("This is a <<text>>", "<p>This is a &laquo;text&raquo;</p>", "pipetables+smartypants|advanced+smartypants");
}
// Unbalanced quotes are not changed:
[Test]
public void ExtensionsSmartyPantsQuotes_Example004()
{
// Example 4
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// This is a "text
//
// Should be rendered as:
// <p>This is a &quot;text</p>
Console.WriteLine("Example 4\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("This is a \"text", "<p>This is a &quot;text</p>", "pipetables+smartypants|advanced+smartypants");
}
[Test]
public void ExtensionsSmartyPantsQuotes_Example005()
{
// Example 5
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// This is a 'text
//
// Should be rendered as:
// <p>This is a 'text</p>
Console.WriteLine("Example 5\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("This is a 'text", "<p>This is a 'text</p>", "pipetables+smartypants|advanced+smartypants");
}
[Test]
public void ExtensionsSmartyPantsQuotes_Example006()
{
// Example 6
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// This is a <<text
//
// Should be rendered as:
// <p>This is a &lt;&lt;text</p>
Console.WriteLine("Example 6\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("This is a <<text", "<p>This is a &lt;&lt;text</p>", "pipetables+smartypants|advanced+smartypants");
}
// Unbalanced quotes inside other quotes are not changed:
[Test]
public void ExtensionsSmartyPantsQuotes_Example007()
{
// Example 7
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// This is a "text 'with" a another text'
//
// Should be rendered as:
// <p>This is a &ldquo;text 'with&rdquo; a another text'</p>
Console.WriteLine("Example 7\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("This is a \"text 'with\" a another text'", "<p>This is a &ldquo;text 'with&rdquo; a another text'</p>", "pipetables+smartypants|advanced+smartypants");
}
[Test]
public void ExtensionsSmartyPantsQuotes_Example008()
{
// Example 8
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// This is a 'text <<with' a another text>>
//
// Should be rendered as:
// <p>This is a &lsquo;text &lt;&lt;with&rsquo; a another text&gt;&gt;</p>
Console.WriteLine("Example 8\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("This is a 'text <<with' a another text>>", "<p>This is a &lsquo;text &lt;&lt;with&rsquo; a another text&gt;&gt;</p>", "pipetables+smartypants|advanced+smartypants");
}
[Test]
public void ExtensionsSmartyPantsQuotes_Example009()
{
// Example 9
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// This is a <<text 'with>> a another text'
//
// Should be rendered as:
// <p>This is a &laquo;text 'with&raquo; a another text'</p>
Console.WriteLine("Example 9\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("This is a <<text 'with>> a another text'", "<p>This is a &laquo;text 'with&raquo; a another text'</p>", "pipetables+smartypants|advanced+smartypants");
}
// Quotes requires to have the same rules than emphasis `_` regarding left/right frankling rules:
[Test]
public void ExtensionsSmartyPantsQuotes_Example010()
{
// Example 10
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// It's not quotes'
//
// Should be rendered as:
// <p>It's not quotes'</p>
Console.WriteLine("Example 10\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("It's not quotes'", "<p>It's not quotes'</p>", "pipetables+smartypants|advanced+smartypants");
}
[Test]
public void ExtensionsSmartyPantsQuotes_Example011()
{
// Example 11
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// They are ' not matching quotes '
//
// Should be rendered as:
// <p>They are ' not matching quotes '</p>
Console.WriteLine("Example 11\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("They are ' not matching quotes '", "<p>They are ' not matching quotes '</p>", "pipetables+smartypants|advanced+smartypants");
}
[Test]
public void ExtensionsSmartyPantsQuotes_Example012()
{
// Example 12
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// They are' not matching 'quotes
//
// Should be rendered as:
// <p>They are' not matching 'quotes</p>
Console.WriteLine("Example 12\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("They are' not matching 'quotes", "<p>They are' not matching 'quotes</p>", "pipetables+smartypants|advanced+smartypants");
}
// An emphasis starting inside left/right quotes will span over the right quote:
[Test]
public void ExtensionsSmartyPantsQuotes_Example013()
{
// Example 13
// Section: Extensions / SmartyPants Quotes
//
// The following Markdown:
// This is "a *text" with an emphasis*
//
// Should be rendered as:
// <p>This is &ldquo;a <em>text&rdquo; with an emphasis</em></p>
Console.WriteLine("Example 13\nSection Extensions / SmartyPants Quotes\n");
TestParser.TestSpec("This is \"a *text\" with an emphasis*", "<p>This is &ldquo;a <em>text&rdquo; with an emphasis</em></p>", "pipetables+smartypants|advanced+smartypants");
}
}
[TestFixture]
public class TestExtensionsSmartyPantsSeparators
{
// ## SmartyPants Separators
[Test]
public void ExtensionsSmartyPantsSeparators_Example014()
{
// Example 14
// Section: Extensions / SmartyPants Separators
//
// The following Markdown:
// This is a -- text
//
// Should be rendered as:
// <p>This is a &ndash; text</p>
Console.WriteLine("Example 14\nSection Extensions / SmartyPants Separators\n");
TestParser.TestSpec("This is a -- text", "<p>This is a &ndash; text</p>", "pipetables+smartypants|advanced+smartypants");
}
[Test]
public void ExtensionsSmartyPantsSeparators_Example015()
{
// Example 15
// Section: Extensions / SmartyPants Separators
//
// The following Markdown:
// This is a --- text
//
// Should be rendered as:
// <p>This is a &mdash; text</p>
Console.WriteLine("Example 15\nSection Extensions / SmartyPants Separators\n");
TestParser.TestSpec("This is a --- text", "<p>This is a &mdash; text</p>", "pipetables+smartypants|advanced+smartypants");
}
[Test]
public void ExtensionsSmartyPantsSeparators_Example016()
{
// Example 16
// Section: Extensions / SmartyPants Separators
//
// The following Markdown:
// This is a en ellipsis...
//
// Should be rendered as:
// <p>This is a en ellipsis&hellip;</p>
Console.WriteLine("Example 16\nSection Extensions / SmartyPants Separators\n");
TestParser.TestSpec("This is a en ellipsis...", "<p>This is a en ellipsis&hellip;</p>", "pipetables+smartypants|advanced+smartypants");
}
// Check that a smartypants are not breaking pipetable parsing:
[Test]
public void ExtensionsSmartyPantsSeparators_Example017()
{
// Example 17
// Section: Extensions / SmartyPants Separators
//
// The following Markdown:
// a | b
// -- | --
// 0 | 1
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example 17\nSection Extensions / SmartyPants Separators\n");
TestParser.TestSpec("a | b\n-- | --\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables+smartypants|advanced+smartypants");
}
// Check quotes and dash:
[Test]
public void ExtensionsSmartyPantsSeparators_Example018()
{
// Example 18
// Section: Extensions / SmartyPants Separators
//
// The following Markdown:
// A "quote" with a ---
//
// Should be rendered as:
// <p>A &ldquo;quote&rdquo; with a &mdash;</p>
Console.WriteLine("Example 18\nSection Extensions / SmartyPants Separators\n");
TestParser.TestSpec("A \"quote\" with a ---", "<p>A &ldquo;quote&rdquo; with a &mdash;</p>", "pipetables+smartypants|advanced+smartypants");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,342 +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.The MIT License (MIT)
This is a modified version of code released previously with the following license:
--------------------------
Copyright (c) 2014 Morten Houston Ludvigsen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/#>
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.CodeDom" #>
<#@ import namespace="System.CodeDom.Compiler" #>
<#@ output extension=".cs" encoding="utf-8"#>
<#
var specFiles = new KeyValuePair<string, string>[] {
new KeyValuePair<string, string>("https://raw.githubusercontent.com/jgm/CommonMark/4ec06917c3a3632be4a935ffa0973092bd2621be/spec.txt", string.Empty), // 0.28
new KeyValuePair<string, string>(Host.ResolvePath("PipeTableSpecs.md"), "pipetables|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("FootnotesSpecs.md"), "footnotes|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("GenericAttributesSpecs.md"), "attributes|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("EmphasisExtraSpecs.md"), "emphasisextras|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("HardlineBreakSpecs.md"), "hardlinebreak|advanced+hardlinebreak"),
new KeyValuePair<string, string>(Host.ResolvePath("GridTableSpecs.md"), "gridtables|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("CustomContainerSpecs.md"), "customcontainers+attributes|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("DefinitionListSpecs.md"), "definitionlists+attributes|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("EmojiSpecs.md"), "emojis|advanced+emojis"),
new KeyValuePair<string, string>(Host.ResolvePath("AbbreviationSpecs.md"), "abbreviations|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("ListExtraSpecs.md"), "listextras|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("FigureFooterAndCiteSpecs.md"), "figures+footers+citations|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("MathSpecs.md"), "mathematics|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("BootstrapSpecs.md"), "bootstrap+pipetables+figures+attributes"),
new KeyValuePair<string, string>(Host.ResolvePath("MediaSpecs.md"), "medialinks|advanced+medialinks"),
new KeyValuePair<string, string>(Host.ResolvePath("SmartyPantsSpecs.md"), "pipetables+smartypants|advanced+smartypants"), // Check with smartypants to make sure that it doesn't break pipetables
new KeyValuePair<string, string>(Host.ResolvePath("AutoIdentifierSpecs.md"), "autoidentifiers|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("TaskListSpecs.md"), "tasklists|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("DiagramsSpecs.md"), "diagrams|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("NoHtmlSpecs.md"), "nohtml"),
new KeyValuePair<string, string>(Host.ResolvePath("YamlSpecs.md"), "yaml"),
new KeyValuePair<string, string>(Host.ResolvePath("AutoLinks.md"), "autolinks|advanced"),
new KeyValuePair<string, string>(Host.ResolvePath("JiraLinks.md"), "jiralinks"),
new KeyValuePair<string, string>(Host.ResolvePath("GlobalizationSpecs.md"), "globalization+advanced+emojis"),
};
var emptyLines = false;
var displayEmptyLines = false;
#>
// Generated the <#= DateTime.Now #>
using System;
using NUnit.Framework;
namespace Markdig.Tests
{
<#
foreach (var specFileItem in specFiles)
{
var specFile = specFileItem.Key;
foreach (var item in ReadSpecs(specFile))
{
if (item is string)
{
var line = (string)item;
if (line == "")
{
emptyLines = true;
}
else
{
if (emptyLines && displayEmptyLines)
{
#>
//
<#
}
#>
// <#= line #>
<#
displayEmptyLines = true;
emptyLines = false;
}
}
if (item is Spec)
{
var spec = (Spec)item;
emptyLines = false;
displayEmptyLines = false;
#>
[TestFixture]
public partial class Test<#= spec.SecHeadingCompact #>
{
[Test]
public void <#= spec.Name #>()
{
// Example <#= spec.Example #>
// Section: <#= spec.SecHeading #>
//
// The following CommonMark:
<#= string.Join(Environment.NewLine, spec.CommonMark.Split('\n').Select(s => string.Format(" // {0}", s))) #>
//
// Should be rendered as:
<#= string.Join(Environment.NewLine, spec.Html.Split('\n').Select(s => string.Format(" // {0}", s))) #>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, <#= spec.Example #>, "<#= Escape(spec.SecHeading) #>");
TestParser.TestSpec("<#= Escape(spec.CommonMark) #>", "<#= Escape(spec.Html) #>", "<#= Escape(specFileItem.Value) #>");
}
}
<#
}
}
}
#>
}
<#+
private class Item
{
}
private class Spec
{
public int Example { get; set; }
public int Number { get; set; }
public string SecHeading { get; set; }
public string SecHeadingCompact {get; set;}
public string SectionId { get; set; }
public string CommonMark { get; set; }
public string Html { get; set; }
public string Name
{
get { return string.Format("{0}_Example{1:000}", SecHeadingCompact, Example); }
}
}
private bool Match(string value, string pattern, out string[] groups)
{
var match = Regex.Match(value, pattern);
if (match.Success)
{
groups = match.Groups.Cast<Group>().Select(g => g.Value).ToArray();
return true;
}
groups = new string[] { };
return false;
}
private string GetSectionId(IEnumerable<string> sectionHeadings)
{
var name = string.Join("", sectionHeadings.Select(s => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(s)));
name = Regex.Replace(name, @"\s+", "");
return name;
}
private IEnumerable<object> ReadSpecs(string fileName)
{
var stage = 0;
var commonMark = "";
var html = "";
var example = 0;
var lineNum = 0;
var exampleLine = 0;
var sectionNums = new List<int>();
var sectionHeadings = new Stack<string>();
var sectionHeading = "";
var groups = new string[] { };
var line = "";
var sectionSpecNumbers = new Dictionary<string, int>();
var endOfTests = false;
string content;
if (fileName.StartsWith("http"))
{
using (var client = new System.Net.WebClient() { Encoding = Encoding.UTF8 })
content = client.DownloadString(fileName);
}
else
{
content = File.ReadAllText(fileName);
}
using (var reader = new StringReader(content))
{
while (!endOfTests && (line = reader.ReadLine()) != null)
{
lineNum += 1;
if (stage == 0 && line == "```````````````````````````````` example")
{
stage = 1; // parse markdown
exampleLine = lineNum;
}
else if (stage == 1 && line == ".")
{
stage = 2;
}
else if (stage == 2 && line == "````````````````````````````````")
{
stage = 0;
var sectionId = GetSectionId(sectionHeadings.Reverse());
if (sectionSpecNumbers.ContainsKey(sectionId))
{
sectionSpecNumbers[sectionId] += 1;
}
else
{
sectionSpecNumbers[sectionId] = 1;
}
var heading = string.Join(" ", sectionHeadings.Reverse()).Replace("\\","\\\\");
var headingCompact = Regex.Replace(heading, "[^A-Za-z0-9]+", string.Empty);
example++;
yield return new Spec
{
Example = example,
Number = sectionSpecNumbers[sectionId],
SecHeading = heading,
SecHeadingCompact = headingCompact,
SectionId = sectionId,
CommonMark = commonMark,
Html = html
};
commonMark = "";
html = "";
}
else if (stage == 0 && line == "<!-- END TESTS -->")
{
endOfTests = true;
}
else if (stage == 0 && Match(line, "^(#+) +(.*)", out groups))
{
var sectionLevel = groups[1].Length;
sectionHeading = groups[2];
while (sectionHeadings.Count() < sectionLevel)
{
sectionHeadings.Push("");
}
while (sectionHeadings.Count() >= sectionLevel)
{
sectionHeadings.Pop();
}
sectionHeadings.Push(sectionHeading);
if (sectionNums.Count > 0 && sectionNums.Count == sectionLevel - 1)
{
sectionNums[sectionNums.Count - 1]++;
}
else if (sectionNums.Count > sectionLevel - 1)
{
sectionNums = Enumerable.Range(0, sectionLevel).ToList();
sectionNums[sectionNums.Count - 1]++;
}
else
{
while (sectionNums.Count < sectionLevel - 1)
{
sectionNums.Add(1);
}
}
yield return line.Trim();
}
else if (stage == 1)
{
if (commonMark.Length > 0)
{
commonMark += '\n';
}
commonMark += line;
}
else if (stage == 2)
{
if (html.Length > 0)
{
html += '\n';
}
html += line;
}
else
{
yield return line.Trim();
}
}
while ((line = reader.ReadLine()) != null)
{
yield return line.Trim();
}
}
}
private static string Escape(string input)
{
return input
.Replace("→", "\t")
.Replace("\\", "\\\\")
.Replace("\"", "\\\"")
.Replace("\0", "\\0")
.Replace("\a", "\\a")
.Replace("\b", "\\b")
.Replace("\f", "\\f")
.Replace("\n", "\\n")
.Replace("\r", "\\r")
.Replace("\t", "\\t")
.Replace("\v", "\\v")
;
}
private static string ToLiteral(string input)
{
using (var writer = new StringWriter())
{
using (var provider = CodeDomProvider.CreateProvider("CSharp"))
{
provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, null);
return writer.ToString();
}
}
}
#>

View File

@@ -0,0 +1,63 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Task Lists
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.TaskLists
{
[TestFixture]
public class TestExtensionsTaskLists
{
// # Extensions
//
// Adds support for task lists:
//
// ## TaskLists
//
// A task list item consist of `[ ]` or `[x]` or `[X]` inside a list item (ordered or unordered)
[Test]
public void ExtensionsTaskLists_Example001()
{
// Example 1
// Section: Extensions / TaskLists
//
// The following Markdown:
// - [ ] Item1
// - [x] Item2
// - [ ] Item3
// - Item4
//
// Should be rendered as:
// <ul class="contains-task-list">
// <li class="task-list-item"><input disabled="disabled" type="checkbox" /> Item1</li>
// <li class="task-list-item"><input disabled="disabled" type="checkbox" checked="checked" /> Item2</li>
// <li class="task-list-item"><input disabled="disabled" type="checkbox" /> Item3</li>
// <li>Item4</li>
// </ul>
Console.WriteLine("Example 1\nSection Extensions / TaskLists\n");
TestParser.TestSpec("- [ ] Item1\n- [x] Item2\n- [ ] Item3\n- Item4", "<ul class=\"contains-task-list\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> Item1</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> Item2</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> Item3</li>\n<li>Item4</li>\n</ul>", "tasklists|advanced");
}
// A task is not recognized outside a list item:
[Test]
public void ExtensionsTaskLists_Example002()
{
// Example 2
// Section: Extensions / TaskLists
//
// The following Markdown:
// [ ] This is not a task list
//
// Should be rendered as:
// <p>[ ] This is not a task list</p>
Console.WriteLine("Example 2\nSection Extensions / TaskLists\n");
TestParser.TestSpec("[ ] This is not a task list", "<p>[ ] This is not a task list</p>", "tasklists|advanced");
}
}
}

View File

@@ -0,0 +1,214 @@
// Generated: 21. 01. 2019 14:26:34
// --------------------------------
// Yaml
// --------------------------------
using System;
using NUnit.Framework;
namespace Markdig.Tests.Specs.Yaml
{
[TestFixture]
public class TestExtensionsYAMLFrontmatterDiscard
{
// # Extensions
//
// Adds support for YAML frontmatter parsing:
//
// ## YAML frontmatter discard
//
// If a frontmatter is present, it will not be rendered:
[Test]
public void ExtensionsYAMLFrontmatterDiscard_Example001()
{
// Example 1
// Section: Extensions / YAML frontmatter discard
//
// The following Markdown:
// ---
// this: is a frontmatter
// ---
// This is a text
//
// Should be rendered as:
// <p>This is a text</p>
Console.WriteLine("Example 1\nSection Extensions / YAML frontmatter discard\n");
TestParser.TestSpec("---\nthis: is a frontmatter\n---\nThis is a text", "<p>This is a text</p>", "yaml");
}
// But if a frontmatter doesn't happen on the first line, it will be parse as regular Markdown content
[Test]
public void ExtensionsYAMLFrontmatterDiscard_Example002()
{
// Example 2
// Section: Extensions / YAML frontmatter discard
//
// The following Markdown:
// This is a text1
// ---
// this: is a frontmatter
// ---
// This is a text2
//
// Should be rendered as:
// <h2>This is a text1</h2>
// <h2>this: is a frontmatter</h2>
// <p>This is a text2</p>
Console.WriteLine("Example 2\nSection Extensions / YAML frontmatter discard\n");
TestParser.TestSpec("This is a text1\n---\nthis: is a frontmatter\n---\nThis is a text2", "<h2>This is a text1</h2>\n<h2>this: is a frontmatter</h2>\n<p>This is a text2</p>", "yaml");
}
// It expects an exact 3 dashes `---`:
[Test]
public void ExtensionsYAMLFrontmatterDiscard_Example003()
{
// Example 3
// Section: Extensions / YAML frontmatter discard
//
// The following Markdown:
// ----
// this: is a frontmatter
// ----
// This is a text
//
// Should be rendered as:
// <hr />
// <h2>this: is a frontmatter</h2>
// <p>This is a text</p>
Console.WriteLine("Example 3\nSection Extensions / YAML frontmatter discard\n");
TestParser.TestSpec("----\nthis: is a frontmatter\n----\nThis is a text", "<hr />\n<h2>this: is a frontmatter</h2>\n<p>This is a text</p>", "yaml");
}
// It can end with three dots `...`:
[Test]
public void ExtensionsYAMLFrontmatterDiscard_Example004()
{
// Example 4
// Section: Extensions / YAML frontmatter discard
//
// The following Markdown:
// ---
// this: is a frontmatter
//
// ...
// This is a text
//
// Should be rendered as:
// <p>This is a text</p>
Console.WriteLine("Example 4\nSection Extensions / YAML frontmatter discard\n");
TestParser.TestSpec("---\nthis: is a frontmatter\n\n...\nThis is a text", "<p>This is a text</p>", "yaml");
}
// If the end front matter marker (`...` or `---`) is not present, it will render the `---` has a `<hr>`:
[Test]
public void ExtensionsYAMLFrontmatterDiscard_Example005()
{
// Example 5
// Section: Extensions / YAML frontmatter discard
//
// The following Markdown:
// ---
// this: is a frontmatter
// This is a text
//
// Should be rendered as:
// <hr />
// <p>this: is a frontmatter
// This is a text</p>
Console.WriteLine("Example 5\nSection Extensions / YAML frontmatter discard\n");
TestParser.TestSpec("---\nthis: is a frontmatter\nThis is a text", "<hr />\n<p>this: is a frontmatter\nThis is a text</p>", "yaml");
}
// It expects exactly three dots `...`:
[Test]
public void ExtensionsYAMLFrontmatterDiscard_Example006()
{
// Example 6
// Section: Extensions / YAML frontmatter discard
//
// The following Markdown:
// ---
// this: is a frontmatter
// ....
// This is a text
//
// Should be rendered as:
// <hr />
// <p>this: is a frontmatter
// ....
// This is a text</p>
Console.WriteLine("Example 6\nSection Extensions / YAML frontmatter discard\n");
TestParser.TestSpec("---\nthis: is a frontmatter\n....\nThis is a text", "<hr />\n<p>this: is a frontmatter\n....\nThis is a text</p>", "yaml");
}
// Front matter ends with the first line containing three dots `...` or three dashes `---`:
[Test]
public void ExtensionsYAMLFrontmatterDiscard_Example007()
{
// Example 7
// Section: Extensions / YAML frontmatter discard
//
// The following Markdown:
// ---
// this: is a frontmatter
// ....
//
// Hello
// ---
// This is a text
//
// Should be rendered as:
// <p>This is a text</p>
Console.WriteLine("Example 7\nSection Extensions / YAML frontmatter discard\n");
TestParser.TestSpec("---\nthis: is a frontmatter\n....\n\nHello\n---\nThis is a text", "<p>This is a text</p>", "yaml");
}
// It expects whitespace can exist after the leading characters
[Test]
public void ExtensionsYAMLFrontmatterDiscard_Example008()
{
// Example 8
// Section: Extensions / YAML frontmatter discard
//
// The following Markdown:
// ---
// this: is a frontmatter
// ...
// This is a text
//
// Should be rendered as:
// <p>This is a text</p>
Console.WriteLine("Example 8\nSection Extensions / YAML frontmatter discard\n");
TestParser.TestSpec("--- \nthis: is a frontmatter\n...\nThis is a text", "<p>This is a text</p>", "yaml");
}
// It expects whitespace can exist after the trailing characters
[Test]
public void ExtensionsYAMLFrontmatterDiscard_Example009()
{
// Example 9
// Section: Extensions / YAML frontmatter discard
//
// The following Markdown:
// ---
// this: is a frontmatter
// ...
// This is a text
//
// Should be rendered as:
// <p>This is a text</p>
Console.WriteLine("Example 9\nSection Extensions / YAML frontmatter discard\n");
TestParser.TestSpec("---\nthis: is a frontmatter\n... \nThis is a text", "<p>This is a text</p>", "yaml");
}
}
}

View File

@@ -0,0 +1,85 @@
using NUnit.Framework;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using System.Linq;
using System.Collections.Generic;
namespace Markdig.Tests
{
[TestFixture]
public class TestDescendantsOrder
{
[Test]
public void TestSchemas()
{
foreach (var markdown in TestParser.SpecsMarkdown)
{
AssertSameDescendantsOrder(markdown);
}
}
private void AssertSameDescendantsOrder(string markdown)
{
var syntaxTree = Markdown.Parse(markdown, new MarkdownPipelineBuilder().UseAdvancedExtensions().Build());
var descendants_legacy = Descendants_Legacy(syntaxTree).ToList();
var descendants_new = syntaxTree.Descendants().ToList();
Assert.AreEqual(descendants_legacy.Count, descendants_new.Count);
for (int i = 0; i < descendants_legacy.Count; i++)
{
Assert.AreSame(descendants_legacy[i], descendants_new[i]);
}
}
private static IEnumerable<MarkdownObject> Descendants_Legacy(MarkdownObject markdownObject)
{
// TODO: implement a recursiveless method
var block = markdownObject as ContainerBlock;
if (block != null)
{
foreach (var subBlock in block)
{
yield return subBlock;
foreach (var sub in Descendants_Legacy(subBlock))
{
yield return sub;
}
// Visit leaf block that have inlines
var leafBlock = subBlock as LeafBlock;
if (leafBlock?.Inline != null)
{
foreach (var subInline in Descendants_Legacy(leafBlock.Inline))
{
yield return subInline;
}
}
}
}
else
{
var inline = markdownObject as ContainerInline;
if (inline != null)
{
var child = inline.FirstChild;
while (child != null)
{
var next = child.NextSibling;
yield return child;
foreach (var sub in Descendants_Legacy(child))
{
yield return sub;
}
child = next;
}
}
}
}
}
}

View File

@@ -0,0 +1,168 @@
using Markdig.Parsers.Inlines;
using Markdig.Renderers;
using Markdig.Renderers.Html;
using Markdig.Syntax.Inlines;
using NUnit.Framework;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Markdig.Tests
{
[TestFixture]
public class TestEmphasisExtended
{
class EmphasisTestExtension : IMarkdownExtension
{
public void Setup(MarkdownPipelineBuilder pipeline)
{
var emphasisParser = pipeline.InlineParsers.Find<EmphasisInlineParser>();
Debug.Assert(emphasisParser != null);
foreach (var emphasis in EmphasisTestDescriptors)
{
emphasisParser.EmphasisDescriptors.Add(
new EmphasisDescriptor(emphasis.Character, emphasis.Minimum, emphasis.Maximum, true));
}
emphasisParser.TryCreateEmphasisInlineList.Add((delimiterChar, delimiterCount) =>
{
return delimiterChar == '*' || delimiterChar == '_'
? null
: new CustomEmphasisInline() { DelimiterChar = delimiterChar, DelimiterCount = delimiterCount };
});
}
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
renderer.ObjectRenderers.Insert(0, new EmphasisRenderer());
}
class EmphasisRenderer : HtmlObjectRenderer<CustomEmphasisInline>
{
protected override void Write(HtmlRenderer renderer, CustomEmphasisInline obj)
{
var tag = EmphasisTestDescriptors.First(test => test.Character == obj.DelimiterChar).Tags[obj.DelimiterCount];
renderer.Write(tag.OpeningTag);
renderer.WriteChildren(obj);
renderer.Write(tag.ClosingTag);
}
}
}
class Tag
{
#pragma warning disable CS0649
public int Level;
#pragma warning restore CS0649
public string RawTag;
public string OpeningTag;
public string ClosingTag;
public Tag(string tag)
{
RawTag = tag;
OpeningTag = "<" + tag + ">";
ClosingTag = "</" + tag + ">";
}
public static implicit operator Tag(string tag)
=> new Tag(tag);
}
class EmphasisTestDescriptor
{
public char Character;
public int Minimum;
public int Maximum;
public Dictionary<int, Tag> Tags = new Dictionary<int, Tag>();
private EmphasisTestDescriptor(char character, int min, int max)
{
Character = character;
Minimum = min;
Maximum = max;
}
public EmphasisTestDescriptor(char character, int min, int max, params Tag[] tags)
: this(character, min, max)
{
Debug.Assert(tags.Length == max - min + 1);
foreach (var tag in tags)
{
Tags.Add(min++, tag);
}
}
public EmphasisTestDescriptor(char character, int min, int max, string tag)
: this(character, min, max, new Tag(tag)) { }
}
class CustomEmphasisInline : EmphasisInline { }
static readonly EmphasisTestDescriptor[] EmphasisTestDescriptors = new[]
{
// Min Max
new EmphasisTestDescriptor('"', 1, 1, "quotation"),
new EmphasisTestDescriptor(',', 1, 2, "comma", "extra-comma"),
new EmphasisTestDescriptor('!', 2, 3, "warning", "error"),
new EmphasisTestDescriptor('=', 1, 3, "equal", "really-equal", "congruent"),
new EmphasisTestDescriptor('1', 1, 1, "one-only"),
new EmphasisTestDescriptor('2', 2, 2, "two-only"),
new EmphasisTestDescriptor('3', 3, 3, "three-only"),
};
static readonly MarkdownPipeline Pipeline = new MarkdownPipelineBuilder().Use<EmphasisTestExtension>().Build();
[Test]
[TestCase("*foo**", "<em>foo</em>*")]
[TestCase("**foo*", "*<em>foo</em>")]
[TestCase("***foo***", "<em><strong>foo</strong></em>")]
[TestCase("**_foo_**", "<strong><em>foo</em></strong>")]
[TestCase("_**foo**_", "<em><strong>foo</strong></em>")]
[TestCase("\"foo\"", "<quotation>foo</quotation>")]
[TestCase("\"\"foo\"\"", "<quotation><quotation>foo</quotation></quotation>")]
[TestCase("\"foo\"\"", "<quotation>foo</quotation>&quot;")]
[TestCase("\"\"foo\"", "&quot;<quotation>foo</quotation>")]
[TestCase(", foo", ", foo")]
[TestCase(", foo,", ", foo,")]
[TestCase(",some, foo,", "<comma>some</comma> foo,")]
[TestCase(",,foo,,", "<extra-comma>foo</extra-comma>")]
[TestCase(",foo,,", "<comma>foo</comma>,")]
[TestCase(",,,foo,,,", "<comma><extra-comma>foo</extra-comma></comma>")]
[TestCase("!1!", "!1!")]
[TestCase("!!2!!", "<warning>2</warning>")]
[TestCase("!!!3!!!", "<error>3</error>")]
[TestCase("!!!34!!!!", "<error>34</error>!")]
[TestCase("!!!!43!!!", "!<error>43</error>")]
[TestCase("!!!!44!!!!", "!<error>44!</error>")] // This is a new case - should the second ! be before or after </error>?
[TestCase("!!!!!5!!!!!", "<warning><error>5</error></warning>")]
[TestCase("!!!!!!6!!!!!!", "<error><error>6</error></error>")]
[TestCase("!! !mixed!!!", "!! !mixed!!!")] // can't open the delimiter because of the whitespace
[TestCase("=", "=")]
[TestCase("==", "==")]
[TestCase("====", "====")]
[TestCase("=a", "=a")]
[TestCase("=a=", "<equal>a</equal>")]
[TestCase("==a=", "=<equal>a</equal>")]
[TestCase("==a==", "<really-equal>a</really-equal>")]
[TestCase("==a===", "<really-equal>a</really-equal>=")]
[TestCase("===a===", "<congruent>a</congruent>")]
[TestCase("====a====", "<equal><congruent>a</congruent></equal>")]
[TestCase("=====a=====", "<really-equal><congruent>a</congruent></really-equal>")]
[TestCase("1", "1")]
[TestCase("1 1", "1 1")]
[TestCase("1Foo1", "<one-only>Foo</one-only>")]
[TestCase("1121", "1<one-only>2</one-only>")]
[TestCase("22322", "<two-only>3</two-only>")]
[TestCase("2232", "2232")]
[TestCase("333", "333")]
[TestCase("3334333", "<three-only>4</three-only>")]
[TestCase("33334333", "3<three-only>4</three-only>")]
[TestCase("33343333", "<three-only>4</three-only>3")]
[TestCase("122122", "<one-only>22</one-only>22")]
[TestCase("221221", "<two-only>1</two-only>1")]
[TestCase("122foo221", "<one-only><two-only>foo</two-only></one-only>")]
[TestCase("122foo122", "<one-only>22foo</one-only>22")]
[TestCase("!!!!!Attention:!! \"==1+1== 2\",but ===333 and 222===, mod 111!!!",
"<error><warning>Attention:</warning> <quotation><really-equal><one-only>+</one-only></really-equal> 2</quotation><comma>but <congruent>333 and 222</congruent></comma> mod 111</error>")]
public void TestEmphasis(string markdown, string expectedHtml)
{
TestParser.TestSpec(markdown, "<p>" + expectedHtml + "</p>", Pipeline);
}
}
}

View File

@@ -0,0 +1,45 @@
using NUnit.Framework;
using Markdig.Extensions.EmphasisExtras;
namespace Markdig.Tests
{
[TestFixture]
public class TestEmphasisExtraOptions
{
[Test]
public void OnlyStrikethrough_Single()
{
TestParser.TestSpec("~foo~", "<p>~foo~</p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough).Build());
}
[Test]
public void OnlyStrikethrough_Double()
{
TestParser.TestSpec("~~foo~~", "<p><del>foo</del></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough).Build());
}
[Test]
public void OnlySubscript_Single()
{
TestParser.TestSpec("~foo~", "<p><sub>foo</sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build());
}
[Test]
public void OnlySubscript_Double()
{
TestParser.TestSpec("~~foo~~", "<p><sub><sub>foo</sub></sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build());
}
[Test]
public void SubscriptAndStrikethrough_Single()
{
TestParser.TestSpec("~foo~", "<p><sub>foo</sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough | EmphasisExtraOptions.Subscript).Build());
}
[Test]
public void SubscriptAndStrikethrough_Double()
{
TestParser.TestSpec("~~foo~~", "<p><del>foo</del></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough | EmphasisExtraOptions.Subscript).Build());
}
}
}

View File

@@ -1,8 +1,6 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using NUnit.Framework;
namespace Markdig.Tests

View File

@@ -1,159 +1,285 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Markdig.Extensions.JiraLinks;
using NUnit.Framework;
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<EFBFBD><EFBFBD>__</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>");
}
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null)
{
foreach (var pipeline in GetPipeline(extensions))
{
Console.WriteLine($"Pipeline configured with extensions: {pipeline.Key}");
TestSpec(inputText, expectedOutputText, pipeline.Value);
}
}
public static void TestSpec(string inputText, string expectedOutputText, MarkdownPipeline pipeline)
{
// Uncomment this line to get more debug information for process inlines.
//pipeline.DebugLog = Console.Out;
var result = Markdown.ToHtml(inputText, pipeline);
result = Compact(result);
expectedOutputText = Compact(expectedOutputText);
Console.WriteLine("```````````````````Source");
Console.WriteLine(DisplaySpaceAndTabs(inputText));
Console.WriteLine("```````````````````Result");
Console.WriteLine(DisplaySpaceAndTabs(result));
Console.WriteLine("```````````````````Expected");
Console.WriteLine(DisplaySpaceAndTabs(expectedOutputText));
Console.WriteLine("```````````````````");
Console.WriteLine();
TextAssert.AreEqual(expectedOutputText, result);
}
private static IEnumerable<KeyValuePair<string, MarkdownPipeline>> GetPipeline(string extensionsGroupText)
{
// For the standard case, we make sure that both the CommmonMark core and Extra/Advanced are CommonMark compliant!
if (string.IsNullOrEmpty(extensionsGroupText))
{
yield return new KeyValuePair<string, MarkdownPipeline>("default", new MarkdownPipelineBuilder().Build());
yield return new KeyValuePair<string, MarkdownPipeline>("advanced", new MarkdownPipelineBuilder() // Use similar to advanced extension without auto-identifier
.UseAbbreviations()
//.UseAutoIdentifiers()
.UseCitations()
.UseCustomContainers()
.UseDefinitionLists()
.UseEmphasisExtras()
.UseFigures()
.UseFooters()
.UseFootnotes()
.UseGridTables()
.UseMathematics()
.UseMediaLinks()
.UsePipeTables()
.UseListExtras()
.UseGenericAttributes().Build());
yield break;
}
var extensionGroups = extensionsGroupText.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var extensionsText in extensionGroups)
{
var builder = new MarkdownPipelineBuilder();
builder.DebugLog = Console.Out;
if (extensionsText == "jiralinks")
{
builder.UseJiraLinks(new JiraLinkOptions("http://your.company.abc"));
}
else
{
builder = extensionsText == "self" ? builder.UseSelfPipeline() : builder.Configure(extensionsText);
}
yield return new KeyValuePair<string, MarkdownPipeline>(extensionsText, builder.Build());
}
}
public static string DisplaySpaceAndTabs(string text)
{
// Output special characters to check correctly the results
return text.Replace('\t', '→').Replace(' ', '·');
}
private static string Compact(string html)
{
// Normalize the output to make it compatible with CommonMark specs
html = html.Replace("\r\n", "\n").Replace(@"\r", @"\n").Trim();
html = Regex.Replace(html, @"\s+</li>", "</li>");
html = Regex.Replace(html, @"<li>\s+", "<li>");
html = html.Normalize(NormalizationForm.FormKD);
return html;
}
}
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Markdig.Extensions.JiraLinks;
using NUnit.Framework;
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()
{
// In CI, SpecFileGen is guaranteed to run
if (IsContinuousIntegration)
return;
foreach (var specFilePath in SpecsFilePaths)
{
string testFilePath = Path.ChangeExtension(specFilePath, ".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.");
DateTime specTime = File.GetLastWriteTimeUtc(specFilePath);
DateTime testTime = File.GetLastWriteTimeUtc(testFilePath);
// If file creation times aren't preserved by git, add some leeway
// If specs have come from git, assume that they were regenerated since CI would fail otherwise
testTime = testTime.AddSeconds(2);
// This might not catch a changed spec every time, but should most of the time. Otherwise CI will catch it
// This could also trigger, if a user has modified the spec file but reverted the change - can't think of a good workaround
Assert.Less(specTime, testTime,
$"{Path.GetFileName(specFilePath)} has been modified. Run SpecFileGen to regenerate the tests. " +
"If you have modified a specification file, but reverted all changes, ignore this error or revert the 'changed' timestamp metadata on the file.");
}
}
[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)
{
foreach (var pipeline in GetPipeline(extensions))
{
Console.WriteLine($"Pipeline configured with extensions: {pipeline.Key}");
TestSpec(inputText, expectedOutputText, pipeline.Value);
}
}
public static void TestSpec(string inputText, string expectedOutputText, MarkdownPipeline pipeline)
{
// Uncomment this line to get more debug information for process inlines.
//pipeline.DebugLog = Console.Out;
var result = Markdown.ToHtml(inputText, pipeline);
result = Compact(result);
expectedOutputText = Compact(expectedOutputText);
Console.WriteLine("```````````````````Source");
Console.WriteLine(DisplaySpaceAndTabs(inputText));
Console.WriteLine("```````````````````Result");
Console.WriteLine(DisplaySpaceAndTabs(result));
Console.WriteLine("```````````````````Expected");
Console.WriteLine(DisplaySpaceAndTabs(expectedOutputText));
Console.WriteLine("```````````````````");
Console.WriteLine();
TextAssert.AreEqual(expectedOutputText, result);
}
private static IEnumerable<KeyValuePair<string, MarkdownPipeline>> GetPipeline(string extensionsGroupText)
{
// For the standard case, we make sure that both the CommmonMark core and Extra/Advanced are CommonMark compliant!
if (string.IsNullOrEmpty(extensionsGroupText))
{
yield return new KeyValuePair<string, MarkdownPipeline>("default", new MarkdownPipelineBuilder().Build());
yield return new KeyValuePair<string, MarkdownPipeline>("advanced", new MarkdownPipelineBuilder() // Use similar to advanced extension without auto-identifier
.UseAbbreviations()
//.UseAutoIdentifiers()
.UseCitations()
.UseCustomContainers()
.UseDefinitionLists()
.UseEmphasisExtras()
.UseFigures()
.UseFooters()
.UseFootnotes()
.UseGridTables()
.UseMathematics()
.UseMediaLinks()
.UsePipeTables()
.UseListExtras()
.UseGenericAttributes().Build());
yield break;
}
var extensionGroups = extensionsGroupText.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var extensionsText in extensionGroups)
{
var builder = new MarkdownPipelineBuilder();
builder.DebugLog = Console.Out;
if (extensionsText == "jiralinks")
{
builder.UseJiraLinks(new JiraLinkOptions("http://your.company.abc"));
}
else
{
builder = extensionsText == "self" ? builder.UseSelfPipeline() : builder.Configure(extensionsText);
}
yield return new KeyValuePair<string, MarkdownPipeline>(extensionsText, builder.Build());
}
}
public static string DisplaySpaceAndTabs(string text)
{
// Output special characters to check correctly the results
return text.Replace('\t', '→').Replace(' ', '·');
}
private static string Compact(string html)
{
// Normalize the output to make it compatible with CommonMark specs
html = html.Replace("\r\n", "\n").Replace(@"\r", @"\n").Trim();
html = Regex.Replace(html, @"\s+</li>", "</li>");
html = Regex.Replace(html, @"<li>\s+", "<li>");
html = html.Normalize(NormalizationForm.FormKD);
return html;
}
public static readonly bool IsContinuousIntegration = Environment.GetEnvironmentVariable("CI") != null;
/// <summary>
/// Contains absolute paths to specification markdown files (order is the same as in <see cref="SpecsMarkdown"/>)
/// </summary>
public static readonly string[] SpecsFilePaths;
/// <summary>
/// Contains the markdown source for specification files (order is the same as in <see cref="SpecsFilePaths"/>)
/// </summary>
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"))
.ToArray();
SpecsMarkdown = new string[SpecsFilePaths.Length];
for (int i = 0; i < SpecsFilePaths.Length; i++)
{
SpecsMarkdown[i] = File.ReadAllText(SpecsFilePaths[i]);
}
}
}
}

View File

@@ -22,12 +22,12 @@ namespace Markdig.Tests
public static void AreEqual(string expectedValue, string actualValue)
{
AreEqual(actualValue, expectedValue, DiffStyle.Full, Console.Out);
AreEqual(expectedValue, actualValue, DiffStyle.Full, Console.Out);
}
public static void AreEqual(string expectedValue, string actualValue, DiffStyle diffStyle)
{
AreEqual(actualValue, expectedValue, diffStyle, Console.Out);
AreEqual(expectedValue, actualValue, diffStyle, Console.Out);
}
public static void AreEqual(string expectedValue, string actualValue, DiffStyle diffStyle, TextWriter output)

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.Collections.Generic;
@@ -12,7 +12,7 @@ namespace Markdig.Extensions.Abbreviations
/// <summary>
/// A block parser for abbreviations.
/// </summary>
/// <seealso cref="Markdig.Parsers.BlockParser" />
/// <seealso cref="BlockParser" />
public class AbbreviationParser : BlockParser
{
/// <summary>
@@ -40,8 +40,7 @@ namespace Markdig.Extensions.Abbreviations
}
SourceSpan labelSpan;
string label;
if (!LinkHelper.TryParseLabel(ref slice, out label, out labelSpan))
if (!LinkHelper.TryParseLabel(ref slice, out string label, out labelSpan))
{
return BlockState.None;
}
@@ -85,8 +84,7 @@ namespace Markdig.Extensions.Abbreviations
}
// Build a text matcher from the abbreviations labels
var labels = new HashSet<string>(abbreviations.Keys);
var matcher = new TextMatchHelper(labels);
var prefixTree = new CompactPrefixTree<Abbreviation>(abbreviations);
inlineProcessor.LiteralInlineParser.PostMatch += (InlineProcessor processor, ref StringSlice slice) =>
{
@@ -98,20 +96,35 @@ namespace Markdig.Extensions.Abbreviations
// This is slow, but we don't have much the choice
var content = literal.Content;
var text = content.Text;
for (int i = content.Start; i < content.End; i++)
{
string match;
if (matcher.TryMatch(text, i, content.End - i + 1, out match) && IsValidAbbreviation(match, content, i))
{
var indexAfterMatch = i + match.Length;
// We should have a match, but in case...
Abbreviation abbr;
if (!abbreviations.TryGetValue(match, out abbr))
for (int i = content.Start; i <= content.End; i++)
{
// Abbreviation must be a whole word == start at the start of a line or after a whitespace
if (i != 0)
{
for (i = i - 1; i <= content.End; i++)
{
if (text[i].IsWhitespace())
{
i++;
goto ValidAbbreviationStart;
}
}
break;
}
ValidAbbreviationStart:;
if (prefixTree.TryMatchLongest(text, i, content.End - i + 1, out KeyValuePair<string, Abbreviation> abbreviationMatch))
{
var match = abbreviationMatch.Key;
if (!IsValidAbbreviationEnding(match, content, i))
{
continue;
continue;
}
var indexAfterMatch = i + match.Length;
// If we don't have a container, create a new one
if (container == null)
{
@@ -124,13 +137,11 @@ namespace Markdig.Extensions.Abbreviations
};
}
int line;
int column;
var abbrInline = new AbbreviationInline(abbr)
var abbrInline = new AbbreviationInline(abbreviationMatch.Value)
{
Span =
{
Start = processor.GetSourcePosition(i, out line, out column),
Start = processor.GetSourcePosition(i, out int line, out int column),
},
Line = line,
Column = column
@@ -138,13 +149,9 @@ namespace Markdig.Extensions.Abbreviations
abbrInline.Span.End = abbrInline.Span.Start + match.Length - 1;
// Append the previous literal
if (i > content.Start)
{
if (literal.Parent == null)
{
container.AppendChild(literal);
}
if (i > content.Start && literal.Parent == null)
{
container.AppendChild(literal);
}
literal.Span.End = abbrInline.Span.Start - 1;
@@ -152,11 +159,10 @@ namespace Markdig.Extensions.Abbreviations
literal.Content.End = i - 1;
// Appned the abbreviation
// Append the abbreviation
container.AppendChild(abbrInline);
// If this is the end of the string, clear the literal
// and exit
// If this is the end of the string, clear the literal and exit
if (content.End == indexAfterMatch - 1)
{
literal = null;
@@ -188,34 +194,12 @@ namespace Markdig.Extensions.Abbreviations
};
}
private static bool IsValidAbbreviation(string match, StringSlice content, int matchIndex)
private static bool IsValidAbbreviationEnding(string match, StringSlice content, int matchIndex)
{
// The word matched must be embraced by punctuation or whitespace or \0.
var index = matchIndex - 1;
while (index >= content.Start)
{
var c = content.PeekCharAbsolute(index);
if (!(c == '\0' || c.IsWhitespace() || c.IsAsciiPunctuation()))
{
return false;
}
if (c.IsAlphaNumeric())
{
return false;
}
if (!c.IsAsciiPunctuation() || c.IsWhitespace())
{
break;
}
index--;
}
// This will check if the next char at the end of the StringSlice is whitespace, punctuation or \0.
var contentNew = content;
contentNew.End = content.End + 1;
index = matchIndex + match.Length;
int index = matchIndex + match.Length;
while (index <= contentNew.End)
{
var c = contentNew.PeekCharAbsolute(index);

View File

@@ -6,13 +6,14 @@ using Markdig.Parsers.Inlines;
using Markdig.Renderers;
using Markdig.Renderers.Html.Inlines;
using Markdig.Syntax.Inlines;
using System.Diagnostics;
namespace Markdig.Extensions.Citations
{
/// <summary>
/// Extension for cite ""...""
/// </summary>
/// <seealso cref="Markdig.IMarkdownExtension" />
/// <seealso cref="IMarkdownExtension" />
public class CitationExtension : IMarkdownExtension
{
public void Setup(MarkdownPipelineBuilder pipeline)
@@ -26,8 +27,7 @@ namespace Markdig.Extensions.Citations
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
if (renderer is HtmlRenderer htmlRenderer)
{
// Extend the rendering here.
var emphasisRenderer = renderer.ObjectRenderers.FindExact<EmphasisInlineRenderer>();
@@ -42,7 +42,8 @@ namespace Markdig.Extensions.Citations
private static string GetTag(EmphasisInline emphasisInline)
{
return emphasisInline.IsDouble && emphasisInline.DelimiterChar == '"' ? "cite" : null;
Debug.Assert(emphasisInline.DelimiterCount <= 2);
return emphasisInline.DelimiterCount == 2 && emphasisInline.DelimiterChar == '"' ? "cite" : null;
}
}
}

View File

@@ -1,16 +1,16 @@
// 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 Markdig.Parsers.Inlines;
using Markdig.Renderers;
using Markdig.Renderers;
namespace Markdig.Extensions.CustomContainers
{
/// <summary>
/// Extension to allow custom containers.
/// </summary>
/// <seealso cref="Markdig.IMarkdownExtension" />
/// <seealso cref="IMarkdownExtension" />
public class CustomContainerExtension : IMarkdownExtension
{
public void Setup(MarkdownPipelineBuilder pipeline)
@@ -26,22 +26,20 @@ namespace Markdig.Extensions.CustomContainers
if (inlineParser != null && !inlineParser.HasEmphasisChar(':'))
{
inlineParser.EmphasisDescriptors.Add(new EmphasisDescriptor(':', 2, 2, true));
var previousCreateEmphasisInline = inlineParser.CreateEmphasisInline;
inlineParser.CreateEmphasisInline = (emphasisChar, strong) =>
{
if (strong && emphasisChar == ':')
{
return new CustomContainerInline();
}
return previousCreateEmphasisInline?.Invoke(emphasisChar, strong);
};
inlineParser.TryCreateEmphasisInlineList.Add((emphasisChar, delimiterCount) =>
{
if (delimiterCount == 2 && emphasisChar == ':')
{
return new CustomContainerInline();
}
return null;
});
}
}
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
if (renderer is HtmlRenderer htmlRenderer)
{
if (!htmlRenderer.ObjectRenderers.Contains<HtmlCustomContainerRenderer>())
{

View File

@@ -9,7 +9,7 @@ namespace Markdig.Extensions.CustomContainers
/// <summary>
/// A HTML renderer for a <see cref="CustomContainerInline"/>.
/// </summary>
/// <seealso cref="Markdig.Renderers.Html.HtmlObjectRenderer{CustomContainerInline}" />
/// <seealso cref="Renderers.Html.HtmlObjectRenderer{CustomContainerInline}" />
public class HtmlCustomContainerInlineRenderer : HtmlObjectRenderer<CustomContainerInline>
{
protected override void Write(HtmlRenderer renderer, CustomContainerInline obj)

View File

@@ -3,8 +3,7 @@
// See the license.txt file in the project root for more information.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
using Markdig.Helpers;
using Markdig.Parsers;
@@ -13,13 +12,17 @@ namespace Markdig.Extensions.Emoji
/// <summary>
/// The inline parser used to for emoji.
/// </summary>
/// <seealso cref="Markdig.Parsers.InlineParser" />
/// <seealso cref="InlineParser" />
public class EmojiParser : InlineParser
{
private static readonly Dictionary<string, string> EmojiToUnicodeDefault;
private static readonly Dictionary<string, string> SmileyToEmojiDefault;
private static readonly CompactPrefixTree<string> EmojiPrefixTreeDefault;
private static readonly CompactPrefixTree<string> EmojiSmileyPrefixTreeDefault;
private static readonly char[] EmojiOpeningCharactersDefault;
private static readonly char[] EmojiSmileyOpeningCharactersDefault;
private TextMatchHelper textMatchHelper;
private CompactPrefixTree<string> _emojiPrefixTree;
/// <summary>
/// Initializes a new instance of the <see cref="EmojiParser"/> class.
@@ -28,8 +31,6 @@ namespace Markdig.Extensions.Emoji
{
EnableSmiley = enableSmiley;
OpeningCharacters = null;
EmojiToUnicode = new Dictionary<string, string>(EmojiToUnicodeDefault);
SmileyToEmoji = new Dictionary<string, string>(SmileyToEmojiDefault);
}
/// <summary>
@@ -37,95 +38,45 @@ namespace Markdig.Extensions.Emoji
/// </summary>
public bool EnableSmiley { get; set; }
/// <summary>
/// Gets the emoji to unicode mapping. This can be modified before this parser is initialized.
/// </summary>
public Dictionary<string, string> EmojiToUnicode { get; }
/// <summary>
/// Gets the smiley to emoji mapping. This can be modified before this parser is initialized.
/// </summary>
public Dictionary<string, string> SmileyToEmoji { get; }
public override void Initialize()
{
var firstChars = new HashSet<char>();
var textToMatch = new HashSet<string>();
foreach (var emoji in EmojiToUnicode)
{
firstChars.Add(emoji.Key[0]);
textToMatch.Add(emoji.Key);
}
foreach (var smiley in SmileyToEmoji)
{
firstChars.Add(smiley.Key[0]);
textToMatch.Add(smiley.Key);
}
textMatchHelper = new TextMatchHelper(textToMatch);
OpeningCharacters = new List<char>(firstChars).ToArray();
Array.Sort(OpeningCharacters);
OpeningCharacters = EnableSmiley ? EmojiSmileyOpeningCharactersDefault : EmojiOpeningCharactersDefault;
_emojiPrefixTree = EnableSmiley ? EmojiSmileyPrefixTreeDefault : EmojiPrefixTreeDefault;
}
public override bool Match(InlineProcessor processor, ref StringSlice slice)
{
string match;
// Previous char must be a space
if (!slice.PeekCharExtra(-1).IsWhiteSpaceOrZero())
if (!slice.PeekCharExtra(-1).IsWhiteSpaceOrZero())
{
return false;
}
// Try to match an emoji
if (!_emojiPrefixTree.TryMatchLongest(slice.Text, slice.Start, slice.Length, out KeyValuePair<string, string> match))
{
return false;
}
// Try to match an existing emoji
var startPosition = slice.Start;
if (!textMatchHelper.TryMatch(slice.Text, slice.Start, slice.Length, out match))
{
return false;
}
string emoji = match;
if (EnableSmiley)
{
// If we have a smiley, we decode it to emoji
if (!SmileyToEmoji.TryGetValue(match, out emoji))
{
emoji = match;
}
}
// Decode the eomji to unicode
string unicode;
if (!EmojiToUnicode.TryGetValue(emoji, out unicode))
{
// Should not happen but in case
return false;
}
// Move the cursor to the character after the matched string
slice.Start += match.Length;
// Push the EmojiInline
int line;
int column;
processor.Inline = new EmojiInline(unicode)
processor.Inline = new EmojiInline(match.Value)
{
Span =
{
Start = processor.GetSourcePosition(startPosition, out line, out column),
Start = processor.GetSourcePosition(slice.Start, out int line, out int column),
},
Line = line,
Column = column,
Match = match
Match = match.Key
};
processor.Inline.Span.End = processor.Inline.Span.Start + match.Length - 1;
processor.Inline.Span.End = processor.Inline.Span.Start + match.Key.Length - 1;
// Move the cursor to the character after the matched string
slice.Start += match.Key.Length;
return true;
}
}
#region Emojis and Smileys
static EmojiParser()
{
@@ -1082,13 +1033,41 @@ namespace Markdig.Extensions.Emoji
// Custom arrows
{"<-", ":custom_arrow_left:" },
{"->", ":custom_arrow_rigth:" },
{"<->", ":custom_arrow_left_rigth:" },
{"->", ":custom_arrow_right:" },
{"<->", ":custom_arrow_left_right:" },
{"<=", ":custom_arrow_left_strong:" },
{"=>", ":custom_arrow_rigth_strong:" },
{"<=>", ":custom_arrow_left_rigth_strong:" },
};
{"=>", ":custom_arrow_right_strong:" },
{"<=>", ":custom_arrow_left_right_strong:" },
};
// Build Emoji and Smiley CompactPrefixTree
EmojiPrefixTreeDefault = new CompactPrefixTree<string>(EmojiToUnicodeDefault);
int jointCount = EmojiToUnicodeDefault.Count + SmileyToEmojiDefault.Count;
// Count * 2 seems to be a good fit for the data set
EmojiSmileyPrefixTreeDefault = new CompactPrefixTree<string>(jointCount, jointCount * 2, jointCount * 2);
// This is not the best data set for the prefix tree as it will have to check the first character linearly
// A work-around would require a bunch of substrings / removing the leading ':' from emojis, neither one is pretty
// This way we sacrifice a few microseconds for not introducing breaking changes, emojis aren't all that common anyhow
var firstChars = new HashSet<char> { ':' };
foreach (var emoji in EmojiToUnicodeDefault)
EmojiSmileyPrefixTreeDefault.Add(emoji);
foreach (var smiley in SmileyToEmojiDefault)
{
if (!EmojiToUnicodeDefault.TryGetValue(smiley.Value, out string unicode))
throw new ArgumentException("Invalid smiley target: {0} is not present in the emoji dictionary", smiley.Value);
firstChars.Add(smiley.Key[0]);
if (!EmojiSmileyPrefixTreeDefault.TryAdd(smiley.Key, unicode))
throw new ArgumentException("Smiley {0} is already present in the Emoji dictionary", smiley.Key);
}
EmojiOpeningCharactersDefault = new[] { ':' };
EmojiSmileyOpeningCharactersDefault = new List<char>(firstChars).ToArray();
}
#endregion
}

View File

@@ -6,13 +6,14 @@ using Markdig.Parsers.Inlines;
using Markdig.Renderers;
using Markdig.Renderers.Html.Inlines;
using Markdig.Syntax.Inlines;
using System.Diagnostics;
namespace Markdig.Extensions.EmphasisExtras
{
/// <summary>
/// Extension for strikethrough, subscript, superscript, inserted and marked.
/// </summary>
/// <seealso cref="Markdig.IMarkdownExtension" />
/// <seealso cref="IMarkdownExtension" />
public class EmphasisExtraExtension : IMarkdownExtension
{
/// <summary>
@@ -67,8 +68,10 @@ namespace Markdig.Extensions.EmphasisExtras
}
if (requireTilde && !hasTilde)
{
parser.EmphasisDescriptors.Add(new EmphasisDescriptor('~', 1, 2, true));
{
int minimumCount = (Options & EmphasisExtraOptions.Subscript) != 0 ? 1 : 2;
int maximumCount = (Options & EmphasisExtraOptions.Strikethrough) != 0 ? 2 : 1;
parser.EmphasisDescriptors.Add(new EmphasisDescriptor('~', minimumCount, maximumCount, true));
}
if (requireSup && !hasSup)
{
@@ -87,8 +90,7 @@ namespace Markdig.Extensions.EmphasisExtras
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
if (renderer is HtmlRenderer htmlRenderer)
{
// Extend the rendering here.
var emphasisRenderer = htmlRenderer.ObjectRenderers.FindExact<EmphasisInlineRenderer>();
@@ -105,8 +107,9 @@ namespace Markdig.Extensions.EmphasisExtras
var c = emphasisInline.DelimiterChar;
switch (c)
{
case '~':
return emphasisInline.IsDouble ? "del" : "sub";
case '~':
Debug.Assert(emphasisInline.DelimiterCount <= 2);
return emphasisInline.DelimiterCount == 2 ? "del" : "sub";
case '^':
return "sup";
case '+':

View File

@@ -15,8 +15,10 @@ namespace Markdig.Extensions.Mathematics
protected override void Write(HtmlRenderer renderer, MathBlock obj)
{
renderer.EnsureLine();
renderer.Write("<div").WriteAttributes(obj).Write(">");
renderer.Write("<div").WriteAttributes(obj).WriteLine(">");
renderer.WriteLine("\\[");
renderer.WriteLeafRawLines(obj, true, true);
renderer.Write("\\]");
renderer.WriteLine("</div>");
}
}

View File

@@ -14,9 +14,9 @@ namespace Markdig.Extensions.Mathematics
{
protected override void Write(HtmlRenderer renderer, MathInline obj)
{
renderer.Write("<span").WriteAttributes(obj).Write(">");
renderer.Write("<span").WriteAttributes(obj).Write(">\\(");
renderer.WriteEscape(ref obj.Content);
renderer.Write("</span>");
renderer.Write("\\)</span>");
}
}
}

View File

@@ -26,15 +26,15 @@ namespace Markdig.Extensions.SmartyPants
{
// We are matching the following characters:
//
// ' &lsquo; &rsquo; 'left-single-quote', 'right-single-quote'
// '' “ ” &ldquo; &rdquo; 'left-double-quote', 'right-double-quote'
// " “ ” &ldquo; &rdquo; 'left-double-quote', 'right-double-quote'
// << >> « » &laquo; &raquo; 'left-angle-quote', 'right-angle-quote'
// ... … &hellip; 'ellipsis'
// ' &lsquo; &rsquo; 'left-single-quote', 'right-single-quote'
// '' “ ” &ldquo; &rdquo; 'left-double-quote', 'right-double-quote'
// " “ ” &ldquo; &rdquo; 'left-double-quote', 'right-double-quote'
// << >> « » &laquo; &raquo; 'left-angle-quote', 'right-angle-quote'
// ... … &hellip; 'ellipsis'
// Special case: &ndash; and &mdash; are handle as a PostProcess step to avoid conflicts with pipetables header separator row
// -- &ndash; 'ndash'
// --- — &mdash; 'mdash'
// -- &ndash; 'ndash'
// --- — &mdash; 'mdash'
var pc = slice.PeekCharExtra(-1);
var c = slice.CurrentChar;

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;
@@ -69,7 +69,7 @@ namespace Markdig.Extensions.Tables
renderer.WriteLine("<tbody>");
hasBody = true;
}
renderer.WriteLine("<tr>");
renderer.Write("<tr").WriteAttributes(row).WriteLine(">");
for (int i = 0; i < row.Count; i++)
{
var cellObj = row[i];

View File

@@ -71,12 +71,8 @@ namespace Markdig.Helpers
// (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.
bool nextIsPunctuation;
bool nextIsWhiteSpace;
bool prevIsPunctuation;
bool prevIsWhiteSpace;
pc.CheckUnicodeCategory(out prevIsWhiteSpace, out prevIsPunctuation);
c.CheckUnicodeCategory(out nextIsWhiteSpace, out nextIsPunctuation);
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);

File diff suppressed because it is too large Load Diff

View File

@@ -1,127 +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.
using System;
using System.Collections.Generic;
namespace Markdig.Helpers
{
/// <summary>
/// Match a text against a list of ASCII string using internally a tree to speedup the lookup
/// </summary>
public class TextMatchHelper
{
private readonly CharNode root;
private readonly ListCache listCache;
/// <summary>
/// Initializes a new instance of the <see cref="TextMatchHelper"/> class.
/// </summary>
/// <param name="matches">The matches to match against.</param>
/// <exception cref="System.ArgumentNullException"></exception>
public TextMatchHelper(HashSet<string> matches)
{
if (matches == null) throw new ArgumentNullException(nameof(matches));
var list = new List<string>(matches);
root = new CharNode();
listCache = new ListCache();
BuildMap(root, 0, list);
listCache.Clear();
}
/// <summary>
/// Tries to match in the text, at offset position, the list of string matches registered to this instance.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="offset">The offset.</param>
/// <param name="length">The length.</param>
/// <param name="match">The match string if the match was successfull.</param>
/// <returns>
/// <c>true</c> if the match was successfull; <c>false</c> otherwise
/// </returns>
/// <exception cref="System.ArgumentNullException"></exception>
public bool TryMatch(string text, int offset, int length, out string match)
{
if (text == null) throw new ArgumentNullException(nameof(text));
// TODO(lazy): we should check offset and length for a better exception experience in case of wrong usage
var node = root;
match = null;
while (length > 0)
{
var c = text[offset];
CharNode nextNode;
if (!node.TryGetValue(c, out nextNode))
{
break;
}
node = nextNode;
offset++;
length--;
}
if (node.Content != null)
{
match = node.Content;
return true;
}
return false;
}
private void BuildMap(CharNode node, int index, List<string> list)
{
// TODO(lazy): This code for building the nodes is not very efficient in terms of memory usage and could be optimized (using structs and indices)
// At least, we are using a cache for the temporary objects build (List<string>)
for (int i = 0; i < list.Count; i++)
{
var str = list[i];
var c = str[index];
CharNode nextNode;
if (!node.TryGetValue(c, out nextNode))
{
nextNode = new CharNode();
node.Add(c, nextNode);
}
// We have found a string for this node
if (index + 1 == str.Length)
{
nextNode.Content = str;
}
else
{
if (nextNode.NextList == null)
{
nextNode.NextList = listCache.Get();
}
nextNode.NextList.Add(str);
}
}
foreach (var charList in node)
{
if (charList.Value.NextList != null)
{
BuildMap(charList.Value, index + 1, charList.Value.NextList);
listCache.Release(charList.Value.NextList);
charList.Value.NextList = null;
}
}
}
private class ListCache : DefaultObjectCache<List<string>>
{
protected override void Reset(List<string> instance)
{
instance.Clear();
}
}
private class CharNode : Dictionary<char, CharNode>
{
public List<string> NextList;
public string Content { get; set; }
}
}
}

View File

@@ -0,0 +1,90 @@
using System;
using System.Diagnostics;
namespace Markdig.Helpers
{
/// <summary>
/// Inspired by CoreLib, taken from https://github.com/MihaZupan/SharpCollections, cc @MihaZupan
/// </summary>
internal static class ThrowHelper
{
public static void ThrowArgumentNullException(ExceptionArgument argument)
{
throw new ArgumentNullException(GetArgumentName(argument));
}
public static void ThrowArgumentException(ExceptionArgument argument, ExceptionReason reason)
{
throw new ArgumentException(GetArgumentName(argument), GetExceptionReason(reason));
}
public static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionReason reason)
{
throw new ArgumentOutOfRangeException(GetArgumentName(argument), GetExceptionReason(reason));
}
public static void ThrowIndexOutOfRangeException()
{
throw new IndexOutOfRangeException();
}
private static string GetArgumentName(ExceptionArgument argument)
{
switch (argument)
{
case ExceptionArgument.key:
case ExceptionArgument.input:
case ExceptionArgument.value:
case ExceptionArgument.length:
case ExceptionArgument.text:
return argument.ToString();
case ExceptionArgument.offsetLength:
return "offset and length";
default:
Debug.Assert(false, "The enum value is not defined, please check the ExceptionArgument Enum.");
return "";
}
}
private static string GetExceptionReason(ExceptionReason reason)
{
switch (reason)
{
case ExceptionReason.String_Empty:
return "String must not be empty.";
case ExceptionReason.SmallCapacity:
return "Capacity was less than the current size.";
case ExceptionReason.InvalidOffsetLength:
return "Offset and length must refer to a position in the string.";
case ExceptionReason.DuplicateKey:
return "The given key is already present in the dictionary.";
default:
Debug.Assert(false, "The enum value is not defined, please check the ExceptionReason Enum.");
return "";
}
}
}
internal enum ExceptionArgument
{
key,
input,
value,
length,
offsetLength,
text
}
internal enum ExceptionReason
{
String_Empty,
SmallCapacity,
InvalidOffsetLength,
DuplicateKey,
}
}

View File

@@ -5,19 +5,20 @@
<Copyright>Alexandre Mutel</Copyright>
<AssemblyTitle>Markdig</AssemblyTitle>
<NeutralLanguage>en-US</NeutralLanguage>
<VersionPrefix>0.15.6</VersionPrefix>
<VersionPrefix>0.16.0</VersionPrefix>
<Authors>Alexandre Mutel</Authors>
<TargetFrameworks>net35;net40;portable40-net40+sl5+win8+wp8+wpa81;netstandard1.1;netstandard2.0;uap10.0</TargetFrameworks>
<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>
<PackageLicenseUrl>https://github.com/lunet-io/markdig/blob/master/license.txt</PackageLicenseUrl>
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.1' ">1.6.0</NetStandardImplicitPackageVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net35' ">
@@ -54,6 +55,10 @@
<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>

View File

@@ -29,11 +29,12 @@ namespace Markdig
/// <param name="markdown">The markdown.</param>
/// <param name="options">The normalize options</param>
/// <param name="pipeline">The pipeline.</param>
/// <param name="context">A parser context used for the parsing.</param>
/// <returns>A normalized markdown text.</returns>
public static string Normalize(string markdown, NormalizeOptions options = null, MarkdownPipeline pipeline = null)
public static string Normalize(string markdown, NormalizeOptions options = null, MarkdownPipeline pipeline = null, MarkdownParserContext context = null)
{
var writer = new StringWriter();
Normalize(markdown, writer, options, pipeline);
Normalize(markdown, writer, options, pipeline, context);
return writer.ToString();
}
@@ -44,8 +45,9 @@ namespace Markdig
/// <param name="writer">The destination <see cref="TextWriter"/> that will receive the result of the conversion.</param>
/// <param name="options">The normalize options</param>
/// <param name="pipeline">The pipeline.</param>
/// <param name="context">A parser context used for the parsing.</param>
/// <returns>A normalized markdown text.</returns>
public static MarkdownDocument Normalize(string markdown, TextWriter writer, NormalizeOptions options = null, MarkdownPipeline pipeline = null)
public static MarkdownDocument Normalize(string markdown, TextWriter writer, NormalizeOptions options = null, MarkdownPipeline pipeline = null, MarkdownParserContext context = null)
{
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
pipeline = CheckForSelfPipeline(pipeline, markdown);
@@ -54,7 +56,7 @@ namespace Markdig
var renderer = new NormalizeRenderer(writer, options);
pipeline.Setup(renderer);
var document = Parse(markdown, pipeline);
var document = Parse(markdown, pipeline, context);
renderer.Render(document);
writer.Flush();
@@ -82,9 +84,10 @@ namespace Markdig
/// <param name="markdown">A Markdown text.</param>
/// <param name="writer">The destination <see cref="TextWriter"/> that will receive the result of the conversion.</param>
/// <param name="pipeline">The pipeline used for the conversion.</param>
/// <param name="context">A parser context used for the parsing.</param>
/// <returns>The Markdown document that has been parsed</returns>
/// <exception cref="System.ArgumentNullException">if reader or writer variable are null</exception>
public static MarkdownDocument ToHtml(string markdown, TextWriter writer, MarkdownPipeline pipeline = null)
public static MarkdownDocument ToHtml(string markdown, TextWriter writer, MarkdownPipeline pipeline = null, MarkdownParserContext context = null)
{
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
if (writer == null) throw new ArgumentNullException(nameof(writer));
@@ -95,7 +98,7 @@ namespace Markdig
var renderer = new HtmlRenderer(writer);
pipeline.Setup(renderer);
var document = Parse(markdown, pipeline);
var document = Parse(markdown, pipeline, context);
renderer.Render(document);
writer.Flush();
@@ -108,15 +111,16 @@ namespace Markdig
/// <param name="markdown">A Markdown text.</param>
/// <param name="renderer">The renderer to convert Markdown to.</param>
/// <param name="pipeline">The pipeline used for the conversion.</param>
/// <param name="context">A parser context used for the parsing.</param>
/// <exception cref="System.ArgumentNullException">if markdown or writer variable are null</exception>
public static object Convert(string markdown, IMarkdownRenderer renderer, MarkdownPipeline pipeline = null)
public static object Convert(string markdown, IMarkdownRenderer renderer, MarkdownPipeline pipeline = null, MarkdownParserContext context = null)
{
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
pipeline = CheckForSelfPipeline(pipeline, markdown);
var document = Parse(markdown, pipeline);
var document = Parse(markdown, pipeline, context);
pipeline.Setup(renderer);
return renderer.Render(document);
}
@@ -138,15 +142,16 @@ namespace Markdig
/// </summary>
/// <param name="markdown">The markdown text.</param>
/// <param name="pipeline">The pipeline used for the parsing.</param>
/// <param name="context">A parser context used for the parsing.</param>
/// <returns>An AST Markdown document</returns>
/// <exception cref="System.ArgumentNullException">if markdown variable is null</exception>
public static MarkdownDocument Parse(string markdown, MarkdownPipeline pipeline)
public static MarkdownDocument Parse(string markdown, MarkdownPipeline pipeline, MarkdownParserContext context = null)
{
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
pipeline = CheckForSelfPipeline(pipeline, markdown);
return MarkdownParser.Parse(markdown, pipeline);
return MarkdownParser.Parse(markdown, pipeline, context);
}
private static MarkdownPipeline CheckForSelfPipeline(MarkdownPipeline pipeline, string markdown)
@@ -165,9 +170,10 @@ namespace Markdig
/// <param name="markdown">A Markdown text.</param>
/// <param name="writer">The destination <see cref="TextWriter"/> that will receive the result of the conversion.</param>
/// <param name="pipeline">The pipeline used for the conversion.</param>
/// <param name="context">A parser context used for the parsing.</param>
/// <returns>The Markdown document that has been parsed</returns>
/// <exception cref="System.ArgumentNullException">if reader or writer variable are null</exception>
public static MarkdownDocument ToPlainText(string markdown, TextWriter writer, MarkdownPipeline pipeline = null)
public static MarkdownDocument ToPlainText(string markdown, TextWriter writer, MarkdownPipeline pipeline = null, MarkdownParserContext context = null)
{
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
if (writer == null) throw new ArgumentNullException(nameof(writer));
@@ -182,7 +188,7 @@ namespace Markdig
};
pipeline.Setup(renderer);
var document = Parse(markdown, pipeline);
var document = Parse(markdown, pipeline, context);
renderer.Render(document);
writer.Flush();
@@ -194,13 +200,14 @@ namespace Markdig
/// </summary>
/// <param name="markdown">A Markdown text.</param>
/// <param name="pipeline">The pipeline used for the conversion.</param>
/// <param name="context">A parser context used for the parsing.</param>
/// <returns>The result of the conversion</returns>
/// <exception cref="System.ArgumentNullException">if markdown variable is null</exception>
public static string ToPlainText(string markdown, MarkdownPipeline pipeline = null)
public static string ToPlainText(string markdown, MarkdownPipeline pipeline = null, MarkdownParserContext context = null)
{
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
var writer = new StringWriter();
ToPlainText(markdown, writer, pipeline);
ToPlainText(markdown, writer, pipeline, context);
return writer.ToString();
}
}

View File

@@ -0,0 +1,23 @@
using System.Collections.Generic;
namespace Markdig
{
/// <summary>
/// Provides a context that can be used as part of parsing Markdown documents.
/// </summary>
public sealed class MarkdownParserContext
{
/// <summary>
/// Gets or sets the context property collection.
/// </summary>
public Dictionary<object, object> Properties { get; }
/// <summary>
/// Initializes a new instance of the <see cref="MarkdownParserContext" /> class.
/// </summary>
public MarkdownParserContext()
{
Properties = new Dictionary<object, object>();
}
}
}

View File

@@ -41,7 +41,7 @@ namespace Markdig.Parsers
/// <param name="parsers">The list of parsers.</param>
/// <exception cref="System.ArgumentNullException">
/// </exception>
public BlockProcessor(StringBuilderCache stringBuilders, MarkdownDocument document, BlockParserList parsers)
public BlockProcessor(StringBuilderCache stringBuilders, MarkdownDocument document, BlockParserList parsers, MarkdownParserContext context)
{
if (stringBuilders == null) throw new ArgumentNullException(nameof(stringBuilders));
if (document == null) throw new ArgumentNullException(nameof(document));
@@ -51,6 +51,7 @@ namespace Markdig.Parsers
Document = document;
document.IsOpen = true;
Parsers = parsers;
Context = context;
OpenedBlocks = new List<Block>();
NewBlocks = new Stack<Block>();
root = this;
@@ -67,6 +68,11 @@ namespace Markdig.Parsers
/// </summary>
public BlockParserList Parsers { get; }
/// <summary>
/// Gets the parser context or <c>null</c> if none is available.
/// </summary>
public MarkdownParserContext Context { get; }
/// <summary>
/// Gets the current active container.
/// </summary>

View File

@@ -1,6 +1,7 @@
// 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.Diagnostics;
using Markdig.Helpers;
using Markdig.Syntax;
@@ -21,6 +22,11 @@ namespace Markdig.Parsers
OpeningCharacters = new[] {'#'};
}
/// <summary>
/// Gets or sets the max count of the leading unescaped # characters
/// </summary>
public int MaxLeadingCount { get; set; } = 6;
/// <summary>
/// A delegates that allows to process attached attributes after #
/// </summary>
@@ -36,7 +42,7 @@ namespace Markdig.Parsers
// 4.2 ATX headings
// An ATX heading consists of a string of characters, parsed as inline content,
// between an opening sequence of 16 unescaped # characters and an optional
// between an opening sequence of 16(configurable) unescaped # characters and an optional
// closing sequence of any number of unescaped # characters. The opening sequence
// of # characters must be followed by a space or by the end of line. The optional
// closing sequence of #s must be preceded by a space and may be followed by spaces
@@ -50,8 +56,9 @@ namespace Markdig.Parsers
var c = line.CurrentChar;
var matchingChar = c;
Debug.Assert(MaxLeadingCount > 0);
int leadingCount = 0;
while (c != '\0' && leadingCount <= 6)
while (c != '\0' && leadingCount <= MaxLeadingCount)
{
if (c != matchingChar)
{
@@ -62,7 +69,7 @@ namespace Markdig.Parsers
}
// A space is required after leading #
if (leadingCount > 0 && leadingCount <= 6 && (c.IsSpaceOrTab() || c == '\0'))
if (leadingCount > 0 && leadingCount <= MaxLeadingCount && (c.IsSpaceOrTab() || c == '\0'))
{
// Move to the content
var headingBlock = new HeadingBlock(this)

View File

@@ -36,7 +36,7 @@ namespace Markdig.Parsers
/// <param name="inlineCreated">The inline created event.</param>
/// <exception cref="System.ArgumentNullException">
/// </exception>
public InlineProcessor(StringBuilderCache stringBuilders, MarkdownDocument document, InlineParserList parsers, bool preciseSourcelocation)
public InlineProcessor(StringBuilderCache stringBuilders, MarkdownDocument document, InlineParserList parsers, bool preciseSourcelocation, MarkdownParserContext context)
{
if (stringBuilders == null) throw new ArgumentNullException(nameof(stringBuilders));
if (document == null) throw new ArgumentNullException(nameof(document));
@@ -44,6 +44,7 @@ namespace Markdig.Parsers
StringBuilders = stringBuilders;
Document = document;
Parsers = parsers;
Context = context;
PreciseSourceLocation = preciseSourcelocation;
lineOffsets = new List<StringLineGroup.LineOffset>();
ParserStates = new object[Parsers.Count];
@@ -80,6 +81,11 @@ namespace Markdig.Parsers
/// </summary>
public InlineParserList Parsers { get; }
/// <summary>
/// Gets the parser context or <c>null</c> if none is available.
/// </summary>
public MarkdownParserContext Context { get; }
/// <summary>
/// Gets the root document.
/// </summary>

View File

@@ -23,7 +23,6 @@ namespace Markdig.Parsers.Inlines
if (minimumCount < 1) throw new ArgumentOutOfRangeException(nameof(minimumCount), "minimumCount must be >= 1");
if (maximumCount < 1) throw new ArgumentOutOfRangeException(nameof(maximumCount), "maximumCount must be >= 1");
if (minimumCount > maximumCount) throw new ArgumentOutOfRangeException(nameof(minimumCount), "minimumCount must be <= maximumCount");
if (maximumCount > 2) throw new ArgumentOutOfRangeException(nameof(maximumCount), "maximum must be <= 2");
Character = character;
MinimumCount = minimumCount;
@@ -42,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 and &lt;= 2)
/// The maximum number of character this emphasis is expected to have (must be >=1 and >= minumunCount)
/// </summary>
public readonly int MaximumCount;

View File

@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Markdig.Helpers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
@@ -14,14 +15,16 @@ namespace Markdig.Parsers.Inlines
/// <summary>
/// An inline parser for <see cref="EmphasisInline"/>.
/// </summary>
/// <seealso cref="Markdig.Parsers.InlineParser" />
/// <seealso cref="InlineParser" />
/// <seealso cref="IPostInlineProcessor" />
public class EmphasisInlineParser : InlineParser, IPostInlineProcessor
{
private CharacterMap<EmphasisDescriptor> emphasisMap;
private readonly DelimitersObjectCache inlinesCache;
private readonly DelimitersObjectCache inlinesCache = new DelimitersObjectCache();
[Obsolete("Use TryCreateEmphasisInlineDelegate instead", error: false)]
public delegate EmphasisInline CreateEmphasisInlineDelegate(char emphasisChar, bool isStrong);
public delegate EmphasisInline TryCreateEmphasisInlineDelegate(char emphasisChar, int delimiterCount);
/// <summary>
/// Initializes a new instance of the <see cref="EmphasisInlineParser"/> class.
@@ -33,7 +36,6 @@ namespace Markdig.Parsers.Inlines
new EmphasisDescriptor('*', 1, 2, true),
new EmphasisDescriptor('_', 1, 2, false)
};
inlinesCache = new DelimitersObjectCache();
}
/// <summary>
@@ -61,7 +63,9 @@ namespace Markdig.Parsers.Inlines
/// <summary>
/// Gets or sets the create emphasis inline delegate (allowing to create a different emphasis inline class)
/// </summary>
[Obsolete("Use TryCreateEmphasisInlineList instead", error: false)]
public CreateEmphasisInlineDelegate CreateEmphasisInline { get; set; }
public readonly List<TryCreateEmphasisInlineDelegate> TryCreateEmphasisInlineList = new List<TryCreateEmphasisInlineDelegate>();
public override void Initialize()
{
@@ -87,17 +91,16 @@ namespace Markdig.Parsers.Inlines
public bool PostProcess(InlineProcessor state, Inline root, Inline lastChild, int postInlineProcessorIndex, bool isFinalProcessing)
{
var container = root as ContainerInline;
if (container == null)
if (!(root is ContainerInline container))
{
return true;
}
List<EmphasisDelimiterInline> delimiters = null;
if (container is EmphasisDelimiterInline)
if (container is EmphasisDelimiterInline emphasisDelimiter)
{
delimiters = inlinesCache.Get();
delimiters.Add((EmphasisDelimiterInline)container);
delimiters.Add(emphasisDelimiter);
}
// Move current_position forward in the delimiter stack (if needed) until
@@ -109,8 +112,7 @@ namespace Markdig.Parsers.Inlines
{
break;
}
var delimiter = child as EmphasisDelimiterInline;
if (delimiter != null)
if (child is EmphasisDelimiterInline delimiter)
{
if (delimiters == null)
{
@@ -131,18 +133,17 @@ namespace Markdig.Parsers.Inlines
}
public override bool Match(InlineProcessor processor, ref StringSlice slice)
{
// First, some definitions. A delimiter run is either a sequence of one or more * characters that
// is not preceded or followed by a * character, or a sequence of one or more _ characters that
// is not preceded or followed by a _ character.
{
// First, some definitions.
// A delimiter run is a sequence of one or more delimiter characters that is not preceded or followed by the same delimiter character
// The amount of delimiter characters in the delimiter run may exceed emphasisDesc.MaximumCount, as that is handeled in `ProcessEmphasis`
var delimiterChar = slice.CurrentChar;
var emphasisDesc = emphasisMap[delimiterChar];
var pc = (char)0;
if (processor.Inline is HtmlEntityInline)
char pc = (char)0;
if (processor.Inline is HtmlEntityInline htmlEntityInline)
{
var htmlEntityInline = (HtmlEntityInline) processor.Inline;
if (htmlEntityInline.Transcoded.Length > 0)
{
pc = htmlEntityInline.Transcoded[htmlEntityInline.Transcoded.End];
@@ -152,7 +153,10 @@ namespace Markdig.Parsers.Inlines
{
pc = slice.PeekCharExtra(-1);
if (pc == delimiterChar && slice.PeekCharExtra(-2) != '\\')
{
{
// If we get here, we determined that either:
// a) there weren't enough delimiters in the delimiter run to satisfy the MinimumCount condition
// b) the previous character couldn't open/close
return false;
}
}
@@ -174,38 +178,26 @@ namespace Markdig.Parsers.Inlines
}
// The following character is actually an entity, we need to decode it
int htmlLength;
string htmlString;
if (HtmlEntityParser.TryParse(ref slice, out htmlString, out htmlLength))
if (HtmlEntityParser.TryParse(ref slice, out string htmlString, out int htmlLength))
{
c = htmlString[0];
}
// Calculate Open-Close for current character
bool canOpen;
bool canClose;
CharHelper.CheckOpenCloseDelimiter(pc, c, emphasisDesc.EnableWithinWord, out canOpen, out canClose);
CharHelper.CheckOpenCloseDelimiter(pc, c, emphasisDesc.EnableWithinWord, out bool canOpen, out bool canClose);
// We have potentially an open or close emphasis
if (canOpen || canClose)
{
var delimiterType = DelimiterType.Undefined;
if (canOpen)
{
delimiterType |= DelimiterType.Open;
}
if (canClose)
{
delimiterType |= DelimiterType.Close;
}
if (canOpen) delimiterType |= DelimiterType.Open;
if (canClose) delimiterType |= DelimiterType.Close;
int line;
int column;
var delimiter = new EmphasisDelimiterInline(this, emphasisDesc)
{
DelimiterCount = delimiterCount,
Type = delimiterType,
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)),
Column = column,
Line = line,
};
@@ -223,18 +215,21 @@ namespace Markdig.Parsers.Inlines
// The following method is inspired by the "An algorithm for parsing nested emphasis and links"
// at the end of the CommonMark specs.
// TODO: Benchmark difference between using List and LinkedList here since there could be a few Remove calls
// Move current_position forward in the delimiter stack (if needed) until
// we find the first potential closer with delimiter * or _. (This will be the potential closer closest to the beginning of the input the first one in parse order.)
for (int i = 0; i < delimiters.Count; i++)
{
var closeDelimiter = delimiters[i];
// Skip delimiters not supported by this instance
if (emphasisMap[closeDelimiter.DelimiterChar] == null)
var closeDelimiter = delimiters[i];
// Skip delimiters not supported by this instance
EmphasisDescriptor emphasisDesc = emphasisMap[closeDelimiter.DelimiterChar];
if (emphasisDesc == null)
{
continue;
}
if ((closeDelimiter.Type & DelimiterType.Close) != 0 && closeDelimiter.DelimiterCount > 0)
if ((closeDelimiter.Type & DelimiterType.Close) != 0 && closeDelimiter.DelimiterCount >= emphasisDesc.MinimumCount)
{
while (true)
{
@@ -253,7 +248,7 @@ namespace Markdig.Parsers.Inlines
if (previousOpenDelimiter.DelimiterChar == closeDelimiter.DelimiterChar &&
(previousOpenDelimiter.Type & DelimiterType.Open) != 0 &&
previousOpenDelimiter.DelimiterCount > 0 && !isOddMatch)
previousOpenDelimiter.DelimiterCount >= emphasisDesc.MinimumCount && !isOddMatch)
{
openDelimiter = previousOpenDelimiter;
openDelimiterIndex = j;
@@ -262,22 +257,45 @@ namespace Markdig.Parsers.Inlines
}
if (openDelimiter != null)
{
process_delims:
bool isStrong = openDelimiter.DelimiterCount >= 2 && closeDelimiter.DelimiterCount >= 2;
// Insert an emph or strong emph node accordingly, after the text node corresponding to the opener.
var emphasis = CreateEmphasisInline?.Invoke(closeDelimiter.DelimiterChar, isStrong)
?? new EmphasisInline()
{
DelimiterChar = closeDelimiter.DelimiterChar,
IsDouble = isStrong
};
{
process_delims:
Debug.Assert(openDelimiter.DelimiterCount >= emphasisDesc.MinimumCount, "Extra emphasis should have been discarded by now");
Debug.Assert(closeDelimiter.DelimiterCount >= emphasisDesc.MinimumCount, "Extra emphasis should have been discarded by now");
int delimiterDelta = Math.Min(Math.Min(openDelimiter.DelimiterCount, closeDelimiter.DelimiterCount), emphasisDesc.MaximumCount);
// Insert an emph or strong emph node accordingly, after the text node corresponding to the opener.
EmphasisInline emphasis = null;
{
if (delimiterDelta <= 2) // We can try using the legacy delegate
{
#pragma warning disable CS0618 // Support fields marked as obsolete
emphasis = CreateEmphasisInline?.Invoke(closeDelimiter.DelimiterChar, isStrong: delimiterDelta == 2);
#pragma warning restore CS0618 // Support fields marked as obsolete
}
if (emphasis == null)
{
// Go in backwards order to give priority to newer delegates
for (int delegateIndex = TryCreateEmphasisInlineList.Count - 1; delegateIndex >= 0; delegateIndex--)
{
emphasis = TryCreateEmphasisInlineList[delegateIndex].Invoke(closeDelimiter.DelimiterChar, delimiterDelta);
if (emphasis != null) break;
}
if (emphasis == null)
{
emphasis = new EmphasisInline()
{
DelimiterChar = closeDelimiter.DelimiterChar,
DelimiterCount = delimiterDelta
};
}
}
}
Debug.Assert(emphasis != null);
// Update position for emphasis
var openDelimitercount = openDelimiter.DelimiterCount;
var closeDelimitercount = closeDelimiter.DelimiterCount;
var delimiterDelta = isStrong ? 2 : 1;
emphasis.Span.Start = openDelimiter.Span.Start;
emphasis.Line = openDelimiter.Line;
@@ -308,17 +326,7 @@ namespace Markdig.Parsers.Inlines
for (int k = i - 1; k >= openDelimiterIndex + 1; k--)
{
var literalDelimiter = delimiters[k];
var literal = new LiteralInline()
{
Content = new StringSlice(literalDelimiter.ToLiteral()),
IsClosed = true,
Span = literalDelimiter.Span,
Line = literalDelimiter.Line,
Column = literalDelimiter.Column
};
literalDelimiter.ReplaceBy(literal);
literalDelimiter.ReplaceBy(literalDelimiter.AsLiteralInline());
delimiters.RemoveAt(k);
i--;
}
@@ -343,10 +351,17 @@ namespace Markdig.Parsers.Inlines
}
// The current delimiters are matching
if (openDelimiter.DelimiterCount > 0)
if (openDelimiter.DelimiterCount >= emphasisDesc.MinimumCount)
{
goto process_delims;
}
else if (openDelimiter.DelimiterCount > 0)
{
// There are still delimiter characters left, there's just not enough of them
openDelimiter.ReplaceBy(openDelimiter.AsLiteralInline());
delimiters.RemoveAt(openDelimiterIndex);
i--;
}
else
{
// Remove the open delimiter if it is also empty
@@ -362,17 +377,7 @@ namespace Markdig.Parsers.Inlines
}
else if ((closeDelimiter.Type & DelimiterType.Open) == 0)
{
var literal = new LiteralInline()
{
Content = new StringSlice(closeDelimiter.ToLiteral()),
IsClosed = true,
Span = closeDelimiter.Span,
Line = closeDelimiter.Line,
Column = closeDelimiter.Column
};
closeDelimiter.ReplaceBy(literal);
// Notifies processor as we are creating an inline locally
closeDelimiter.ReplaceBy(closeDelimiter.AsLiteralInline());
delimiters.RemoveAt(i);
i--;
break;
@@ -389,16 +394,7 @@ namespace Markdig.Parsers.Inlines
for (int i = 0; i < delimiters.Count; i++)
{
var delimiter = delimiters[i];
var literal = new LiteralInline()
{
Content = new StringSlice(delimiter.ToLiteral()),
IsClosed = true,
Span = delimiter.Span,
Line = delimiter.Line,
Column = delimiter.Column
};
delimiter.ReplaceBy(literal);
delimiter.ReplaceBy(delimiter.AsLiteralInline());
}
delimiters.Clear();
}

View File

@@ -35,9 +35,10 @@ namespace Markdig.Parsers
/// </summary>
/// <param name="text">The reader.</param>
/// <param name="pipeline">The pipeline.</param>
/// <param name="context">A parser context used for the parsing.</param>
/// <exception cref="System.ArgumentNullException">
/// </exception>
private MarkdownParser(string text, MarkdownPipeline pipeline)
private MarkdownParser(string text, MarkdownPipeline pipeline, MarkdownParserContext context)
{
if (text == null) throw new ArgumentNullException(nameof(text));
if (pipeline == null) throw new ArgumentNullException(nameof(pipeline));
@@ -51,10 +52,10 @@ namespace Markdig.Parsers
document = new MarkdownDocument();
// Initialize the block parsers
blockProcessor = new BlockProcessor(stringBuilderCache, document, pipeline.BlockParsers);
blockProcessor = new BlockProcessor(stringBuilderCache, document, pipeline.BlockParsers, context);
// Initialize the inline parsers
inlineProcessor = new InlineProcessor(stringBuilderCache, document, pipeline.InlineParsers, pipeline.PreciseSourceLocation)
inlineProcessor = new InlineProcessor(stringBuilderCache, document, pipeline.InlineParsers, pipeline.PreciseSourceLocation, context)
{
DebugLog = pipeline.DebugLog
};
@@ -67,15 +68,16 @@ namespace Markdig.Parsers
/// </summary>
/// <param name="text">A Markdown text</param>
/// <param name="pipeline">The pipeline used for the parsing.</param>
/// <param name="context">A parser context used for the parsing.</param>
/// <returns>An AST Markdown document</returns>
/// <exception cref="System.ArgumentNullException">if reader variable is null</exception>
public static MarkdownDocument Parse(string text, MarkdownPipeline pipeline = null)
public static MarkdownDocument Parse(string text, MarkdownPipeline pipeline = null, MarkdownParserContext context = null)
{
if (text == null) throw new ArgumentNullException(nameof(text));
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
// Perform the parsing
var markdownParser = new MarkdownParser(text, pipeline);
var markdownParser = new MarkdownParser(text, pipeline, context);
return markdownParser.Parse();
}

View File

@@ -2,13 +2,14 @@
// 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;
using System.Diagnostics;
namespace Markdig.Renderers.Html.Inlines
{
/// <summary>
/// A HTML renderer for an <see cref="EmphasisInline"/>.
/// </summary>
/// <seealso cref="Markdig.Renderers.Html.HtmlObjectRenderer{Markdig.Syntax.Inlines.EmphasisInline}" />
/// <seealso cref="Html.HtmlObjectRenderer{EmphasisInline}" />
public class EmphasisInlineRenderer : HtmlObjectRenderer<EmphasisInline>
{
/// <summary>
@@ -55,7 +56,8 @@ namespace Markdig.Renderers.Html.Inlines
{
if (obj.DelimiterChar == '*' || obj.DelimiterChar == '_')
{
return obj.IsDouble ? "strong" : "em";
Debug.Assert(obj.DelimiterCount <= 2);
return obj.DelimiterCount == 2 ? "strong" : "em";
}
return null;
}

View File

@@ -314,12 +314,9 @@ namespace Markdig.Renderers
foreach (var property in attributes.Properties)
{
Write(" ").Write(property.Key);
if (property.Value != null)
{
Write("=").Write("\"");
WriteEscape(property.Value);
Write("\"");
}
Write("=").Write("\"");
WriteEscape(property.Value ?? "");
Write("\"");
}
}

View File

@@ -8,12 +8,12 @@ namespace Markdig.Renderers.Normalize.Inlines
/// <summary>
/// A Normalize renderer for an <see cref="EmphasisInline"/>.
/// </summary>
/// <seealso cref="Markdig.Renderers.Normalize.NormalizeObjectRenderer{Markdig.Syntax.Inlines.EmphasisInline}" />
/// <seealso cref="Normalize.NormalizeObjectRenderer{EmphasisInline}" />
public class EmphasisInlineRenderer : NormalizeObjectRenderer<EmphasisInline>
{
protected override void Write(NormalizeRenderer renderer, EmphasisInline obj)
{
var emphasisText = new string(obj.DelimiterChar, obj.IsDouble ? 2 : 1);
var emphasisText = new string(obj.DelimiterChar, obj.DelimiterCount);
renderer.Write(emphasisText);
renderer.WriteChildren(obj);
renderer.Write(emphasisText);

View File

@@ -94,25 +94,36 @@ namespace Markdig.Syntax.Inlines
/// <returns>An enumeration of T</returns>
public IEnumerable<T> FindDescendants<T>() where T : Inline
{
var child = FirstChild;
while (child != null)
{
var next = child.NextSibling;
// Fast-path an empty container to avoid allocating a Stack
if (LastChild == null) yield break;
if (child is T)
{
yield return (T)child;
}
Stack<Inline> stack = new Stack<Inline>();
if (child is ContainerInline)
{
foreach (var subChild in ((ContainerInline) child).FindDescendants<T>())
{
yield return subChild;
}
}
child = next;
var child = 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;
}
}
}
}

View File

@@ -3,6 +3,7 @@
// See the license.txt file in the project root for more information.
using System;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Parsers.Inlines;
@@ -11,7 +12,7 @@ namespace Markdig.Syntax.Inlines
/// <summary>
/// A delimiter used for parsing emphasis.
/// </summary>
/// <seealso cref="Markdig.Syntax.Inlines.DelimiterInline" />
/// <seealso cref="DelimiterInline" />
public class EmphasisDelimiterInline : DelimiterInline
{
/// <summary>
@@ -19,11 +20,10 @@ namespace Markdig.Syntax.Inlines
/// </summary>
/// <param name="parser">The parser.</param>
/// <param name="descriptor">The descriptor.</param>
/// <exception cref="System.ArgumentNullException"></exception>
/// <exception cref="ArgumentNullException"></exception>
public EmphasisDelimiterInline(InlineParser parser, EmphasisDescriptor descriptor) : base(parser)
{
if (descriptor == null) throw new ArgumentNullException(nameof(descriptor));
Descriptor = descriptor;
Descriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor));
DelimiterChar = descriptor.Character;
}
@@ -46,5 +46,17 @@ namespace Markdig.Syntax.Inlines
{
return DelimiterCount > 0 ? new string(DelimiterChar, DelimiterCount) : string.Empty;
}
public LiteralInline AsLiteralInline()
{
return new LiteralInline()
{
Content = new StringSlice(ToLiteral()),
IsClosed = true,
Span = Span,
Line = Line,
Column = Column
};
}
}
}

View File

@@ -2,6 +2,7 @@
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using System.Diagnostics;
namespace Markdig.Syntax.Inlines
@@ -9,8 +10,8 @@ namespace Markdig.Syntax.Inlines
/// <summary>
/// An emphasis and strong emphasis (Section 6.4 CommonMark specs).
/// </summary>
/// <seealso cref="Markdig.Syntax.Inlines.ContainerInline" />
[DebuggerDisplay("{DelimiterChar} Strong: {IsDouble}")]
/// <seealso cref="ContainerInline" />
[DebuggerDisplay("{DelimiterChar} Count: {DelimiterCount}")]
public class EmphasisInline : ContainerInline
{
/// <summary>
@@ -20,7 +21,18 @@ namespace Markdig.Syntax.Inlines
/// <summary>
/// Gets or sets a value indicating whether this <see cref="EmphasisInline"/> is strong.
/// <para>Marked obsolete as EmphasisInline can now be represented by more than two delimiter characters</para>
/// </summary>
public bool IsDouble { get; set; }
[Obsolete("Use `DelimiterCount == 2` instead", error: false)]
public bool IsDouble
{
get => DelimiterCount == 2;
set => DelimiterCount = value ? 2 : 1;
}
/// <summary>
/// Gets or sets the number of delimiter characters for this emphasis.
/// </summary>
public int DelimiterCount { get; set; }
}
}

View File

@@ -1,131 +1,119 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System.Collections.Generic;
using Markdig.Syntax.Inlines;
namespace Markdig.Syntax
{
/// <summary>
/// Extensions for visiting <see cref="Block"/> or <see cref="Inline"/>
/// </summary>
public static class MarkdownObjectExtensions
{
/// <summary>
/// Iterates over the descendant elements for the specified markdown element, including <see cref="Block"/> and <see cref="Inline"/>.
/// </summary>
/// <param name="markdownObject">The markdown object.</param>
/// <returns>An iteration over the descendant elements</returns>
public static IEnumerable<MarkdownObject> Descendants(this MarkdownObject markdownObject)
{
// TODO: implement a recursiveless method
var block = markdownObject as ContainerBlock;
if (block != null)
{
foreach (var subBlock in block)
{
yield return subBlock;
foreach (var sub in subBlock.Descendants())
{
yield return sub;
}
// Visit leaf block that have inlines
var leafBlock = subBlock as LeafBlock;
if (leafBlock?.Inline != null)
{
foreach (var subInline in Descendants(leafBlock.Inline))
{
yield return subInline;
}
}
}
}
else
{
var inline = markdownObject as ContainerInline;
if (inline != null)
{
var child = inline.FirstChild;
while (child != null)
{
var next = child.NextSibling;
yield return child;
foreach (var sub in child.Descendants())
{
yield return sub;
}
child = next;
}
}
}
}
/// <summary>
/// Iterates over the descendant elements for the specified markdown <see cref="Inline" /> element and filters by the type {T}.
/// </summary>
/// <typeparam name="T">Type to use for filtering the descendants</typeparam>
/// <param name="inline">The inline markdown object.</param>
/// <returns>
/// An iteration over the descendant elements
/// </returns>
public static IEnumerable<T> Descendants<T>(this ContainerInline inline) where T : Inline
{
var child = inline.FirstChild;
while (child != null)
{
var next = child.NextSibling;
var childT = child as T;
if (childT != null)
{
yield return childT;
}
var subContainer = child as ContainerInline;
if (subContainer != null)
{
foreach (var sub in subContainer.Descendants<T>())
{
yield return sub;
}
}
child = next;
}
}
/// <summary>
/// Iterates over the descendant elements for the specified markdown <see cref="Block" /> element and filters by the type {T}.
/// </summary>
/// <typeparam name="T">Type to use for filtering the descendants</typeparam>
/// <param name="block">The markdown object.</param>
/// <returns>
/// An iteration over the descendant elements
/// </returns>
public static IEnumerable<T> Descendants<T>(this ContainerBlock block) where T : Block
{
foreach (var subBlock in block)
{
var subBlockT = subBlock as T;
if (subBlockT != null)
{
yield return subBlockT;
}
var subBlockContainer = subBlock as ContainerBlock;
if (subBlockContainer != null)
{
foreach (var sub in subBlockContainer.Descendants<T>())
{
yield return sub;
}
}
}
}
}
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System.Collections.Generic;
using Markdig.Syntax.Inlines;
namespace Markdig.Syntax
{
/// <summary>
/// Extensions for visiting <see cref="Block"/> or <see cref="Inline"/>
/// </summary>
public static class MarkdownObjectExtensions
{
/// <summary>
/// Iterates over the descendant elements for the specified markdown element, including <see cref="Block"/> and <see cref="Inline"/>.
/// <para>The descendant elements are returned in DFS-like order.</para>
/// </summary>
/// <param name="markdownObject">The markdown object.</param>
/// <returns>An iteration over the descendant elements</returns>
public static IEnumerable<MarkdownObject> Descendants(this MarkdownObject markdownObject)
{
// Fast-path an object with no children to avoid allocating Stack objects
if (!(markdownObject is ContainerBlock) && !(markdownObject is ContainerInline)) yield break;
// TODO: A single Stack<(MarkdownObject block, bool push)> when ValueTuples are available
Stack<MarkdownObject> stack = new Stack<MarkdownObject>();
Stack<bool> pushStack = new Stack<bool>();
stack.Push(markdownObject);
pushStack.Push(false);
while (stack.Count > 0)
{
var block = stack.Pop();
if (pushStack.Pop()) yield return block;
if (block is ContainerBlock containerBlock)
{
int subBlockIndex = containerBlock.Count;
while (subBlockIndex-- > 0)
{
var subBlock = containerBlock[subBlockIndex];
if (subBlock is LeafBlock leafBlock)
{
if (leafBlock.Inline != null)
{
stack.Push(leafBlock.Inline);
pushStack.Push(false);
}
}
stack.Push(subBlock);
pushStack.Push(true);
}
}
else if (block is ContainerInline containerInline)
{
var child = containerInline.LastChild;
while (child != null)
{
stack.Push(child);
pushStack.Push(true);
child = child.PreviousSibling;
}
}
}
}
/// <summary>
/// Iterates over the descendant elements for the specified markdown <see cref="Inline" /> element and filters by the type {T}.
/// </summary>
/// <typeparam name="T">Type to use for filtering the descendants</typeparam>
/// <param name="inline">The inline markdown object.</param>
/// <returns>
/// An iteration over the descendant elements
/// </returns>
public static IEnumerable<T> Descendants<T>(this ContainerInline inline) where T : Inline
=> inline.FindDescendants<T>();
/// <summary>
/// Iterates over the descendant elements for the specified markdown <see cref="Block" /> element and filters by the type {T}.
/// </summary>
/// <typeparam name="T">Type to use for filtering the descendants</typeparam>
/// <param name="block">The markdown object.</param>
/// <returns>
/// An iteration over the descendant elements
/// </returns>
public static IEnumerable<T> Descendants<T>(this ContainerBlock block) where T : Block
{
// Fast-path an empty container to avoid allocating a Stack
if (block.Count == 0) yield break;
Stack<Block> stack = new Stack<Block>();
int childrenCount = block.Count;
while (childrenCount-- > 0)
{
stack.Push(block[childrenCount]);
}
while (stack.Count > 0)
{
var subBlock = stack.Pop();
if (subBlock is T subBlockT)
{
yield return subBlockT;
}
if (subBlock is ContainerBlock subBlockContainer)
{
childrenCount = subBlockContainer.Count;
while (childrenCount-- > 0)
{
stack.Push(subBlockContainer[childrenCount]);
}
}
}
}
}
}

354
src/SpecFileGen/Program.cs Normal file
View File

@@ -0,0 +1,354 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace SpecFileGen
{
class Program
{
static readonly string SpecificationsDirectory =
Path.GetFullPath(
Path.Combine(
Path.GetDirectoryName(typeof(Spec).Assembly.Location),
"../../../../Markdig.Tests/Specs/"));
class Spec
{
public readonly string Name;
public readonly string Path;
public readonly string OutputPath;
public readonly string Extensions;
public int TestCount;
public Spec(string name, string fileName, string extensions)
{
Name = name;
Path = SpecificationsDirectory + fileName;
OutputPath = Path.Substring(0, Path.Length - 2) + "cs";
Extensions = extensions;
}
}
static readonly Spec[] Specs = new[]
{
new Spec("CommonMark v. 0.28", "CommonMark.md", ""),
new Spec("Pipe Tables", "PipeTableSpecs.md", "pipetables|advanced"),
new Spec("Footnotes", "FootnotesSpecs.md", "footnotes|advanced"),
new Spec("Generic Attributes", "GenericAttributesSpecs.md", "attributes|advanced"),
new Spec("Emphasis Extra", "EmphasisExtraSpecs.md", "emphasisextras|advanced"),
new Spec("Hardline Breaks", "HardlineBreakSpecs.md", "hardlinebreak|advanced+hardlinebreak"),
new Spec("Grid Tables", "GridTableSpecs.md", "gridtables|advanced"),
new Spec("Custom Containers", "CustomContainerSpecs.md", "customcontainers+attributes|advanced"),
new Spec("Definition Lists", "DefinitionListSpecs.md", "definitionlists+attributes|advanced"),
new Spec("Emoji", "EmojiSpecs.md", "emojis|advanced+emojis"),
new Spec("Abbreviations", "AbbreviationSpecs.md", "abbreviations|advanced"),
new Spec("List Extras", "ListExtraSpecs.md", "listextras|advanced"),
new Spec("Math", "MathSpecs.md", "mathematics|advanced"),
new Spec("Bootstrap", "BootstrapSpecs.md", "bootstrap+pipetables+figures+attributes"),
new Spec("Media", "MediaSpecs.md", "medialinks|advanced+medialinks"),
new Spec("Smarty Pants", "SmartyPantsSpecs.md", "pipetables+smartypants|advanced+smartypants"),
new Spec("Auto Identifiers", "AutoIdentifierSpecs.md", "autoidentifiers|advanced"),
new Spec("Task Lists", "TaskListSpecs.md", "tasklists|advanced"),
new Spec("Diagrams", "DiagramsSpecs.md", "diagrams|advanced"),
new Spec("No Html", "NoHtmlSpecs.md", "nohtml"),
new Spec("Yaml", "YamlSpecs.md", "yaml"),
new Spec("Auto Links", "AutoLinks.md", "autolinks|advanced"),
new Spec("Jira Links", "JiraLinks.md", "jiralinks"),
new Spec("Globalization", "GlobalizationSpecs.md", "globalization+advanced+emojis"),
new Spec("Figures, Footers and Cites", "FigureFooterAndCiteSpecs.md", "figures+footers+citations|advanced"),
};
static void Main(string[] args)
{
Console.WriteLine("Generating {0} specs ...", Specs.Length);
bool anyChanged = false;
List<Spec> missingSpecs = new List<Spec>();
int totalTests = 0;
foreach (var spec in Specs)
{
if (!File.Exists(spec.Path))
{
missingSpecs.Add(spec);
continue;
}
string source = ParseSpecification(spec, File.ReadAllText(spec.Path));
totalTests += spec.TestCount;
if (File.Exists(spec.OutputPath)) // If the source hasn't changed, don't bump the generated tag
{
string previousSource = File.ReadAllText(spec.OutputPath);
int start = previousSource.IndexOf('\n', StringComparison.Ordinal) + 1;
int previousLength = previousSource.Length - start;
if (start != 0 && previousLength == source.Length)
{
if (previousSource.IndexOf(source, start, previousLength, StringComparison.Ordinal) == start)
{
// The source did not change
continue;
}
}
}
string generated = "// Generated: " + DateTime.UtcNow.ToString() + '\n';
File.WriteAllText(spec.OutputPath, generated + source);
anyChanged = true;
}
if (missingSpecs.Count != 0)
{
foreach (var spec in missingSpecs)
{
EmitError("Could not find the specification file at " + spec.Path);
}
Environment.Exit(1);
}
if (anyChanged && Environment.GetEnvironmentVariable("CI") != null)
{
EmitError("Run SpecFileGen when changing specification files");
Environment.Exit(1);
}
Console.WriteLine("There are {0} tests in total", totalTests);
}
static void EmitError(string error)
{
var prevColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine();
Console.WriteLine(new string('-', error.Length));
Console.WriteLine(error);
Console.WriteLine(new string('-', error.Length));
Console.WriteLine();
Console.ForegroundColor = prevColor;
}
static readonly StringBuilder StringBuilder = new StringBuilder(1 << 20); // 1 MB
static void Write(string text)
{
StringBuilder.Append(text);
}
static void Line(string text = null)
{
if (text != null) StringBuilder.Append(text);
StringBuilder.Append('\n');
}
static void Indent(int count = 1)
{
StringBuilder.Append(new string(' ', 4 * count));
}
static string ParseSpecification(Spec spec, string specSource)
{
Line();
Write("// "); Line(new string('-', 32));
Write("// "); Write(new string(' ', 16 - spec.Name.Length / 2)); Line(spec.Name);
Write("// "); Line(new string('-', 32));
Line();
Line("using System;");
Line("using NUnit.Framework;");
Line();
Write("namespace Markdig.Tests.Specs."); Line(CompressedName(spec.Name).Replace('.', '_'));
Line("{");
var lines = specSource.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
bool nameChanged = true;
string name = "";
string compressedName = "";
int number = 0;
int commentOffset = 0, commentEnd = 0, markdownOffset = 0, markdownEnd = 0, htmlOffset = 0, htmlEnd = 0;
bool first = true;
LinkedList<(string Heading, string Compressed, int Level)> headings = new LinkedList<(string, string, int)>();
StringBuilder nameBuilder = new StringBuilder(64);
int i = 0;
while (i < lines.Length)
{
commentOffset = commentEnd = i;
while (!lines[i].Equals("```````````````````````````````` example", StringComparison.Ordinal))
{
string line = lines[i];
if (line.Length > 2 && line[0] == '#')
{
int level = line.IndexOf(' ', StringComparison.Ordinal);
while (headings.Count != 0)
{
if (headings.Last.Value.Level < level) break;
headings.RemoveLast();
}
string heading = line.Substring(level + 1);
headings.AddLast((heading, CompressedName(heading), level));
foreach (var (Heading, _, _) in headings)
nameBuilder.Append(Heading + " / ");
nameBuilder.Length -= 3;
name = nameBuilder.ToString();
nameBuilder.Length = 0;
foreach (var (_, Compressed, _) in headings)
nameBuilder.Append(Compressed);
compressedName = nameBuilder.ToString();
nameBuilder.Length = 0;
nameChanged = true;
}
i++;
if (!IsEmpty(line))
commentEnd = i;
if (i == lines.Length)
{
if (commentOffset != commentEnd)
{
while (commentOffset < commentEnd && IsEmpty(lines[commentOffset])) commentOffset++;
for (i = commentOffset; i < commentEnd; i++)
{
Indent(2); Write("// "); Line(lines[i]);
}
}
goto End;
}
};
markdownOffset = ++i;
while (!(lines[i].Length == 1 && lines[i][0] == '.')) i++;
markdownEnd = i++;
htmlOffset = i;
while (!lines[i].Equals("````````````````````````````````", StringComparison.Ordinal)) i++;
htmlEnd = i++;
if (nameChanged)
{
if (!first)
{
Indent(); Line("}");
Line();
}
Indent(); Line("[TestFixture]");
Indent(); Line("public class Test" + compressedName);
Indent(); Line("{");
first = false;
nameChanged = false;
}
else Line();
WriteTest(name, compressedName, ++number, spec.Extensions, lines,
commentOffset, commentEnd,
markdownOffset, markdownEnd,
htmlOffset, htmlEnd);
}
End:
if (!first)
{
Indent(); Line("}");
}
Line("}");
string source = StringBuilder.ToString();
StringBuilder.Length = 0;
spec.TestCount = number;
return source;
}
static void WriteTest(string name, string compressedName, int number, string extensions, string[] lines, int commentOffset, int commentEnd, int markdownOffset, int markdownEnd, int htmlOffset, int htmlEnd)
{
if (commentOffset != commentEnd)
{
while (commentOffset < commentEnd && IsEmpty(lines[commentOffset])) commentOffset++;
for (int i = commentOffset; i < commentEnd; i++)
{
Indent(2); Write("// "); Line(lines[i]);
}
}
Indent(2); Line("[Test]");
Indent(2); Line("public void " + compressedName + "_Example" + number.ToString().PadLeft(3, '0') + "()");
Indent(2); Line("{");
Indent(3); Line("// Example " + number);
Indent(3); Line("// Section: " + name);
Indent(3); Line("//");
Indent(3); Line("// The following Markdown:");
for (int i = markdownOffset; i < markdownEnd; i++)
{
Indent(3); Write("// "); Indent(); Line(lines[i]);
}
Indent(3); Line("//");
Indent(3); Line("// Should be rendered as:");
for (int i = htmlOffset; i < htmlEnd; i++)
{
Indent(3); Write("// "); Indent(); Line(lines[i]);
}
if (htmlOffset >= htmlEnd)
{
Indent(3); Write("//");
}
Line();
Indent(3); Line($"Console.WriteLine(\"Example {number}\\nSection {name}\\n\");");
Indent(3);
Write("TestParser.TestSpec(\"");
for (int i = markdownOffset; i < markdownEnd; i++)
{
Write(Escape(lines[i]));
if (i != markdownEnd - 1) Write("\\n");
}
Write("\", \"");
for (int i = htmlOffset; i < htmlEnd; i++)
{
Write(Escape(lines[i]));
if (i != htmlEnd - 1) Write("\\n");
}
Write("\", \"");
Write(extensions);
Line("\");");
Indent(2); Line("}");
}
static string Escape(string input)
{
return input
.Replace("→", "\t")
.Replace("\\", "\\\\")
.Replace("\"", "\\\"")
.Replace("\0", "\\0")
.Replace("\a", "\\a")
.Replace("\b", "\\b")
.Replace("\f", "\\f")
.Replace("\n", "\\n")
.Replace("\r", "\\r")
.Replace("\t", "\\t")
.Replace("\v", "\\v")
;
}
static string CompressedName(string name)
{
string compressedName = "";
foreach (var part in name.Replace(',', ' ').Split(' ', StringSplitOptions.RemoveEmptyEntries))
{
compressedName += char.IsLower(part[0])
? char.ToUpper(part[0]) + part.Substring(1)
: part;
}
return compressedName;
}
static bool IsEmpty(string str)
{
for (int i = 0; i < str.Length; i++)
{
if (str[i] != ' ') return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -1,69 +1,75 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2005
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{061866E2-005C-4D13-A338-EA464BBEC60F}"
ProjectSection(SolutionItems) = preProject
..\appveyor.yml = ..\appveyor.yml
..\changelog.md = ..\changelog.md
..\license.txt = ..\license.txt
..\readme.md = ..\readme.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig", "Markdig\Markdig.csproj", "{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdig.Tests", "Markdig.Tests\Markdig.Tests.csproj", "{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}"
ProjectSection(ProjectDependencies) = postProject
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48} = {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdig.Benchmarks", "Markdig.Benchmarks\Markdig.Benchmarks.csproj", "{6A19F040-BC7C-4283-873A-177B5324F1ED}"
ProjectSection(ProjectDependencies) = postProject
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48} = {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig.WebApp", "Markdig.WebApp\Markdig.WebApp.csproj", "{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnicodeNormDApp", "UnicodeNormDApp\UnicodeNormDApp.csproj", "{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mdtoc", "mdtoc\mdtoc.csproj", "{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Release|Any CPU.Build.0 = Release|Any CPU
{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Release|Any CPU.Build.0 = Release|Any CPU
{6A19F040-BC7C-4283-873A-177B5324F1ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6A19F040-BC7C-4283-873A-177B5324F1ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6A19F040-BC7C-4283-873A-177B5324F1ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6A19F040-BC7C-4283-873A-177B5324F1ED}.Release|Any CPU.Build.0 = Release|Any CPU
{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Release|Any CPU.Build.0 = Release|Any CPU
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Release|Any CPU.Build.0 = Release|Any CPU
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D068F7B6-6ACC-456C-A2E1-10EA746D956D}
EndGlobalSection
EndGlobal
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28407.52
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{061866E2-005C-4D13-A338-EA464BBEC60F}"
ProjectSection(SolutionItems) = preProject
..\appveyor.yml = ..\appveyor.yml
..\changelog.md = ..\changelog.md
..\license.txt = ..\license.txt
..\readme.md = ..\readme.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig", "Markdig\Markdig.csproj", "{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdig.Tests", "Markdig.Tests\Markdig.Tests.csproj", "{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}"
ProjectSection(ProjectDependencies) = postProject
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48} = {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdig.Benchmarks", "Markdig.Benchmarks\Markdig.Benchmarks.csproj", "{6A19F040-BC7C-4283-873A-177B5324F1ED}"
ProjectSection(ProjectDependencies) = postProject
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48} = {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig.WebApp", "Markdig.WebApp\Markdig.WebApp.csproj", "{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnicodeNormDApp", "UnicodeNormDApp\UnicodeNormDApp.csproj", "{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mdtoc", "mdtoc\mdtoc.csproj", "{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpecFileGen", "SpecFileGen\SpecFileGen.csproj", "{DB6E2ED5-7884-4E97-84AF-35E2480CF685}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Release|Any CPU.Build.0 = Release|Any CPU
{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Release|Any CPU.Build.0 = Release|Any CPU
{6A19F040-BC7C-4283-873A-177B5324F1ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6A19F040-BC7C-4283-873A-177B5324F1ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6A19F040-BC7C-4283-873A-177B5324F1ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6A19F040-BC7C-4283-873A-177B5324F1ED}.Release|Any CPU.Build.0 = Release|Any CPU
{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Release|Any CPU.Build.0 = Release|Any CPU
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Release|Any CPU.Build.0 = Release|Any CPU
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Release|Any CPU.Build.0 = Release|Any CPU
{DB6E2ED5-7884-4E97-84AF-35E2480CF685}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DB6E2ED5-7884-4E97-84AF-35E2480CF685}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DB6E2ED5-7884-4E97-84AF-35E2480CF685}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DB6E2ED5-7884-4E97-84AF-35E2480CF685}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D068F7B6-6ACC-456C-A2E1-10EA746D956D}
EndGlobalSection
EndGlobal