Compare commits

...

36 Commits

Author SHA1 Message Date
Alexandre Mutel
9da3eef65f Bump to 0.3.2 2016-06-10 06:20:44 +09:00
Alexandre Mutel
0b4fe3f02f Modify tests to run default and advanced together on the core CommonMark specs to make sure the Advanced mode is compatible with the core specs (except for the AutoIdentifier that we are disabling) 2016-06-10 06:19:14 +09:00
Alexandre Mutel
96b39e1856 Fix exception when extension Media was active. Make sure that we only process absolute Uri for media 2016-06-10 06:18:19 +09:00
Alexandre Mutel
6d90f517cc Fix BlockProcessor.GoToColumn that was messing indent 2016-06-10 06:17:47 +09:00
Alexandre Mutel
c17630e3b6 Add link to babelmark3 2016-06-09 14:00:06 +09:00
Alexandre Mutel
6eecfe2edc Bump version to 0.3.1 2016-06-09 10:13:53 +09:00
Alexandre Mutel
9abeb97394 Remove Bootstrap and add ListExtra to the UseAdvancedExtensions 2016-06-09 10:13:39 +09:00
Alexandre Mutel
fd493865cf Bump version to 0.3 2016-06-08 17:26:16 +09:00
Alexandre Mutel
d43947af45 Breaking change: rename UseAllExtensions to UseAdvancedExtensions. Add pipeline.Configure(string) method 2016-06-08 17:26:07 +09:00
Alexandre Mutel
ab04ac3e00 Add new test that was crashing the CodeInlineParser 2016-06-08 16:09:17 +09:00
Alexandre Mutel
547c00eb5a Remove SoftlineBreakAsHarlineBreak and SmartyPants by default for AllExtensions 2016-06-08 16:08:22 +09:00
Alexandre Mutel
795d002ed0 Fix issue with CodeInlineParser that was incrementing local line index even without matching, resulting in an exception IndexOutOfRange 2016-06-08 16:08:01 +09:00
Alexandre Mutel
987357ef5a Render always with '\n' only instead of platform dependent EOF 2016-06-08 16:07:21 +09:00
Alexandre Mutel
613a1d97fb Remove NuGet.Config no longer used asof dotnet rc2 2016-06-06 12:07:08 +09:00
Alexandre Mutel
874170bc1a Add Markdig.WebApp ASP.NET Core to provide API for BabelMark2 test at http://johnmacfarlane.net/babelmark2 2016-06-06 10:28:09 +09:00
Alexandre Mutel
1c1e56aebe Add Markdown.Version API. Bump to 0.2.1 2016-06-06 10:13:03 +09:00
Alexandre Mutel
ed18d3fa25 Update links from readme 2016-06-01 22:40:40 +09:00
Alexandre Mutel
33a6a39c34 Fix links to pandoc grid and pipe tables extensions 2016-06-01 22:38:14 +09:00
Alexandre Mutel
a1228a1e1c Add missing update to Markdig.xproj 2016-06-01 22:37:57 +09:00
Alexandre Mutel
d9f1c8c818 Update readme with MarkdownPipelineBuilder 2016-05-31 06:57:42 +09:00
Alexandre Mutel
60350dc9bf Fix MarkdownPipeline not being immutable by introducing a MarkdownPipelineBuilder (issue #5). Bump to version 0.2.0 (dev) 2016-05-31 06:55:40 +09:00
Alexandre Mutel
dbaed0a2bb Add credit for BenchmarkDotNet 2016-05-30 22:47:11 +09:00
Alexandre Mutel
0aa26aeef1 Bump version to 0.1.1 2016-05-30 22:34:07 +09:00
Alexandre Mutel
09beb2f867 Make sure that an emphasis is not added twice (issue #4) 2016-05-30 22:33:50 +09:00
Alexandre Mutel
fb559af72b Fix wrong dependencies in project.json (issue #3) 2016-05-30 22:32:56 +09:00
Alexandre Mutel
124b4d6e6e Split usage for better readability in readme 2016-05-26 15:20:51 +09:00
Alexandre Mutel
4a2d270db1 Update link to license. Update comments on other Markdown implems 2016-05-26 15:10:17 +09:00
Alexandre Mutel
feac9d4125 Reorder items in extension lists based on importance. Fix usage 2016-05-26 14:34:09 +09:00
Alexandre Mutel
5e77d7b38e Add link to AppVeyor build 2016-05-26 10:40:21 +09:00
Alexandre Mutel
9f64dcc868 Add info about disabling features 2016-05-26 09:37:00 +09:00
Alexandre Mutel
7964bd0160 Add support for disabling HTML tag parsing 2016-05-26 09:31:56 +09:00
Alexandre Mutel
191c1e896e Update benchmarks with accurate GC 2016-05-26 06:25:51 +09:00
Alexandre Mutel
4bdf1d4d18 Fix link to extensions 2016-05-26 00:17:47 +09:00
Alexandre Mutel
080dee270d Add benchmarks 2016-05-26 00:04:25 +09:00
Alexandre Mutel
7ae36a3794 Fix link to MarkdownPipeline 2016-05-25 23:48:17 +09:00
Alexandre Mutel
f43d5be0e7 Add credits section 2016-05-25 23:33:58 +09:00
57 changed files with 1103 additions and 508 deletions

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<solution>
<add key="disableSourceControlIntegration" value="true" />
</solution>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="dotnet-core" value="http://myget.org/F/dotnet-core" />
</packageSources>
</configuration>

View File

@@ -1,150 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">$(MSBuildProjectDirectory)\..\</SolutionDir>
<!-- Enable the restore command to run before builds -->
<RestorePackages Condition=" '$(RestorePackages)' == '' ">false</RestorePackages>
<!-- Property that enables building a package from a project -->
<BuildPackage Condition=" '$(BuildPackage)' == '' ">false</BuildPackage>
<!-- Determines if package restore consent is required to restore packages -->
<RequireRestoreConsent Condition=" '$(RequireRestoreConsent)' != 'false' ">true</RequireRestoreConsent>
<!-- Download NuGet.exe if it does not already exist -->
<DownloadNuGetExe Condition=" '$(DownloadNuGetExe)' == '' ">true</DownloadNuGetExe>
</PropertyGroup>
<ItemGroup Condition=" '$(PackageSources)' == '' ">
<!-- Package sources used to restore packages. By default will used the registered sources under %APPDATA%\NuGet\NuGet.Config -->
<!--
<PackageSource Include="https://nuget.org/api/v2/" />
<PackageSource Include="https://my-nuget-source/nuget/" />
-->
</ItemGroup>
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT'">
<!-- Windows specific commands -->
<NuGetToolsPath>$([System.IO.Path]::Combine($(SolutionDir), ".nuget"))</NuGetToolsPath>
<PackagesConfig>$([System.IO.Path]::Combine($(ProjectDir), "packages.config"))</PackagesConfig>
</PropertyGroup>
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT'">
<!-- We need to launch nuget.exe with the mono command if we're not on windows -->
<NuGetToolsPath>$(SolutionDir).nuget</NuGetToolsPath>
<PackagesConfig>packages.config</PackagesConfig>
</PropertyGroup>
<PropertyGroup>
<!-- NuGet command -->
<NuGetExePath Condition=" '$(NuGetExePath)' == '' ">$(NuGetToolsPath)\nuget.exe</NuGetExePath>
<PackageSources Condition=" $(PackageSources) == '' ">@(PackageSource)</PackageSources>
<NuGetCommand Condition=" '$(OS)' == 'Windows_NT'">"$(NuGetExePath)"</NuGetCommand>
<NuGetCommand Condition=" '$(OS)' != 'Windows_NT' ">mono --runtime=v4.0.30319 $(NuGetExePath)</NuGetCommand>
<PackageOutputDir Condition="$(PackageOutputDir) == ''">$(TargetDir.Trim('\\'))</PackageOutputDir>
<RequireConsentSwitch Condition=" $(RequireRestoreConsent) == 'true' ">-RequireConsent</RequireConsentSwitch>
<!-- Commands -->
<RestoreCommand>$(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(RequireConsentSwitch) -solutionDir "$(SolutionDir) "</RestoreCommand>
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -p Configuration=$(Configuration) -o "$(PackageOutputDir)" -symbols</BuildCommand>
<!-- We need to ensure packages are restored prior to assembly resolve -->
<ResolveReferencesDependsOn Condition="$(RestorePackages) == 'true'">
RestorePackages;
$(ResolveReferencesDependsOn);
</ResolveReferencesDependsOn>
<!-- Make the build depend on restore packages -->
<BuildDependsOn Condition="$(BuildPackage) == 'true'">
$(BuildDependsOn);
BuildPackage;
</BuildDependsOn>
</PropertyGroup>
<Target Name="CheckPrerequisites">
<!-- Raise an error if we're unable to locate nuget.exe -->
<Error Condition="'$(DownloadNuGetExe)' != 'true' AND !Exists('$(NuGetExePath)')" Text="Unable to locate '$(NuGetExePath)'" />
<SetEnvironmentVariable EnvKey="VisualStudioVersion" EnvValue="$(VisualStudioVersion)" Condition=" '$(VisualStudioVersion)' != '' AND '$(OS)' == 'Windows_NT' " />
<!--
Take advantage of MsBuild's build dependency tracking to make sure that we only ever download nuget.exe once.
This effectively acts as a lock that makes sure that the download operation will only happen once and all
parallel builds will have to wait for it to complete.
-->
<MsBuild Targets="_DownloadNuGet" Projects="$(MSBuildThisFileFullPath)" Properties="Configuration=NOT_IMPORTANT" />
</Target>
<Target Name="_DownloadNuGet">
<DownloadNuGet OutputFilename="$(NuGetExePath)" Condition=" '$(DownloadNuGetExe)' == 'true' AND !Exists('$(NuGetExePath)')" />
</Target>
<Target Name="RestorePackages" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(RestoreCommand)"
Condition="'$(OS)' != 'Windows_NT' And Exists('$(PackagesConfig)')" />
<Exec Command="$(RestoreCommand)"
LogStandardErrorAsError="true"
Condition="'$(OS)' == 'Windows_NT' And Exists('$(PackagesConfig)')" />
</Target>
<Target Name="BuildPackage" DependsOnTargets="CheckPrerequisites">
<Exec Command="$(BuildCommand)"
Condition=" '$(OS)' != 'Windows_NT' " />
<Exec Command="$(BuildCommand)"
LogStandardErrorAsError="true"
Condition=" '$(OS)' == 'Windows_NT' " />
</Target>
<UsingTask TaskName="DownloadNuGet" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<OutputFilename ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Reference Include="System.Core" />
<Using Namespace="System" />
<Using Namespace="System.IO" />
<Using Namespace="System.Net" />
<Using Namespace="Microsoft.Build.Framework" />
<Using Namespace="Microsoft.Build.Utilities" />
<Code Type="Fragment" Language="cs">
<![CDATA[
try {
OutputFilename = Path.GetFullPath(OutputFilename);
Log.LogMessage("Downloading latest version of NuGet.exe...");
WebClient webClient = new WebClient();
webClient.DownloadFile("https://nuget.org/nuget.exe", OutputFilename);
return true;
}
catch (Exception ex) {
Log.LogErrorFromException(ex);
return false;
}
]]>
</Code>
</Task>
</UsingTask>
<UsingTask TaskName="SetEnvironmentVariable" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<EnvKey ParameterType="System.String" Required="true" />
<EnvValue ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Using Namespace="System" />
<Code Type="Fragment" Language="cs">
<![CDATA[
try {
Environment.SetEnvironmentVariable(EnvKey, EnvValue, System.EnvironmentVariableTarget.Process);
}
catch {
}
]]>
</Code>
</Task>
</UsingTask>
</Project>

145
readme.md
View File

@@ -1,9 +1,11 @@
# Markdig [![NuGet](https://img.shields.io/nuget/v/Markdig.svg)](https://www.nuget.org/packages/Markdig/)
# Markdig [![Build status](https://ci.appveyor.com/api/projects/status/hk391x8jcskxt1u8?svg=true)](https://ci.appveyor.com/project/xoofx/markdig) [![NuGet](https://img.shields.io/nuget/v/Markdig.svg)](https://www.nuget.org/packages/Markdig/)
Markdig is a fast, powerfull, [CommonMark](http://commonmark.org/) compliant, extensible Markdown processor for .NET.
> NOTE: The repository is under construction. There will be a dedicated website and proper documentation at some point!
You can **try Markdig online** and compare it to other implems on [babelmark3](http://babelmark.github.io/)
## Features
- **Very fast parser** (no-regexp), very lightweight in terms of GC pressure. See benchmarks
@@ -13,32 +15,33 @@ Markdig is a fast, powerfull, [CommonMark](http://commonmark.org/) compliant, ex
- Includes all the core elements of CommonMark:
- including GFM fenced code blocks.
- **Extensible** architecture
- Even the core Markdown/CommonMark parsing is pluggable, so it allows to disable builtin Markdown/Commonmark parsing (e.g [Disable HTML parsing](https://github.com/lunet-io/markdig/blob/7964bd0160d4c18e4155127a4c863d61ebd8944a/src/Markdig/MarkdownExtensions.cs#L306)) or change behaviour (e.g change matching `#` of a headers with `@`)
- Built-in with **18+ extensions**, including:
- **Abbreviations** (inspired from [PHP Markdown Extra - Abbreviations](https://michelf.ca/projects/php-markdown/extra/#abbr))
- **Auto-identifiers** for headings (similar to [Pandoc](http://pandoc.org/README.html#extension-auto_identifiers)
- **Bootstrap** class (to output bootstrap class)
- **Citation** text by enclosing `""...""` (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/referencing-creative-works-with-cite/892))
- **Custom containers** similar to fenced code block `:::` for generating a proper `<div>...</div>` instead (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/custom-container-for-block-and-inline/2051))
- **Definition lists** (inspired from [PHP Markdown Extra - Definitions Lists](https://michelf.ca/projects/php-markdown/extra/#def-list))
- **Emoji** support (inspired from [Markdown-it](https://markdown-it.github.io/))
- **Extra emphasis** (inspired from [Pandoc](http://pandoc.org/README.html#strikeout) and [Markdown-it](https://markdown-it.github.io/))
- 2 kind of tables:
- **Pipe tables** (inspired from Github tables and [PanDoc - Pipe Tables](http://pandoc.org/README.html#extension-pipe_tables))
- **Grid tables** (inspired from [Pandoc - Grid Tables](http://pandoc.org/README.html#extension-grid_tables))
- **Extra emphasis** (inspired from [Pandoc - Emphasis](http://pandoc.org/README.html#strikeout) and [Markdown-it](https://markdown-it.github.io/))
- strike through `~~`,
- Subscript `~`
- Superscript `^`
- Inserted `++`
- Marked `==`
- **Special attributes** or attached HTML attributes (inspired from [PHP Markdown Extra - Special Attributes](https://michelf.ca/projects/php-markdown/extra/#spe-attr))
- **Definition lists** (inspired from [PHP Markdown Extra - Definitions Lists](https://michelf.ca/projects/php-markdown/extra/#def-list))
- **Footnotes** (inspired from [PHP Markdown Extra - Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes))
- **Auto-identifiers** for headings (similar to [Pandoc - Auto Identifiers](http://pandoc.org/README.html#extension-auto_identifiers))
- **Extra bullet lists**, supporting alpha bullet `a.` `b.` and roman bullet (`i`, `ii`...etc.)
- **Media support** for media url (youtube, vimeo, mp4...etc.) (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/embedded-audio-and-video/441))
- **Abbreviations** (inspired from [PHP Markdown Extra - Abbreviations](https://michelf.ca/projects/php-markdown/extra/#abbr))
- **Citation** text by enclosing `""...""` (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/referencing-creative-works-with-cite/892))
- **Custom containers** similar to fenced code block `:::` for generating a proper `<div>...</div>` instead (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/custom-container-for-block-and-inline/2051))
- **Figures** (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/image-tag-should-expand-to-figure-when-used-with-title/265/5))
- **Footers** (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/syntax-for-footer/2070))
- **Footnotes** (inspired from [PHP Markdown Extra - Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes))
- **Special attributes** or attached HTML attributes (inspired from [PHP Markdown Extra - Footnotes](https://michelf.ca/projects/php-markdown/extra/#spe-attr))
- **Soft lines as hard lines**
- **Extra bullet lists**, supporting alpha bullet `a.` `b.` and roman bullet (`i`, `ii`...etc.)
- **Mathematics**/Latex extension by enclosing `$$` for block and `$` for inline math (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/mathematics-extension/457/31))
- **Embed player** for media url (youtube, vimeo, mp4...etc.) (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/embedded-audio-and-video/441))
- **Soft lines as hard lines**
- **Emoji** support (inspired from [Markdown-it](https://markdown-it.github.io/))
- **SmartyPants** (inspired from [Daring Fireball - SmartyPants](https://daringfireball.net/projects/smartypants/))
- Tables:
- **Pipe tables** (inspired from Kramdown/[PanDoc](http://pandoc.org/README.html#pipe_tables))
- **Grid tables** (inspired from [Pandoc](http://pandoc.org/README.html#grid_tables))
- **Bootstrap** class (to output bootstrap class)
- Compatible with .NET 3.5, 4.0+ and .NET Core (`netstandard1.1+`)
## Download
@@ -47,40 +50,110 @@ Markdig is available as a NuGet package: [![NuGet](https://img.shields.io/nuget/
## Usage
The main entry point for the API is the `Markdown` class:
The main entry point for the API is the `Markdig.Markdown` class:
By default, without any options, Markdig is using the plain CommonMark parser:
```csharp
var result = Markdown.ToHtml("This is a text with some **emphasis**");
var result = Markdown.ToHtml("This is a text with some *emphasis*");
Console.WriteLine(result); // prints: <p>This is a text with some <em>emphasis</em></p>
```
In order to activate all extensions (except Emoji)
In order to activate most of all advanced extensions (except Emoji, SoftLine as HarLine and SmartyPants)
```csharp
var result = Markdown.ToHtml("This is a text with some **emphasis**", new MarkdownPipeline().UseAllExtensions());
// Configure the pipeline with all advanced extensions active
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
var result = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
```
You can have a look at the [MarkdownPipeline](https://github.com/lunet-io/markdig/blob/src/Markdig/MarkdownPipeline.cs) file that describes all actionable extensions.
You can have a look at the [MarkdownExtensions](https://github.com/lunet-io/markdig/blob/master/src/Markdig/MarkdownExtensions.cs) that describes all actionable extensions (by modifying the MarkdownPipeline)
## License
This software is released under the [BSD-Clause 2 license](http://opensource.org/licenses/BSD-2-Clause).
This software is released under the [BSD-Clause 2 license](https://github.com/lunet-io/markdig/blob/master/license.txt).
## Benchmarking
This is an early preview of the benchmarking against various implementations:
- Markdig: itself
- CommonMarkCpp: [cmark](https://github.com/jgm/cmark), Reference C implementation of CommonMark, no support for extensions
- [CommonMark.NET](https://github.com/Knagis/CommonMark.NET): CommonMark implementation for .NET, no support for extensions, port of cmark
- [CommonMarkNet (devel)](https://github.com/AMDL/CommonMark.NET/tree/pipe-tables): An evolution of CommonMark.NET, supports extensions, not released yet
- [MarkdownDeep](https://github.com/toptensoftware/markdowndeep) another .NET implementation
- [MarkdownSharp](https://github.com/Kiri-rin/markdownsharp): Open source C# implementation of Markdown processor, as featured on Stack Overflow, regexp based.
- [Moonshine](https://github.com/brandonc/moonshine): popular C Markdown processor
Markdig is roughly x100 times faster than MarkdownSharp and extremelly competitive to other implems (that are not feature wise comparable)
Performance in x86:
```
// * Summary *
BenchmarkDotNet-Dev=v0.9.6.0+
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3319351 ticks, Resolution=301.2637 ns, Timer=TSC
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
JitModules=clrjit-v4.6.1080.0
Type=Program Mode=SingleRun LaunchCount=2
WarmupCount=2 TargetCount=10
Method | Median | StdDev | Gen 0 | Gen 1 | Gen 2 | Bytes Allocated/Op |
--------------------- |---------- |---------- |------- |------ |------- |------------------- |
TestMarkdig | 5.4870 ms | 0.0158 ms | 193.00 | 12.00 | 84.00 | 1,425,192.72 |
TestCommonMarkCpp | 4.0134 ms | 0.1008 ms | - | - | 180.00 | 454,859.74 |
TestCommonMarkNet | 4.6139 ms | 0.0581 ms | 193.00 | 12.00 | 84.00 | 1,406,367.27 |
TestCommonMarkNetNew | 5.5327 ms | 0.0461 ms | 193.00 | 96.00 | 84.00 | 1,738,465.42 |
TestMarkdownDeep | 7.5910 ms | 0.1006 ms | 205.00 | 96.00 | 84.00 | 1,758,383.79 |
TestMoonshine | 5.8843 ms | 0.1758 ms | - | - | 215.00 | 565,000.73 |
// * Diagnostic Output - MemoryDiagnoser *
// ***** BenchmarkRunner: End *****
```
Performance for x64:
```
// * Summary *
BenchmarkDotNet-Dev=v0.9.6.0+
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3319351 ticks, Resolution=301.2637 ns, Timer=TSC
HostCLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT]
JitModules=clrjit-v4.6.1080.0
Type=Program Mode=SingleRun LaunchCount=2
WarmupCount=2 TargetCount=10
Method | Median | StdDev | Gen 0 | Gen 1 | Gen 2 | Bytes Allocated/Op |
--------------------- |---------- |---------- |------- |------- |------ |------------------- |
TestMarkdig | 5.9539 ms | 0.0495 ms | 157.00 | 96.00 | 84.00 | 1,767,834.52 |
TestCommonMarkNet | 4.3158 ms | 0.0161 ms | 157.00 | 96.00 | 84.00 | 1,747,432.06 |
TestCommonMarkNetNew | 5.3421 ms | 0.0435 ms | 229.00 | 168.00 | 84.00 | 2,323,922.97 |
TestMarkdownDeep | 7.4750 ms | 0.0281 ms | 318.00 | 186.00 | 84.00 | 2,576,728.69 |
// * Diagnostic Output - MemoryDiagnoser *
// ***** BenchmarkRunner: End *****
```
## Credits
Thanks to the fantastic work done by [John Mac Farlane](http://johnmacfarlane.net/) for the CommonMark specs and all the people involved in making Markdown a better standard!
This project would not have been possible without this huge foundation.
Thanks also to the project [BenchmarkDotNet](https://github.com/PerfDotNet/BenchmarkDotNet) that makes benchmarking so easy to setup!
## Author
Alexandre MUTEL aka [xoofx](http://xoofx.com)

View File

@@ -98,6 +98,14 @@ This is ``a code span''``
<p>This is <code>a code span''</code></p>
````````````````````````````````
```````````````````````````````` example
hello ``there```
test
.
<p>hello &ldquo;there&rdquo;`
test</p>
````````````````````````````````
An emphasis starting inside left/right quotes will span over the right quote:
```````````````````````````````` example

View File

@@ -29,7 +29,7 @@ namespace Markdig.Tests
// articles, slide shows, letters, and lecture notes.
//
// What distinguishes Markdown from many other lightweight markup
// syntaxes, which are often easier to write, is its readibility.
// syntaxes, which are often easier to write, is its readability.
// As Gruber writes:
//
// > The overriding design goal for Markdown's formatting syntax is
@@ -17088,7 +17088,7 @@ namespace Markdig.Tests
// <p>The following text <del>is deleted</del></p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 1, "Extensions Strikethrough");
TestParser.TestSpec("The following text ~~is deleted~~", "<p>The following text <del>is deleted</del></p>", "emphasis_extra");
TestParser.TestSpec("The following text ~~is deleted~~", "<p>The following text <del>is deleted</del></p>", "emphasisextra");
}
}
// ## Superscript and Subscript
@@ -17110,7 +17110,7 @@ namespace Markdig.Tests
// <p>H<sub>2</sub>O is a liquid. 2<sup>10</sup> is 1024</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 2, "Extensions Superscript and Subscript");
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>", "emphasis_extra");
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>", "emphasisextra");
}
}
// ## Inserted
@@ -17132,7 +17132,7 @@ namespace Markdig.Tests
// <p><ins>Inserted text</ins></p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 3, "Extensions Inserted");
TestParser.TestSpec("++Inserted text++", "<p><ins>Inserted text</ins></p>", "emphasis_extra");
TestParser.TestSpec("++Inserted text++", "<p><ins>Inserted text</ins></p>", "emphasisextra");
}
}
// ## Marked
@@ -17154,7 +17154,7 @@ namespace Markdig.Tests
// <p><mark>Marked text</mark></p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 4, "Extensions Marked");
TestParser.TestSpec("==Marked text==", "<p><mark>Marked text</mark></p>", "emphasis_extra");
TestParser.TestSpec("==Marked text==", "<p><mark>Marked text</mark></p>", "emphasisextra");
}
}
// # Extensions
@@ -17991,7 +17991,7 @@ namespace Markdig.Tests
// </ol>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 1, "Extensions Ordered list with alpha letter");
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>", "list_extra");
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>", "listextra");
}
}
// It works also for uppercase alpha:
@@ -18017,7 +18017,7 @@ namespace Markdig.Tests
// </ol>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 2, "Extensions Ordered list with alpha letter");
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>", "list_extra");
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>", "listextra");
}
}
// Like for numbered list, a list can start with a different letter
@@ -18041,7 +18041,7 @@ namespace Markdig.Tests
// </ol>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 3, "Extensions Ordered list with alpha letter");
TestParser.TestSpec("b. First item\nc. Second item", "<ol type=\"a\" start=\"b\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "list_extra");
TestParser.TestSpec("b. First item\nc. Second item", "<ol type=\"a\" start=\"b\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "listextra");
}
}
// A different type of list will break the existing list:
@@ -18069,7 +18069,7 @@ namespace Markdig.Tests
// </ol>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 4, "Extensions Ordered list with alpha letter");
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>", "list_extra");
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>", "listextra");
}
}
// ## Ordered list with roman letter
@@ -18099,7 +18099,7 @@ namespace Markdig.Tests
// </ol>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 5, "Extensions Ordered list with roman letter");
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>", "list_extra");
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>", "listextra");
}
}
// It works also for uppercase alpha:
@@ -18127,7 +18127,7 @@ namespace Markdig.Tests
// </ol>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 6, "Extensions Ordered list with roman letter");
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>", "list_extra");
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>", "listextra");
}
}
// Like for numbered list, a list can start with a different letter
@@ -18151,7 +18151,7 @@ namespace Markdig.Tests
// </ol>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 7, "Extensions Ordered list with roman letter");
TestParser.TestSpec("ii. First item\niii. Second item", "<ol type=\"i\" start=\"ii\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "list_extra");
TestParser.TestSpec("ii. First item\niii. Second item", "<ol type=\"i\" start=\"ii\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "listextra");
}
}
// # Extensions
@@ -18863,7 +18863,6 @@ namespace Markdig.Tests
TestParser.TestSpec("This is ``a code span''`` ", "<p>This is <code>a code span''</code></p>", "smartypants");
}
}
// An emphasis starting inside left/right quotes will span over the right quote:
[TestFixture]
public partial class TestExtensionsSmartyPantsQuotes
{
@@ -18874,35 +18873,38 @@ namespace Markdig.Tests
// Section: Extensions SmartyPants Quotes
//
// The following CommonMark:
// This is "a *text" with an emphasis*
// hello ``there```
// test
//
// Should be rendered as:
// <p>This is &ldquo;a <em>text&rdquo; with an emphasis</em></p>
// <p>hello &ldquo;there&rdquo;`
// test</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 15, "Extensions SmartyPants Quotes");
TestParser.TestSpec("This is \"a *text\" with an emphasis*", "<p>This is &ldquo;a <em>text&rdquo; with an emphasis</em></p>", "smartypants");
TestParser.TestSpec("hello ``there```\ntest", "<p>hello &ldquo;there&rdquo;`\ntest</p>", "smartypants");
}
}
// ## SmartyPants Separators
// An emphasis starting inside left/right quotes will span over the right quote:
[TestFixture]
public partial class TestExtensionsSmartyPantsSeparators
public partial class TestExtensionsSmartyPantsQuotes
{
[Test]
public void Example016()
{
// Example 16
// Section: Extensions SmartyPants Separators
// Section: Extensions SmartyPants Quotes
//
// The following CommonMark:
// This is a -- text
// This is "a *text" with an emphasis*
//
// Should be rendered as:
// <p>This is a &ndash; text</p>
// <p>This is &ldquo;a <em>text&rdquo; with an emphasis</em></p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 16, "Extensions SmartyPants Separators");
TestParser.TestSpec("This is a -- text", "<p>This is a &ndash; text</p>", "smartypants");
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 16, "Extensions SmartyPants Quotes");
TestParser.TestSpec("This is \"a *text\" with an emphasis*", "<p>This is &ldquo;a <em>text&rdquo; with an emphasis</em></p>", "smartypants");
}
}
// ## SmartyPants Separators
[TestFixture]
public partial class TestExtensionsSmartyPantsSeparators
{
@@ -18913,13 +18915,13 @@ namespace Markdig.Tests
// Section: Extensions SmartyPants Separators
//
// The following CommonMark:
// This is a --- text
// This is a -- text
//
// Should be rendered as:
// <p>This is a &mdash; text</p>
// <p>This is a &ndash; text</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 17, "Extensions SmartyPants Separators");
TestParser.TestSpec("This is a --- text", "<p>This is a &mdash; text</p>", "smartypants");
TestParser.TestSpec("This is a -- text", "<p>This is a &ndash; text</p>", "smartypants");
}
}
[TestFixture]
@@ -18932,12 +18934,31 @@ namespace Markdig.Tests
// Section: Extensions SmartyPants Separators
//
// The following CommonMark:
// This is a --- text
//
// Should be rendered as:
// <p>This is a &mdash; text</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 18, "Extensions SmartyPants Separators");
TestParser.TestSpec("This is a --- text", "<p>This is a &mdash; text</p>", "smartypants");
}
}
[TestFixture]
public partial class TestExtensionsSmartyPantsSeparators
{
[Test]
public void Example019()
{
// Example 19
// Section: Extensions SmartyPants Separators
//
// The following CommonMark:
// This is a en ellipsis...
//
// Should be rendered as:
// <p>This is a en ellipsis&hellip;</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 18, "Extensions SmartyPants Separators");
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 19, "Extensions SmartyPants Separators");
TestParser.TestSpec("This is a en ellipsis...", "<p>This is a en ellipsis&hellip;</p>", "smartypants");
}
}

View File

@@ -42,14 +42,14 @@ SOFTWARE.
new KeyValuePair<string, string>(Host.ResolvePath("PipeTableSpecs.md"), "pipetables"),
new KeyValuePair<string, string>(Host.ResolvePath("FootnotesSpecs.md"), "footnotes"),
new KeyValuePair<string, string>(Host.ResolvePath("GenericAttributesSpecs.md"), "attributes"),
new KeyValuePair<string, string>(Host.ResolvePath("EmphasisExtraSpecs.md"), "emphasis_extra"),
new KeyValuePair<string, string>(Host.ResolvePath("EmphasisExtraSpecs.md"), "emphasisextra"),
new KeyValuePair<string, string>(Host.ResolvePath("HardlineBreakSpecs.md"), "hardlinebreak"),
new KeyValuePair<string, string>(Host.ResolvePath("GridTableSpecs.md"), "gridtables"),
new KeyValuePair<string, string>(Host.ResolvePath("CustomContainerSpecs.md"), "customcontainers+attributes"),
new KeyValuePair<string, string>(Host.ResolvePath("DefinitionListSpecs.md"), "definitionlists+attributes"),
new KeyValuePair<string, string>(Host.ResolvePath("EmojiSpecs.md"), "emojis"),
new KeyValuePair<string, string>(Host.ResolvePath("AbbreviationSpecs.md"), "abbreviations"),
new KeyValuePair<string, string>(Host.ResolvePath("ListExtraSpecs.md"), "list_extra"),
new KeyValuePair<string, string>(Host.ResolvePath("ListExtraSpecs.md"), "listextra"),
new KeyValuePair<string, string>(Host.ResolvePath("FigureFooterAndCiteSpecs.md"), "figures+footers+cites"),
new KeyValuePair<string, string>(Host.ResolvePath("MathSpecs.md"), "math"),
new KeyValuePair<string, string>(Host.ResolvePath("BootstrapSpecs.md"), "bootstrap+pipetables+figures+attributes"),

View File

@@ -14,10 +14,10 @@ namespace Markdig.Tests
{
foreach (var pipeline in GetPipeline(extensions))
{
Console.WriteLine($"Pipeline configured with extensions: {extensions}");
Console.WriteLine($"Pipeline configured with extensions: {pipeline.Key}");
// Uncomment this line to get more debug information for process inlines.
//pipeline.DebugLog = Console.Out;
var result = Markdown.ToHtml(inputText, pipeline);
var result = Markdown.ToHtml(inputText, pipeline.Value);
result = Compact(result);
expectedOutputText = Compact(expectedOutputText);
@@ -34,86 +34,38 @@ namespace Markdig.Tests
}
}
private static IEnumerable<MarkdownPipeline> GetPipeline(string extensionsGroupText)
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 MarkdownPipeline();
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
.UseAbbreviation()
//.UseAutoIdentifier()
.UseCite()
.UseCustomContainer()
.UseDefinitionList()
.UseEmphasisExtra()
.UseFigure()
.UseFooter()
.UseFootnotes()
.UseGridTable()
.UseMath()
.UseMedia()
.UsePipeTable()
.UseListExtra()
.UseGenericAttributes().Build());
yield break;
}
var extensionGroups = extensionsGroupText.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var extensionsText in extensionGroups)
{
var pipeline = new MarkdownPipeline();
foreach (var extension in extensionsText.Split(new[] { '+' }, StringSplitOptions.RemoveEmptyEntries))
{
switch (extension.ToLowerInvariant())
{
case "pipetables":
pipeline.UsePipeTable();
break;
case "emphasis_extra":
pipeline.UseEmphasisExtra();
break;
case "list_extra":
pipeline.UseListExtra();
break;
case "hardlinebreak":
pipeline.UseSoftlineBreakAsHardlineBreak();
break;
case "footnotes":
pipeline.UseFootnotes();
break;
case "footers":
pipeline.UseFooter();
break;
case "cites":
pipeline.UseCite();
break;
case "attributes":
pipeline.UseGenericAttributes();
break;
case "gridtables":
pipeline.UseGridTable();
break;
case "abbreviations":
pipeline.UseAbbreviation();
break;
case "emojis":
pipeline.UseEmojiAndSmiley();
break;
case "definitionlists":
pipeline.UseDefinitionList();
break;
case "customcontainers":
pipeline.UseCustomContainer();
break;
case "figures":
pipeline.UseFigure();
break;
case "math":
pipeline.UseMath();
break;
case "bootstrap":
pipeline.UseBootstrap();
break;
case "medias":
pipeline.UseMedia();
break;
case "smartypants":
pipeline.UseSmartyPants();
break;
case "autoidentifiers":
pipeline.UseAutoIdentifier();
break;
default:
Console.WriteLine($"Unsupported extension: {extension}");
break;
}
}
yield return pipeline;
var pipeline = new MarkdownPipelineBuilder().Configure(extensionsText);
yield return new KeyValuePair<string, MarkdownPipeline>(extensionsText, pipeline.Build());
}
}

View File

@@ -22,39 +22,52 @@ Later in a text we are using HTML and it becomes an abbr tag HTML
//");
//var result = Markdown.ToHtml(text, new MarkdownPipeline().UseFootnotes().UseEmphasisExtra());
var result = Markdown.ToHtml(text, new MarkdownPipeline().UseAbbreviation());
var result = Markdown.ToHtml(text, new MarkdownPipelineBuilder().UseAbbreviation().Build());
//File.WriteAllText("test.html", result, Encoding.UTF8);
Console.WriteLine(result);
}
// Test for emoji and smileys
// var text = @" This is a test with a :) and a :angry: smiley";
[Test]
public void TestSamePipelineAllExtensions()
{
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
// Reuse the same pipeline
var result1 = Markdown.ToHtml("This is a \"\"citation\"\"", pipeline);
var result2 = Markdown.ToHtml("This is a \"\"citation\"\"", pipeline);
Assert.AreEqual("<p>This is a <cite>citation</cite></p>", result1.Trim());
Assert.AreEqual(result1, result2);
}
// Test for emoji and smileys
// var text = @" This is a test with a :) and a :angry: smiley";
// Test for definition lists:
//
// var text = @"
//Term 1
//: This is a definition item
// With a paragraph
// > This is a block quote
// Test for definition lists:
//
// var text = @"
//Term 1
//: This is a definition item
// With a paragraph
// > This is a block quote
// - This is a list
// - item2
// - This is a list
// - item2
// ```java
// Test
// ```java
// Test
// ```
// ```
// And a lazy line
//: This ia another definition item
// And a lazy line
//: This ia another definition item
//Term2
//Term3 *with some inline*
//: This is another definition for term2
//";
//Term2
//Term3 *with some inline*
//: This is another definition for term2
//";
// Test for grid table

View File

@@ -0,0 +1,48 @@
using System;
using System.Text;
using Microsoft.AspNetCore.Mvc;
namespace Markdig.WebApp
{
public class ApiController : Controller
{
// GET api/to_html?text=xxx&extensions=advanced
[Route("api/to_html")]
[HttpGet()]
public object Get([FromQuery] string text, [FromQuery] string extension)
{
try
{
if (text == null)
{
text = string.Empty;
}
if (text.Length > 1000)
{
text = text.Substring(0, 1000);
}
var pipeline = new MarkdownPipelineBuilder().Configure(extension).Build();
var result = Markdown.ToHtml(text, pipeline);
return new {name = "markdig", html = result, version = Markdown.Version};
}
catch (Exception ex)
{
return new { name = "markdig", html = "exception: " + GetPrettyMessageFromException(ex), version = Markdown.Version };
}
}
private static string GetPrettyMessageFromException(Exception exception)
{
var builder = new StringBuilder();
while (exception != null)
{
builder.Append(exception.Message);
exception = exception.InnerException;
}
return builder.ToString();
}
}
}

View File

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

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
namespace Markdig.WebApp
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Markdig.WebApp
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsEnvironment("Development"))
{
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
builder.AddApplicationInsightsSettings(developerMode: true);
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseApplicationInsightsRequestTelemetry();
app.UseApplicationInsightsExceptionTelemetry();
app.UseMvc();
}
}
}

View File

@@ -0,0 +1,10 @@
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",
"Microsoft": "Information"
}
}
}

View File

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

View File

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

View File

@@ -12,18 +12,18 @@ namespace Markdig.Extensions.Abbreviations
/// <seealso cref="Markdig.IMarkdownExtension" />
public class AbbreviationExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
pipeline.BlockParsers.AddIfNotAlready<AbbreviationParser>();
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
if (htmlRenderer != null)
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null && !htmlRenderer.ObjectRenderers.Contains<HtmlAbbreviationRenderer>())
{
if (!htmlRenderer.ObjectRenderers.Contains<HtmlAbbreviationRenderer>())
{
// Must be inserted before CodeBlockRenderer
htmlRenderer.ObjectRenderers.Insert(0, new HtmlAbbreviationRenderer());
}
// Must be inserted before CodeBlockRenderer
htmlRenderer.ObjectRenderers.Insert(0, new HtmlAbbreviationRenderer());
}
}
}

View File

@@ -41,7 +41,7 @@ namespace Markdig.Extensions.AutoIdentifiers
};
}
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
var headingBlockParser = pipeline.BlockParsers.Find<HeadingBlockParser>();
if (headingBlockParser != null)
@@ -59,6 +59,10 @@ namespace Markdig.Extensions.AutoIdentifiers
}
}
public void Setup(IMarkdownRenderer renderer)
{
}
/// <summary>
/// Process on a new <see cref="HeadingBlock"/>
/// </summary>

View File

@@ -2,6 +2,7 @@
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Renderers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
@@ -14,13 +15,17 @@ namespace Markdig.Extensions.Bootstrap
/// <seealso cref="Markdig.IMarkdownExtension" />
public class BootstrapExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
// Make sure we don't have a delegate twice
pipeline.DocumentProcessed -= PipelineOnDocumentProcessed;
pipeline.DocumentProcessed += PipelineOnDocumentProcessed;
}
public void Setup(IMarkdownRenderer renderer)
{
}
private static void PipelineOnDocumentProcessed(MarkdownDocument document)
{
foreach(var node in document.Descendants())

View File

@@ -15,28 +15,25 @@ namespace Markdig.Extensions.Cites
/// <seealso cref="Markdig.IMarkdownExtension" />
public class CiteExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
var parser = pipeline.InlineParsers.FindExact<EmphasisInlineParser>();
if (parser != null)
if (parser != null && !parser.HasEmphasisChar('"'))
{
foreach (var emphasis in parser.EmphasisDescriptors)
{
if (emphasis.Character == '"')
{
return;
}
}
parser.EmphasisDescriptors.Add(new EmphasisDescriptor('"', 2, 2, false));
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
// Extend the rendering here.
var emphasisRenderer = htmlRenderer.ObjectRenderers.FindExact<EmphasisInlineRenderer>();
var emphasisRenderer = renderer.ObjectRenderers.FindExact<EmphasisInlineRenderer>();
if (emphasisRenderer != null)
{
// TODO: Use an ordered list instead as we don't know if this specific GetTag has been already added
var previousTag = emphasisRenderer.GetTag;
emphasisRenderer.GetTag = inline => GetTag(inline) ?? previousTag(inline);
}

View File

@@ -13,7 +13,7 @@ namespace Markdig.Extensions.CustomContainers
/// <seealso cref="Markdig.IMarkdownExtension" />
public class CustomContainerExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<CustomContainerParser>())
{
@@ -23,7 +23,7 @@ namespace Markdig.Extensions.CustomContainers
// Plug the inline parser for CustomContainerInline
var inlineParser = pipeline.InlineParsers.Find<EmphasisInlineParser>();
if (inlineParser != null)
if (inlineParser != null && !inlineParser.HasEmphasisChar(':'))
{
inlineParser.EmphasisDescriptors.Add(new EmphasisDescriptor(':', 2, 2, true));
var previousCreateEmphasisInline = inlineParser.CreateEmphasisInline;
@@ -36,8 +36,11 @@ namespace Markdig.Extensions.CustomContainers
return previousCreateEmphasisInline?.Invoke(emphasisChar, strong);
};
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
if (!htmlRenderer.ObjectRenderers.Contains<HtmlCustomContainerRenderer>())
@@ -51,6 +54,7 @@ namespace Markdig.Extensions.CustomContainers
htmlRenderer.ObjectRenderers.Insert(0, new HtmlCustomContainerInlineRenderer());
}
}
}
}
}

View File

@@ -11,15 +11,18 @@ namespace Markdig.Extensions.DefinitionLists
/// <seealso cref="Markdig.IMarkdownExtension" />
public class DefinitionListExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<DefinitionListParser>())
{
// Insert the parser before any other parsers
pipeline.BlockParsers.Insert(0, new DefinitionListParser());
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
if (!htmlRenderer.ObjectRenderers.Contains<HtmlDefinitionListRenderer>())

View File

@@ -2,6 +2,8 @@
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Renderers;
namespace Markdig.Extensions.Emoji
{
/// <summary>
@@ -10,7 +12,7 @@ namespace Markdig.Extensions.Emoji
/// <seealso cref="Markdig.IMarkdownExtension" />
public class EmojiExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.InlineParsers.Contains<EmojiParser>())
{
@@ -18,5 +20,9 @@ namespace Markdig.Extensions.Emoji
pipeline.InlineParsers.Insert(0, new EmojiParser());
}
}
public void Setup(IMarkdownRenderer renderer)
{
}
}
}

View File

@@ -29,7 +29,7 @@ namespace Markdig.Extensions.EmphasisExtra
/// </summary>
public EmphasisExtraOptions Options { get; }
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
var parser = pipeline.InlineParsers.FindExact<EmphasisInlineParser>();
if (parser != null)
@@ -83,8 +83,11 @@ namespace Markdig.Extensions.EmphasisExtra
parser.EmphasisDescriptors.Add(new EmphasisDescriptor('=', 2, 2, true));
}
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
// Extend the rendering here.

View File

@@ -14,7 +14,7 @@ namespace Markdig.Extensions.Figures
/// <seealso cref="Markdig.IMarkdownExtension" />
public class FigureExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<FigureBlockParser>())
{
@@ -28,7 +28,11 @@ namespace Markdig.Extensions.Figures
pipeline.BlockParsers.Insert(0, new FigureBlockParser());
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
}
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
htmlRenderer.ObjectRenderers.AddIfNotAlready<HtmlFigureRenderer>();

View File

@@ -13,7 +13,7 @@ namespace Markdig.Extensions.Footers
/// <seealso cref="Markdig.IMarkdownExtension" />
public class FooterExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<FooterBlockParser>())
{
@@ -27,8 +27,11 @@ namespace Markdig.Extensions.Footers
pipeline.BlockParsers.Insert(0, new FooterBlockParser());
}
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
htmlRenderer.ObjectRenderers.AddIfNotAlready(new HtmlFooterBlockRenderer());

View File

@@ -11,15 +11,18 @@ namespace Markdig.Extensions.Footnotes
/// <seealso cref="Markdig.IMarkdownExtension" />
public class FootnoteExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<FootnoteParser>())
{
// Insert the parser before any other parsers
pipeline.BlockParsers.Insert(0, new FootnoteParser());
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
htmlRenderer.ObjectRenderers.AddIfNotAlready(new HtmlFootnoteGroupRenderer());

View File

@@ -5,6 +5,7 @@
using System;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Renderers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
@@ -18,7 +19,7 @@ namespace Markdig.Extensions.GenericAttributes
/// <seealso cref="Markdig.IMarkdownExtension" />
public class GenericAttributesExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.InlineParsers.Contains<GenericAttributesParser>())
{
@@ -36,6 +37,10 @@ namespace Markdig.Extensions.GenericAttributes
}
}
public void Setup(IMarkdownRenderer renderer)
{
}
private bool TryProcessAttributesForHeading(BlockProcessor processor, ref StringSlice line, IBlock block)
{
// Try to find if there is any attributes { in the info string on the first line of a FencedCodeBlock

View File

@@ -3,6 +3,7 @@
// See the license.txt file in the project root for more information.
using Markdig.Parsers.Inlines;
using Markdig.Renderers;
namespace Markdig.Extensions.Hardlines
{
@@ -12,7 +13,7 @@ namespace Markdig.Extensions.Hardlines
/// <seealso cref="Markdig.IMarkdownExtension" />
public class SoftlineBreakAsHardlineExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
// Simply modify the LineBreakInlineParser
// TODO: We might want more options (like pandoc)
@@ -22,5 +23,9 @@ namespace Markdig.Extensions.Hardlines
parser.EnableSoftAsHard = true;
}
}
public void Setup(IMarkdownRenderer renderer)
{
}
}
}

View File

@@ -3,6 +3,7 @@
// See the license.txt file in the project root for more information.
using Markdig.Parsers;
using Markdig.Renderers;
namespace Markdig.Extensions.ListExtra
{
@@ -12,7 +13,7 @@ namespace Markdig.Extensions.ListExtra
/// <seealso cref="Markdig.IMarkdownExtension" />
public class ListExtraExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
var parser = pipeline.BlockParsers.Find<ListBlockParser>();
if (parser != null)
@@ -20,5 +21,9 @@ namespace Markdig.Extensions.ListExtra
parser.ItemParsers.AddIfNotAlready<ListExtraItemParser>();
}
}
public void Setup(IMarkdownRenderer renderer)
{
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Markdig.Extensions.Mathematics
/// <seealso cref="Markdig.IMarkdownExtension" />
public class MathExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
// Adds the inline parser
if (!pipeline.InlineParsers.Contains<MathInlineParser>())
@@ -27,8 +27,11 @@ namespace Markdig.Extensions.Mathematics
// Insert before EmphasisInlineParser to take precedence
pipeline.BlockParsers.Insert(0, new MathBlockParser());
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
if (!htmlRenderer.ObjectRenderers.Contains<HtmlMathInlineRenderer>())

View File

@@ -26,9 +26,13 @@ namespace Markdig.Extensions.Medias
public MediaOptions Options { get; }
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
}
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
var inlineRenderer = htmlRenderer.ObjectRenderers.FindExact<LinkInlineRenderer>();
@@ -45,7 +49,8 @@ namespace Markdig.Extensions.Medias
if (linkInline.IsImage && linkInline.Url != null)
{
Uri uri;
if (Uri.TryCreate(linkInline.Url, UriKind.RelativeOrAbsolute, out uri))
// Only process absolute Uri
if (Uri.TryCreate(linkInline.Url, UriKind.RelativeOrAbsolute, out uri) && uri.IsAbsoluteUri)
{
var htmlAttributes = new HtmlAttributes();
var fromAttributes = linkInline.TryGetAttributes();

View File

@@ -26,15 +26,18 @@ namespace Markdig.Extensions.SmartyPants
/// </summary>
public SmartyPantOptions Options { get; }
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.InlineParsers.Contains<SmaryPantsInlineParser>())
{
// Insert the parser after the code span parser
pipeline.InlineParsers.InsertAfter<CodeInlineParser>(new SmaryPantsInlineParser());
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
if (!htmlRenderer.ObjectRenderers.Contains<HtmlSmartyPantRenderer>())

View File

@@ -11,14 +11,17 @@ namespace Markdig.Extensions.Tables
/// <seealso cref="Markdig.IMarkdownExtension" />
public class GridTableExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<GridTableParser>())
{
pipeline.BlockParsers.Insert(0, new GridTableParser());
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null && !htmlRenderer.ObjectRenderers.Contains<HtmlTableRenderer>())
{
htmlRenderer.ObjectRenderers.Add(new HtmlTableRenderer());

View File

@@ -26,14 +26,17 @@ namespace Markdig.Extensions.Tables
/// </summary>
public PipeTableOptions Options { get; }
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.InlineParsers.Contains<PipeTableParser>())
{
pipeline.InlineParsers.InsertBefore<EmphasisInlineParser>(new PipeTableParser(Options));
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null && !htmlRenderer.ObjectRenderers.Contains<HtmlTableRenderer>())
{
htmlRenderer.ObjectRenderers.Add(new HtmlTableRenderer());

View File

@@ -14,6 +14,14 @@ namespace Markdig.Helpers
/// <remarks>We use a typed list and don't use extension methods because it would pollute all list implemts and the top level namespace.</remarks>
public class OrderedList<T> : List<T>
{
public OrderedList()
{
}
public OrderedList(IEnumerable<T> collection) : base(collection)
{
}
public bool InsertBefore<TElement>(T element) where TElement : T
{
if (element == null) throw new ArgumentNullException(nameof(element));

View File

@@ -1,6 +1,9 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using Markdig.Renderers;
namespace Markdig
{
/// <summary>
@@ -12,6 +15,12 @@ namespace Markdig
/// Setups this extension for the specified pipeline.
/// </summary>
/// <param name="pipeline">The pipeline.</param>
void Setup(MarkdownPipeline pipeline);
void Setup(MarkdownPipelineBuilder pipeline);
/// <summary>
/// Setups this extension for the specified renderer.
/// </summary>
/// <param name="renderer">The renderer.</param>
void Setup(IMarkdownRenderer renderer);
}
}

View File

@@ -4,16 +4,18 @@
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>8A58A7E2-627C-4F41-933F-5AC92ADFAB48</ProjectGuid>
<RootNamespace>Markdig</RootNamespace>
<OutputPath Condition="'$(OutputPath)'=='' ">.\Bin</OutputPath>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<ProduceOutputsOnBuild>True</ProduceOutputsOnBuild>
<TypeScriptCompileBlocked>True</TypeScriptCompileBlocked>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@@ -12,7 +12,7 @@ namespace Markdig
/// <summary>
/// Provides methods for parsing a Markdown string to a syntax tree and converting it to other formats.
/// </summary>
public static class Markdown
public static partial class Markdown
{
/// <summary>
/// Converts a Markdown string to HTML.
@@ -54,33 +54,33 @@ namespace Markdig
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
if (writer == null) throw new ArgumentNullException(nameof(writer));
pipeline = pipeline ?? new MarkdownPipeline();
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
// We override the renderer with our own writer
pipeline.Renderer = new HtmlRenderer(writer);
var renderer = new HtmlRenderer(writer);
pipeline.Setup(renderer);
var document = Parse(reader, pipeline);
pipeline.Renderer.Render(document);
renderer.Render(document);
writer.Flush();
}
/// <summary>
/// Converts a Markdown string using a custom <see cref="IMarkdownRenderer"/> specified in the <see cref="MarkdownPipeline.Renderer"/>.
/// Converts a Markdown string using a custom <see cref="IMarkdownRenderer"/>.
/// </summary>
/// <param name="reader">A Markdown text from a <see cref="TextReader"/>.</param>
/// <param name="renderer">The renderer to convert Markdown to.</param>
/// <param name="pipeline">The pipeline used for the conversion.</param>
/// <exception cref="System.ArgumentNullException">if reader or writer variable are null</exception>
public static object Convert(TextReader reader, MarkdownPipeline pipeline = null)
public static object Convert(TextReader reader, IMarkdownRenderer renderer, MarkdownPipeline pipeline = null)
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
pipeline = pipeline ?? new MarkdownPipeline();
if (pipeline.Renderer == null)
{
throw new InvalidOperationException("The property MarkdownPipeline.Renderer cannot be null");
}
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
var document = Parse(reader, pipeline);
return pipeline.Renderer.Render(document);
pipeline.Setup(renderer);
return renderer.Render(document);
}
/// <summary>
@@ -92,7 +92,7 @@ namespace Markdig
public static MarkdownDocument Parse(string markdown)
{
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
return Parse(new StringReader(markdown), new MarkdownPipeline());
return Parse(new StringReader(markdown));
}
/// <summary>
@@ -105,7 +105,7 @@ namespace Markdig
public static MarkdownDocument Parse(TextReader reader, MarkdownPipeline pipeline = null)
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
pipeline = pipeline ?? new MarkdownPipeline();
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
return MarkdownParser.Parse(reader, pipeline);
}

View File

@@ -1,6 +1,8 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using Markdig.Extensions.Abbreviations;
using Markdig.Extensions.AutoIdentifiers;
using Markdig.Extensions.Bootstrap;
@@ -19,6 +21,8 @@ using Markdig.Extensions.Mathematics;
using Markdig.Extensions.Medias;
using Markdig.Extensions.SmartyPants;
using Markdig.Extensions.Tables;
using Markdig.Parsers;
using Markdig.Parsers.Inlines;
namespace Markdig
{
@@ -28,16 +32,15 @@ namespace Markdig
public static class MarkdownExtensions
{
/// <summary>
/// Uses all extensions except the Emoji.
/// Uses all extensions except the BootStrap, Emoji, SmartyPants and soft line as hard line breaks extensions.
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseAllExtensions(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseAdvancedExtensions(this MarkdownPipelineBuilder pipeline)
{
return pipeline
.UseAbbreviation()
.UseAutoIdentifier()
.UseBootstrap()
.UseCite()
.UseCustomContainer()
.UseDefinitionList()
@@ -49,8 +52,7 @@ namespace Markdig
.UseMath()
.UseMedia()
.UsePipeTable()
.UseSoftlineBreakAsHardlineBreak()
.UseSmartyPants()
.UseListExtra()
.UseGenericAttributes(); // Must be last as it is one parser that is modifying other parsers
}
@@ -59,7 +61,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseCustomContainer(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseCustomContainer(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<CustomContainerExtension>();
return pipeline;
@@ -73,7 +75,7 @@ namespace Markdig
/// <returns>
/// The modified pipeline
/// </returns>
public static MarkdownPipeline UseMedia(this MarkdownPipeline pipeline, MediaOptions options = null)
public static MarkdownPipelineBuilder UseMedia(this MarkdownPipelineBuilder pipeline, MediaOptions options = null)
{
if (!pipeline.Extensions.Contains<MediaExtension>())
{
@@ -90,7 +92,7 @@ namespace Markdig
/// <returns>
/// The modified pipeline
/// </returns>
public static MarkdownPipeline UseAutoIdentifier(this MarkdownPipeline pipeline, AutoIdentifierOptions options = AutoIdentifierOptions.Default)
public static MarkdownPipelineBuilder UseAutoIdentifier(this MarkdownPipelineBuilder pipeline, AutoIdentifierOptions options = AutoIdentifierOptions.Default)
{
if (!pipeline.Extensions.Contains<AutoIdentifierExtension>())
{
@@ -107,7 +109,7 @@ namespace Markdig
/// <returns>
/// The modified pipeline
/// </returns>
public static MarkdownPipeline UseSmartyPants(this MarkdownPipeline pipeline, SmartyPantOptions options = null)
public static MarkdownPipelineBuilder UseSmartyPants(this MarkdownPipelineBuilder pipeline, SmartyPantOptions options = null)
{
if (!pipeline.Extensions.Contains<SmartyPantsExtension>())
{
@@ -121,7 +123,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseBootstrap(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseBootstrap(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<BootstrapExtension>();
return pipeline;
@@ -132,7 +134,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseMath(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseMath(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<MathExtension>();
return pipeline;
@@ -143,7 +145,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseFigure(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseFigure(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<FigureExtension>();
return pipeline;
@@ -154,7 +156,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseAbbreviation(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseAbbreviation(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<AbbreviationExtension>();
return pipeline;
@@ -165,7 +167,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseDefinitionList(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseDefinitionList(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<DefinitionListExtension>();
return pipeline;
@@ -179,7 +181,7 @@ namespace Markdig
/// <returns>
/// The modified pipeline
/// </returns>
public static MarkdownPipeline UsePipeTable(this MarkdownPipeline pipeline, PipeTableOptions options = null)
public static MarkdownPipelineBuilder UsePipeTable(this MarkdownPipelineBuilder pipeline, PipeTableOptions options = null)
{
if (!pipeline.Extensions.Contains<PipeTableExtension>())
{
@@ -193,7 +195,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseGridTable(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseGridTable(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<GridTableExtension>();
return pipeline;
@@ -205,7 +207,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseCite(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseCite(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<CiteExtension>();
return pipeline;
@@ -216,7 +218,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseFooter(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseFooter(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<FooterExtension>();
return pipeline;
@@ -227,7 +229,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseFootnotes(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseFootnotes(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<FootnoteExtension>();
return pipeline;
@@ -238,7 +240,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseSoftlineBreakAsHardlineBreak(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseSoftlineBreakAsHardlineBreak(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<SoftlineBreakAsHardlineExtension>();
return pipeline;
@@ -252,7 +254,7 @@ namespace Markdig
/// <returns>
/// The modified pipeline
/// </returns>
public static MarkdownPipeline UseEmphasisExtra(this MarkdownPipeline pipeline, EmphasisExtraOptions options = EmphasisExtraOptions.Default)
public static MarkdownPipelineBuilder UseEmphasisExtra(this MarkdownPipelineBuilder pipeline, EmphasisExtraOptions options = EmphasisExtraOptions.Default)
{
if (!pipeline.Extensions.Contains<EmphasisExtraExtension>())
{
@@ -268,7 +270,7 @@ namespace Markdig
/// <returns>
/// The modified pipeline
/// </returns>
public static MarkdownPipeline UseListExtra(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseListExtra(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<ListExtraExtension>();
return pipeline;
@@ -279,7 +281,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseGenericAttributes(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseGenericAttributes(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<GenericAttributesExtension>();
return pipeline;
@@ -290,10 +292,115 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseEmojiAndSmiley(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseEmojiAndSmiley(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<EmojiExtension>();
return pipeline;
}
/// <summary>
/// This will disable the HTML support in the markdown processor (for constraint/safe parsing).
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipelineBuilder DisableHtml(this MarkdownPipelineBuilder pipeline)
{
var parser = pipeline.BlockParsers.Find<HtmlBlockParser>();
if (parser != null)
{
pipeline.BlockParsers.Remove(parser);
}
var inlineParser = pipeline.InlineParsers.Find<AutolineInlineParser>();
if (inlineParser != null)
{
inlineParser.EnableHtmlParsing = false;
}
return pipeline;
}
/// <summary>
/// Configures the pipeline using a string that defines the extensions to activate.
/// </summary>
/// <param name="pipeline">The pipeline (e.g: advanced for <see cref="UseAdvancedExtensions"/>, pipetables+gridtables for <see cref="UsePipeTable"/> and <see cref="UseGridTable"/></param>
/// <param name="extensions">The extensions to activate as a string</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipelineBuilder Configure(this MarkdownPipelineBuilder pipeline, string extensions)
{
if (extensions == null)
{
return pipeline;
}
foreach (var extension in extensions.Split(new[] { '+' }, StringSplitOptions.RemoveEmptyEntries))
{
switch (extension.ToLowerInvariant())
{
case "advanced":
pipeline.UseAdvancedExtensions();
break;
case "pipetables":
pipeline.UsePipeTable();
break;
case "emphasisextra":
pipeline.UseEmphasisExtra();
break;
case "listextra":
pipeline.UseListExtra();
break;
case "hardlinebreak":
pipeline.UseSoftlineBreakAsHardlineBreak();
break;
case "footnotes":
pipeline.UseFootnotes();
break;
case "footers":
pipeline.UseFooter();
break;
case "cites":
pipeline.UseCite();
break;
case "attributes":
pipeline.UseGenericAttributes();
break;
case "gridtables":
pipeline.UseGridTable();
break;
case "abbreviations":
pipeline.UseAbbreviation();
break;
case "emojis":
pipeline.UseEmojiAndSmiley();
break;
case "definitionlists":
pipeline.UseDefinitionList();
break;
case "customcontainers":
pipeline.UseCustomContainer();
break;
case "figures":
pipeline.UseFigure();
break;
case "math":
pipeline.UseMath();
break;
case "bootstrap":
pipeline.UseBootstrap();
break;
case "medias":
pipeline.UseMedia();
break;
case "smartypants":
pipeline.UseSmartyPants();
break;
case "autoidentifiers":
pipeline.UseAutoIdentifier();
break;
default:
throw new ArgumentException($"unknown extension {extension}");
}
}
return pipeline;
}
}
}

View File

@@ -5,104 +5,51 @@ using System;
using System.IO;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Parsers.Inlines;
using Markdig.Renderers;
namespace Markdig
{
/// <summary>
/// This class allows to modify the pipeline to parse and render a Markdown document.
/// This class is the Markdown pipeline build from a <see cref="MarkdownPipelineBuilder"/>.
/// </summary>
public class MarkdownPipeline
{
// This class is immutable
/// <summary>
/// Initializes a new instance of the <see cref="MarkdownPipeline" /> class.
/// </summary>
public MarkdownPipeline()
internal MarkdownPipeline(OrderedList<IMarkdownExtension> extensions, BlockParserList blockParsers, InlineParserList inlineParsers, StringBuilderCache cache, TextWriter debugLog, ProcessDocumentDelegate documentProcessed)
{
if (blockParsers == null) throw new ArgumentNullException(nameof(blockParsers));
if (inlineParsers == null) throw new ArgumentNullException(nameof(inlineParsers));
// Add all default parsers
BlockParsers = new BlockParserList()
{
new ThematicBreakParser(),
new HeadingBlockParser(),
new QuoteBlockParser(),
new ListBlockParser(),
new HtmlBlockParser(),
new FencedCodeBlockParser(),
new IndentedCodeBlockParser(),
new ParagraphBlockParser(),
};
InlineParsers = new InlineParserList()
{
new HtmlEntityParser(),
new LinkInlineParser(),
new EscapeInlineParser(),
new EmphasisInlineParser(),
new CodeInlineParser(),
new AutolineInlineParser(),
new LineBreakInlineParser(),
};
Extensions = new OrderedList<IMarkdownExtension>();
Renderer = new HtmlRenderer(new StringWriter());
StringBuilderCache = new StringBuilderCache();
Extensions = extensions;
BlockParsers = blockParsers;
InlineParsers = inlineParsers;
StringBuilderCache = cache;
DebugLog = debugLog;
DocumentProcessed = documentProcessed;
}
internal OrderedList<IMarkdownExtension> Extensions { get; }
/// <summary>
/// Gets the block parsers.
/// </summary>
public BlockParserList BlockParsers { get; private set; }
internal BlockParserList BlockParsers { get; }
/// <summary>
/// Gets the inline parsers.
/// </summary>
public InlineParserList InlineParsers { get; private set; }
internal InlineParserList InlineParsers { get; }
/// <summary>
/// Gets or sets the renderer.
/// </summary>
public IMarkdownRenderer Renderer { get; set; }
internal StringBuilderCache StringBuilderCache { get; }
/// <summary>
/// Gets the register extensions.
/// </summary>
public OrderedList<IMarkdownExtension> Extensions { get; }
// TODO: Move the log to a better place
internal TextWriter DebugLog { get; }
/// <summary>
/// Gets or sets the string builder cache used by the parsers.
/// </summary>
public StringBuilderCache StringBuilderCache { get; set; }
internal ProcessDocumentDelegate DocumentProcessed;
/// <summary>
/// Gets or sets the debug log.
/// </summary>
public TextWriter DebugLog { get; set; }
/// <summary>
/// Occurs when a document has been processed after the <see cref="MarkdownParser.Parse"/> method.
/// </summary>
public event ProcessDocumentDelegate DocumentProcessed;
internal ProcessDocumentDelegate GetDocumentProcessed => DocumentProcessed;
/// <summary>
/// Initializes this instance.
/// </summary>
/// <exception cref="System.InvalidOperationException">An extension cannot be null</exception>
public void Initialize()
internal void Setup(IMarkdownRenderer renderer)
{
// Allow extensions to modify existing BlockParsers, InlineParsers and Renderer
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
foreach (var extension in Extensions)
{
if (extension == null)
{
throw new InvalidOperationException("An extension cannot be null");
}
extension.Setup(this);
extension.Setup(renderer);
}
}
}

View File

@@ -0,0 +1,118 @@
// 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.IO;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Parsers.Inlines;
using Markdig.Renderers;
namespace Markdig
{
/// <summary>
/// This class allows to modify the pipeline to parse and render a Markdown document.
/// </summary>
/// <remarks>NOTE: A pipeline is not thread-safe.</remarks>
public class MarkdownPipelineBuilder
{
private MarkdownPipeline pipeline;
/// <summary>
/// Initializes a new instance of the <see cref="MarkdownPipeline" /> class.
/// </summary>
public MarkdownPipelineBuilder()
{
// Add all default parsers
BlockParsers = new BlockParserList()
{
new ThematicBreakParser(),
new HeadingBlockParser(),
new QuoteBlockParser(),
new ListBlockParser(),
new HtmlBlockParser(),
new FencedCodeBlockParser(),
new IndentedCodeBlockParser(),
new ParagraphBlockParser(),
};
InlineParsers = new InlineParserList()
{
new HtmlEntityParser(),
new LinkInlineParser(),
new EscapeInlineParser(),
new EmphasisInlineParser(),
new CodeInlineParser(),
new AutolineInlineParser(),
new LineBreakInlineParser(),
};
Extensions = new OrderedList<IMarkdownExtension>();
StringBuilderCache = new StringBuilderCache();
}
/// <summary>
/// Gets the block parsers.
/// </summary>
public BlockParserList BlockParsers { get; private set; }
/// <summary>
/// Gets the inline parsers.
/// </summary>
public InlineParserList InlineParsers { get; private set; }
/// <summary>
/// Gets the register extensions.
/// </summary>
public OrderedList<IMarkdownExtension> Extensions { get; }
/// <summary>
/// Gets or sets the string builder cache used by the parsers.
/// </summary>
public StringBuilderCache StringBuilderCache { get; set; }
/// <summary>
/// Gets or sets the debug log.
/// </summary>
public TextWriter DebugLog { get; set; }
/// <summary>
/// Occurs when a document has been processed after the <see cref="MarkdownParser.Parse"/> method.
/// </summary>
public event ProcessDocumentDelegate DocumentProcessed;
internal ProcessDocumentDelegate GetDocumentProcessed => DocumentProcessed;
/// <summary>
/// Builds a pipeline from this instance. Once the pipeline is build, it cannot be modified.
/// </summary>
/// <exception cref="System.InvalidOperationException">An extension cannot be null</exception>
public MarkdownPipeline Build()
{
if (pipeline != null)
{
return pipeline;
}
// TODO: Review the whole initialization process for extensions
// - It does not prevent a user to modify the pipeline after it has been used
// - a pipeline is not thread safe.
// We should find a proper way to make the pipeline safely modifiable/freezable (PipelineBuilder -> Pipeline)
// Allow extensions to modify existing BlockParsers, InlineParsers and Renderer
foreach (var extension in Extensions)
{
if (extension == null)
{
throw new InvalidOperationException("An extension cannot be null");
}
extension.Setup(this);
}
pipeline = new MarkdownPipeline(new OrderedList<IMarkdownExtension>(Extensions), new BlockParserList(BlockParsers), new InlineParserList(InlineParsers), StringBuilderCache, DebugLog, GetDocumentProcessed);
return pipeline;
}
}
}

View File

@@ -1,6 +1,9 @@
// 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;
namespace Markdig.Parsers
{
/// <summary>
@@ -9,5 +12,19 @@ namespace Markdig.Parsers
/// <seealso cref="Markdig.Parsers.ParserList{Markdig.Parsers.BlockParser, Markdig.Parsers.BlockParserState}" />
public class BlockParserList : ParserList<BlockParser, BlockProcessor>
{
/// <summary>
/// Initializes a new instance of the <see cref="BlockParserList"/> class.
/// </summary>
public BlockParserList()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BlockParserList"/> class.
/// </summary>
/// <param name="parsers">The parsers.</param>
public BlockParserList(IEnumerable<BlockParser> parsers) : base(parsers)
{
}
}
}

View File

@@ -260,12 +260,10 @@ namespace Markdig.Parsers
public void GoToColumn(int newColumn)
{
// Optimized path when we are moving above the previous start of indent
if (newColumn > ColumnBeforeIndent)
if (newColumn >= ColumnBeforeIndent)
{
Line.Start = StartBeforeIndent;
Column = ColumnBeforeIndent;
ColumnBeforeIndent = 0;
StartBeforeIndent = 0;
}
else
{

View File

@@ -37,7 +37,7 @@ namespace Markdig.Parsers
}
// If we don't have a blank line, we reset to the indent
if (processor.Indent >= 4)
if (processor.Indent > 4)
{
processor.GoToCodeIndent();
}

View File

@@ -11,6 +11,14 @@ namespace Markdig.Parsers
/// <seealso cref="Markdig.Parsers.ParserList{Markdig.Parsers.InlineParser, Markdig.Parsers.InlineParserState}" />
public class InlineParserList : ParserList<InlineParser, InlineProcessor>
{
public InlineParserList()
{
}
public InlineParserList(IEnumerable<InlineParser> parsers) : base(parsers)
{
}
/// <summary>
/// Gets the registered delimiter processors.
/// </summary>

View File

@@ -18,8 +18,14 @@ namespace Markdig.Parsers.Inlines
public AutolineInlineParser()
{
OpeningCharacters = new[] {'<'};
EnableHtmlParsing = true;
}
/// <summary>
/// Gets or sets a value indicating whether to enable HTML parsing. Default is <c>true</c>
/// </summary>
public bool EnableHtmlParsing { get; set; }
public override bool Match(InlineProcessor processor, ref StringSlice slice)
{
string link;
@@ -29,7 +35,7 @@ namespace Markdig.Parsers.Inlines
{
processor.Inline = new AutolinkInline() {IsEmail = isEmail, Url = link};
}
else
else if (EnableHtmlParsing)
{
slice = saved;
string htmlTag;

View File

@@ -47,13 +47,13 @@ namespace Markdig.Parsers.Inlines
// The contents of the code span are the characters between the two backtick strings, with leading and trailing spaces and line endings removed, and whitespace collapsed to single spaces.
var pc = ' ';
int newLinesFound = 0;
while (c != '\0')
{
// Transform '\n' into a single space
if (c == '\n')
{
processor.LocalLineIndex++;
processor.LineIndex++;
newLinesFound++;
c = ' ';
}
@@ -104,6 +104,9 @@ namespace Markdig.Parsers.Inlines
Content = builder.ToString()
};
isMatching = true;
processor.LocalLineIndex += newLinesFound;
processor.LineIndex += newLinesFound;
}
// Release the builder if not used

View File

@@ -41,6 +41,23 @@ namespace Markdig.Parsers.Inlines
/// </summary>
public List<EmphasisDescriptor> EmphasisDescriptors { get; }
/// <summary>
/// Determines whether this parser is using the specified character as an emphasis delimiter.
/// </summary>
/// <param name="c">The character to look for.</param>
/// <returns><c>true</c> if this parser is using the specified character as an emphasis delimiter; otherwise <c>false</c></returns>
public bool HasEmphasisChar(char c)
{
foreach (var emphasis in EmphasisDescriptors)
{
if (emphasis.Character == c)
{
return true;
}
}
return false;
}
/// <summary>
/// Gets or sets the create emphasis inline delegate (allowing to create a different emphasis inline class)
/// </summary>

View File

@@ -41,7 +41,6 @@ namespace Markdig.Parsers
Reader = reader;
// Initialize the pipeline
pipeline.Initialize();
var stringBuilderCache = pipeline.StringBuilderCache ?? new StringBuilderCache();
document = new MarkdownDocument();
@@ -59,7 +58,7 @@ namespace Markdig.Parsers
DebugLog = pipeline.DebugLog
};
documentProcessed = pipeline.GetDocumentProcessed;
documentProcessed = pipeline.DocumentProcessed;
}
/// <summary>
@@ -72,7 +71,7 @@ namespace Markdig.Parsers
public static MarkdownDocument Parse(TextReader reader, MarkdownPipeline pipeline = null)
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
pipeline = pipeline ?? new MarkdownPipeline();
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
// Perform the parsing
var markdownParser = new MarkdownParser(reader, pipeline);

View File

@@ -1,6 +1,7 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
namespace Markdig.Parsers
{
/// <summary>
@@ -8,7 +9,7 @@ namespace Markdig.Parsers
/// </summary>
/// <typeparam name="TProcessor">Type of the parser processor</typeparam>
/// <seealso cref="Markdig.Parsers.IMarkdownParser{TParserState}" />
public class ParserBase<TProcessor> : IMarkdownParser<TProcessor>
public abstract class ParserBase<TProcessor> : IMarkdownParser<TProcessor>
{
/// <summary>
/// Gets the opening characters this parser will be triggered if the character is found.

View File

@@ -23,6 +23,10 @@ namespace Markdig.Parsers
{
}
protected ParserList(IEnumerable<T> parsers) : base(parsers)
{
}
/// <summary>
/// Gets the list of global parsers (that don't have any opening characters defined)
/// </summary>

View File

@@ -1,7 +1,5 @@
using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
@@ -20,8 +18,13 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: AssemblyVersion("0.1.0.0")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: InternalsVisibleTo("Scriban.Tests")]
[assembly: AssemblyVersion(Markdig.Markdown.Version)]
[assembly: AssemblyFileVersion(Markdig.Markdown.Version)]
namespace Markdig
{
public static partial class Markdown
{
public const string Version = "0.3.2";
}
}

View File

@@ -27,6 +27,8 @@ namespace Markdig.Renderers
{
if (writer == null) throw new ArgumentNullException(nameof(writer));
this.Writer = writer;
// By default we output a newline with '\n' only even on Windows platforms
Writer.NewLine = "\n";
}
/// <summary>

View File

@@ -1,6 +1,6 @@
{
"title": "Markdig",
"version": "0.1.0",
"version": "0.3.2",
"authors": [ "Alexandre Mutel" ],
"description": "A fast, powerfull, CommonMark compliant, extensible Markdown processor for .NET",
"copyright": "Alexandre Mutel",
@@ -10,7 +10,8 @@
"licenseUrl": "https://github.com/lunet-io/markdig/blob/master/license.txt",
"projectUrl": "https://github.com/lunet-io/markdig",
"requireLicenseAcceptance": false,
"tags": [ "Markdown CommonMark md html" ]
"releaseNotes": "Fix exception when Media extension was used with non absolute URI. Make the UseAdvancedExtensions() CommonMark compliant on core specs",
"tags": [ "Markdown CommonMark md html md2html" ]
},
"configurations": {
"Debug": {
@@ -28,9 +29,6 @@
}
}
},
"dependencies": {
"NETStandard.Library": "1.5.0-rc2-24027"
},
"frameworks": {
"net35": {
"buildOptions": {
@@ -61,6 +59,7 @@
},
"netstandard1.1": {
"dependencies": {
"NETStandard.Library": "1.5.0-rc2-24027",
"System.Threading": "4.0.11-rc2-24027",
"System.Threading.Tasks": "4.0.11-rc2-24027",
"System.Threading.Tasks.Parallel": "4.0.1-rc2-24027"

View File

@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
VisualStudioVersion = 14.0.25123.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Markdig", "Markdig\Markdig.xproj", "{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}"
EndProject
@@ -13,6 +13,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{061866E2-005C-4D13-A338-EA464BBEC60F}"
ProjectSection(SolutionItems) = preProject
..\license.txt = ..\license.txt
..\readme.md = ..\readme.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdig.Benchmarks", "Markdig.Benchmarks\Markdig.Benchmarks.csproj", "{6A19F040-BC7C-4283-873A-177B5324F1ED}"
@@ -20,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdig.Benchmarks", "Markd
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48} = {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Markdig.WebApp", "Markdig.WebApp\Markdig.WebApp.xproj", "{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -38,6 +41,10 @@ Global
{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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE