mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-12 13:54:45 +00:00
Compare commits
156 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16a9bbc84e | ||
|
|
0e5338a709 | ||
|
|
9139e0142b | ||
|
|
9a38312df0 | ||
|
|
d808dcf6f8 | ||
|
|
d5985fc94c | ||
|
|
994687d5ae | ||
|
|
2f4e958ab2 | ||
|
|
26beaa81da | ||
|
|
de5ed11963 | ||
|
|
b557d51276 | ||
|
|
27a8345943 | ||
|
|
131163ff9a | ||
|
|
241f674b99 | ||
|
|
194edee243 | ||
|
|
0995fa0cec | ||
|
|
6717be5210 | ||
|
|
e15745f346 | ||
|
|
a513b0c587 | ||
|
|
72adb963e8 | ||
|
|
aac7df6b87 | ||
|
|
4cf4bfb58f | ||
|
|
59630aec8e | ||
|
|
70c4f6deda | ||
|
|
e2b3f812cb | ||
|
|
fc8adc70e0 | ||
|
|
72c2c06fcb | ||
|
|
7174e32b7a | ||
|
|
600219529c | ||
|
|
d7fd04d14c | ||
|
|
2147e434f7 | ||
|
|
7bd00d115f | ||
|
|
80ef9d2799 | ||
|
|
d6a705d76c | ||
|
|
42472085a6 | ||
|
|
3897e875ee | ||
|
|
c63392657d | ||
|
|
34579b51a1 | ||
|
|
1bb35c5fc1 | ||
|
|
0c408951b8 | ||
|
|
218a094f0d | ||
|
|
3cc405b05b | ||
|
|
d58db530bb | ||
|
|
3628dc3b17 | ||
|
|
89ff42805d | ||
|
|
48866a2609 | ||
|
|
9cc5856c1c | ||
|
|
a4bb174a77 | ||
|
|
82987fa879 | ||
|
|
2df2ff17c5 | ||
|
|
4e4825cb3f | ||
|
|
ed2371beae | ||
|
|
2c2898769e | ||
|
|
001a99ab77 | ||
|
|
9233ec220c | ||
|
|
bb30dc21c5 | ||
|
|
c761fa2243 | ||
|
|
9d172ffcc1 | ||
|
|
1863e22328 | ||
|
|
c591d1950f | ||
|
|
339fcc9152 | ||
|
|
3a54f06540 | ||
|
|
3f305a25a8 | ||
|
|
5a210223b6 | ||
|
|
a7786d934d | ||
|
|
cb3c1f1505 | ||
|
|
50b33b8512 | ||
|
|
80790b5038 | ||
|
|
311c28ae60 | ||
|
|
9906a0554f | ||
|
|
7e92f1881d | ||
|
|
41911806a0 | ||
|
|
610f1519b0 | ||
|
|
29aa56b4f1 | ||
|
|
5004ecedb7 | ||
|
|
ba06a796dc | ||
|
|
b1d6f34976 | ||
|
|
28f4236a57 | ||
|
|
0739a82735 | ||
|
|
3ac9f2e788 | ||
|
|
d9607cc687 | ||
|
|
f0573ef9e2 | ||
|
|
017a3bd7e2 | ||
|
|
d6f9a1055c | ||
|
|
2612e9d565 | ||
|
|
1076d7af10 | ||
|
|
850cecfa6c | ||
|
|
bfdb03bd78 | ||
|
|
56b6e329d5 | ||
|
|
006e384d1a | ||
|
|
9cd9b683a5 | ||
|
|
e01e2c3d2b | ||
|
|
97ce0da564 | ||
|
|
26c5ce59b5 | ||
|
|
d2d94ecf39 | ||
|
|
0ec82bb81d | ||
|
|
ad9e941cb0 | ||
|
|
27dab6ca6d | ||
|
|
762196f03b | ||
|
|
26a565fa45 | ||
|
|
4369db1a43 | ||
|
|
d83f1f87cc | ||
|
|
14027f4be3 | ||
|
|
8bcfb53607 | ||
|
|
700bb21e03 | ||
|
|
1bc9d9083c | ||
|
|
1d7bb021a7 | ||
|
|
3305a74b06 | ||
|
|
6312bc0515 | ||
|
|
b595383281 | ||
|
|
bf85964543 | ||
|
|
d5b020b784 | ||
|
|
8ab0467e78 | ||
|
|
74fe3be7c7 | ||
|
|
b47ae8d9ba | ||
|
|
a926e1a326 | ||
|
|
6048406f52 | ||
|
|
1edf14c742 | ||
|
|
d4bb7c0a51 | ||
|
|
ba4e463517 | ||
|
|
2d0dae4ebb | ||
|
|
1a9d923c44 | ||
|
|
013ec0bf80 | ||
|
|
f82e874cb7 | ||
|
|
976fc569fa | ||
|
|
5aab21f0bf | ||
|
|
1df8344576 | ||
|
|
9eb84cf95e | ||
|
|
47d45b5577 | ||
|
|
ebcc286df0 | ||
|
|
f11d8037ac | ||
|
|
f668b3fe38 | ||
|
|
5ca4e31332 | ||
|
|
a8fbe79e30 | ||
|
|
bdb09b9820 | ||
|
|
79348ebea8 | ||
|
|
b366c1a5e3 | ||
|
|
a5e53b014f | ||
|
|
c2f21e21c8 | ||
|
|
81b8dce7a8 | ||
|
|
e6223f86d8 | ||
|
|
2054c16662 | ||
|
|
95641e562f | ||
|
|
e4953931c7 | ||
|
|
93b5b7a091 | ||
|
|
4f52e893ee | ||
|
|
23ede077a1 | ||
|
|
d5e6f17683 | ||
|
|
4d5980a485 | ||
|
|
19dd902519 | ||
|
|
f046a46275 | ||
|
|
25e9eafa8b | ||
|
|
f4ff981008 | ||
|
|
b20b111385 | ||
|
|
6ac2429e2a | ||
|
|
9d52732f18 |
38
.editorconfig
Normal file
38
.editorconfig
Normal file
@@ -0,0 +1,38 @@
|
||||
# EditorConfig is awesome:http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# All Files
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = crlf
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = false
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Solution Files
|
||||
[*.sln]
|
||||
indent_style = tab
|
||||
|
||||
# XML Project Files
|
||||
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
|
||||
indent_size = 2
|
||||
|
||||
# Configuration Files
|
||||
[*.{json,xml,yml,config,props,targets,nuspec,resx,ruleset}]
|
||||
indent_size = 2
|
||||
|
||||
# Markdown Files
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# Web Files
|
||||
[*.{htm,html,js,ts,css,scss,less}]
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
|
||||
# Bash Files
|
||||
[*.sh]
|
||||
end_of_line = lf
|
||||
46
appveyor.yml
Normal file
46
appveyor.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
version: 10.0.{build}
|
||||
image: Visual Studio 2017
|
||||
configuration: Release
|
||||
install:
|
||||
- ps: >-
|
||||
cd src
|
||||
|
||||
nuget restore Markdig.sln
|
||||
|
||||
$env:MARKDIG_BUILD_NUMBER = ([int]$env:APPVEYOR_BUILD_NUMBER).ToString("000")
|
||||
|
||||
$env:MARKDIG_VERSION_SUFFIX = ""
|
||||
|
||||
$env:appveyor_nuget_push = 'false'
|
||||
|
||||
if(-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) {
|
||||
if($env:appveyor_repo_tag -eq 'True') {
|
||||
if($env:appveyor_repo_tag_name -match '^v[0-9]') {
|
||||
$env:appveyor_nuget_push = 'true'
|
||||
$env:MARKDIG_VERSION_SUFFIX = ""
|
||||
}
|
||||
if($env:appveyor_repo_tag_name -eq 'latest') {
|
||||
$env:appveyor_nuget_push = 'true'
|
||||
$env:MARKDIG_VERSION_SUFFIX = "pre$env:MARKDIG_BUILD_NUMBER"
|
||||
}
|
||||
}
|
||||
}
|
||||
build:
|
||||
project: src/Markdig.sln
|
||||
verbosity: minimal
|
||||
before_package:
|
||||
- cmd: >-
|
||||
msbuild /t:pack /p:VersionSuffix="%MARKDIG_VERSION_SUFFIX%" /p:Configuration=Release Markdig/Markdig.csproj
|
||||
|
||||
msbuild /t:Clean Markdig/Markdig.csproj
|
||||
|
||||
msbuild /t:pack /p:VersionSuffix="%MARKDIG_VERSION_SUFFIX%" /p:Configuration=Release;SignAssembly=true Markdig/Markdig.csproj
|
||||
artifacts:
|
||||
- path: src\Markdig\Bin\Release\*.nupkg
|
||||
name: Markdig Nugets
|
||||
deploy:
|
||||
- provider: NuGet
|
||||
api_key:
|
||||
secure: 7cthHh+wYWZjhqxaxR6QObRaRnstvFkQOY7MkxIsC5kpQEBlKZXuinf0IybbYxJt
|
||||
on:
|
||||
appveyor_nuget_push: true
|
||||
64
readme.md
64
readme.md
@@ -1,4 +1,4 @@
|
||||
# Markdig [](https://ci.appveyor.com/project/xoofx/markdig) [](https://www.nuget.org/packages/Markdig/)
|
||||
# Markdig [](https://ci.appveyor.com/project/xoofx/markdig) [](https://www.nuget.org/packages/Markdig/) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRGHXBTP442JL)
|
||||
|
||||
<img align="right" width="160px" height="160px" src="img/markdig.png">
|
||||
|
||||
@@ -14,53 +14,58 @@ You can **try Markdig online** and compare it to other implementations on [babel
|
||||
- **Abstract Syntax Tree** with precise source code location for syntax tree, useful when building a Markdown editor.
|
||||
- Checkout [MarkdownEditor for Visual Studio](https://visualstudiogallery.msdn.microsoft.com/eaab33c3-437b-4918-8354-872dfe5d1bfe) powered by Markdig!
|
||||
- Converter to **HTML**
|
||||
- Passing more than **600+ tests** from the latest [CommonMark specs](http://spec.commonmark.org/)
|
||||
- Passing more than **600+ tests** from the latest [CommonMark specs (0.28)](http://spec.commonmark.org/)
|
||||
- Includes all the core elements of CommonMark:
|
||||
- including **GFM fenced code blocks**.
|
||||
- **Extensible** architecture
|
||||
- Even the core Markdown/CommonMark parsing is pluggable, so it allows to disable builtin Markdown/Commonmark parsing (e.g [Disable HTML parsing](https://github.com/lunet-io/markdig/blob/7964bd0160d4c18e4155127a4c863d61ebd8944a/src/Markdig/MarkdownExtensions.cs#L306)) or change behaviour (e.g change matching `#` of a headers with `@`)
|
||||
- Built-in with **20+ extensions**, including:
|
||||
- 2 kind of tables:
|
||||
- **Pipe tables** (inspired from Github tables and [PanDoc - Pipe Tables](http://pandoc.org/README.html#extension-pipe_tables))
|
||||
- **Grid tables** (inspired from [Pandoc - Grid Tables](http://pandoc.org/README.html#extension-grid_tables))
|
||||
- **Extra emphasis** (inspired from [Pandoc - Emphasis](http://pandoc.org/README.html#strikeout) and [Markdown-it](https://markdown-it.github.io/))
|
||||
- [**Pipe tables**](src/Markdig.Tests/Specs/PipeTableSpecs.md) (inspired from Github tables and [PanDoc - Pipe Tables](http://pandoc.org/README.html#extension-pipe_tables))
|
||||
- [**Grid tables**](src/Markdig.Tests/Specs/GridTableSpecs.md) (inspired from [Pandoc - Grid Tables](http://pandoc.org/README.html#extension-grid_tables))
|
||||
- [**Extra emphasis**](src/Markdig.Tests/Specs/EmphasisExtraSpecs.md) (inspired from [Pandoc - Emphasis](http://pandoc.org/README.html#strikeout) and [Markdown-it](https://markdown-it.github.io/))
|
||||
- strike through `~~`,
|
||||
- Subscript `~`
|
||||
- Superscript `^`
|
||||
- Inserted `++`
|
||||
- Marked `==`
|
||||
- **Special attributes** or attached HTML attributes (inspired from [PHP Markdown Extra - Special Attributes](https://michelf.ca/projects/php-markdown/extra/#spe-attr))
|
||||
- **Definition lists** (inspired from [PHP Markdown Extra - Definitions Lists](https://michelf.ca/projects/php-markdown/extra/#def-list))
|
||||
- **Footnotes** (inspired from [PHP Markdown Extra - Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes))
|
||||
- **Auto-identifiers** for headings (similar to [Pandoc - Auto Identifiers](http://pandoc.org/README.html#extension-auto_identifiers))
|
||||
- **Task Lists** inspired from [Github Task lists](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments).
|
||||
- **Extra bullet lists**, supporting alpha bullet `a.` `b.` and roman bullet (`i`, `ii`...etc.)
|
||||
- **Media support** for media url (youtube, vimeo, mp4...etc.) (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/embedded-audio-and-video/441))
|
||||
- **Abbreviations** (inspired from [PHP Markdown Extra - Abbreviations](https://michelf.ca/projects/php-markdown/extra/#abbr))
|
||||
- **Citation** text by enclosing `""...""` (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/referencing-creative-works-with-cite/892))
|
||||
- **Custom containers** similar to fenced code block `:::` for generating a proper `<div>...</div>` instead (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/custom-container-for-block-and-inline/2051))
|
||||
- **Figures** (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/image-tag-should-expand-to-figure-when-used-with-title/265/5))
|
||||
- **Footers** (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/syntax-for-footer/2070))
|
||||
- **Mathematics**/Latex extension by enclosing `$$` for block and `$` for inline math (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/mathematics-extension/457/31))
|
||||
- **Soft lines as hard lines**
|
||||
- **Emoji** support (inspired from [Markdown-it](https://markdown-it.github.io/))
|
||||
- **SmartyPants** (inspired from [Daring Fireball - SmartyPants](https://daringfireball.net/projects/smartypants/))
|
||||
- **Bootstrap** class (to output bootstrap class)
|
||||
- **Diagrams** extension whenever a fenced code block contains a special keyword, it will be converted to a div block with the content as-is (currently, supports only for [`mermaid` diagrams](https://knsv.github.io/mermaid/))
|
||||
- **YAML frontmatter** to parse without evaluating the frontmatter and to discard it from the HTML output (typically used for previewing without the frontmatter in MarkdownEditor)
|
||||
- [**Special attributes**](src/Markdig.Tests/Specs/GenericAttributesSpecs.md) or attached HTML attributes (inspired from [PHP Markdown Extra - Special Attributes](https://michelf.ca/projects/php-markdown/extra/#spe-attr))
|
||||
- [**Definition lists**](src/Markdig.Tests/Specs/DefinitionListSpecs.md) (inspired from [PHP Markdown Extra - Definitions Lists](https://michelf.ca/projects/php-markdown/extra/#def-list))
|
||||
- [**Footnotes**](src/Markdig.Tests/Specs/FootnotesSpecs.md) (inspired from [PHP Markdown Extra - Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes))
|
||||
- [**Auto-identifiers**](src/Markdig.Tests/Specs/AutoIdentifierSpecs.md) for headings (similar to [Pandoc - Auto Identifiers](http://pandoc.org/README.html#extension-auto_identifiers))
|
||||
- [**Auto-links**](src/Markdig.Tests/Specs/AutoLinks.md) generates links if a text starts with `http://` or `https://` or `ftp://` or `mailto:` or `www.xxx.yyy`
|
||||
- [**Task Lists**](src/Markdig.Tests/Specs/TaskListSpecs.md) inspired from [Github Task lists](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments).
|
||||
- [**Extra bullet lists**](src/Markdig.Tests/Specs/ListExtraSpecs.md), supporting alpha bullet `a.` `b.` and roman bullet (`i`, `ii`...etc.)
|
||||
- [**Media support**](src/Markdig.Tests/Specs/MediaSpecs.md) for media url (youtube, vimeo, mp4...etc.) (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/embedded-audio-and-video/441))
|
||||
- [**Abbreviations**](src/Markdig.Tests/Specs/AbbreviationSpecs.md) (inspired from [PHP Markdown Extra - Abbreviations](https://michelf.ca/projects/php-markdown/extra/#abbr))
|
||||
- [**Citation**](src/Markdig.Tests/Specs/FigureFooterAndCiteSpecs.md) text by enclosing `""...""` (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/referencing-creative-works-with-cite/892))
|
||||
- [**Custom containers**](src/Markdig.Tests/Specs/CustomContainerSpecs.md) similar to fenced code block `:::` for generating a proper `<div>...</div>` instead (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/custom-container-for-block-and-inline/2051))
|
||||
- [**Figures**](src/Markdig.Tests/Specs/FigureFooterAndCiteSpecs.md) (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/image-tag-should-expand-to-figure-when-used-with-title/265/5))
|
||||
- [**Footers**](src/Markdig.Tests/Specs/FigureFooterAndCiteSpecs.md) (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/syntax-for-footer/2070))
|
||||
- [**Mathematics**](src/Markdig.Tests/Specs/MathSpecs.md)/Latex extension by enclosing `$$` for block and `$` for inline math (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/mathematics-extension/457/31))
|
||||
- [**Soft lines as hard lines**](src/Markdig.Tests/Specs/HardlineBreakSpecs.md)
|
||||
- [**Emoji**](src/Markdig.Tests/Specs/EmojiSpecs.md) support (inspired from [Markdown-it](https://markdown-it.github.io/))
|
||||
- [**SmartyPants**](src/Markdig.Tests/Specs/SmartyPantsSpecs.md) (inspired from [Daring Fireball - SmartyPants](https://daringfireball.net/projects/smartypants/))
|
||||
- [**Bootstrap**](src/Markdig.Tests/Specs/BootstrapSpecs.md) class (to output bootstrap class)
|
||||
- [**Diagrams**](src/Markdig.Tests/Specs/DiagramsSpecs.md) extension whenever a fenced code block contains a special keyword, it will be converted to a div block with the content as-is (currently, supports [`mermaid`](https://knsv.github.io/mermaid/) and [`nomnoml`](https://github.com/skanaar/nomnoml) diagrams)
|
||||
- [**YAML frontmatter**](src/Markdig.Tests/Specs/YamlSpecs.md) to parse without evaluating the frontmatter and to discard it from the HTML output (typically used for previewing without the frontmatter in MarkdownEditor)
|
||||
- [**JIRA links**](src/Markdig.Tests/Specs/JiraLinks.md) to automatically generate links for JIRA project references (Thanks to @clarkd: https://github.com/clarkd/MarkdigJiraLinker)
|
||||
- Compatible with .NET 3.5, 4.0+ and .NET Core (`netstandard1.1+`)
|
||||
|
||||
## Documentation
|
||||
|
||||
> The repository is under construction. There will be a dedicated website and proper documentation at some point!
|
||||
|
||||
In the meantime, you can have a "behind the scene" article about Markdig in my blog post ["Implementing a Markdown Engine for .NET"](http://xoofx.com/blog/2016/06/13/implementing-a-markdown-processor-for-dotnet/)
|
||||
While there is not yet a dedicated documentation, you can find from the [specs documentation](src/Markdig.Tests/Specs/readme.md) how to use these extensions.
|
||||
|
||||
In the meantime, you can have a "behind the scene" article about Markdig in my blog post ["Implementing a Markdown Engine for .NET"](http://xoofx.com/blog/2016/06/13/implementing-a-markdown-processor-for-dotnet/)
|
||||
|
||||
## Download
|
||||
|
||||
Markdig is available as a NuGet package: [](https://www.nuget.org/packages/Markdig/)
|
||||
|
||||
Also [Markdig.Signed](https://www.nuget.org/packages/Markdig.Signed/) NuGet package provides signed assemblies.
|
||||
|
||||
## Usage
|
||||
|
||||
The main entry point for the API is the `Markdig.Markdown` class:
|
||||
@@ -72,7 +77,7 @@ var result = Markdown.ToHtml("This is a text with some *emphasis*");
|
||||
Console.WriteLine(result); // prints: <p>This is a text with some <em>emphasis</em></p>
|
||||
```
|
||||
|
||||
In order to activate most of all advanced extensions (except Emoji, SoftLine as HarLine and SmartyPants)
|
||||
In order to activate most of all advanced extensions (except Emoji, SoftLine as HardLine, JiraLinks and SmartyPants)
|
||||
|
||||
```csharp
|
||||
// Configure the pipeline with all advanced extensions active
|
||||
@@ -175,6 +180,12 @@ WarmupCount=2 TargetCount=10
|
||||
TestMarkdownDeep | 7.4076 ms | 0.0617 ms | 318.00 | 186.00 | 84.00 | 2,576,728.69 |
|
||||
```
|
||||
|
||||
## Donate
|
||||
|
||||
If you are using this library and find it useful for your project, please consider a donation for it!
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRGHXBTP442JL)
|
||||
|
||||
## Credits
|
||||
|
||||
Thanks to the fantastic work done by [John Mac Farlane](http://johnmacfarlane.net/) for the CommonMark specs and all the people involved in making Markdown a better standard!
|
||||
@@ -185,6 +196,7 @@ Thanks also to the project [BenchmarkDotNet](https://github.com/PerfDotNet/Bench
|
||||
|
||||
Some decoding part (e.g HTML [EntityHelper.cs](https://github.com/lunet-io/markdig/blob/master/src/Markdig/Helpers/EntityHelper.cs)) have been re-used from [CommonMark.NET](https://github.com/Knagis/CommonMark.NET)
|
||||
|
||||
Thanks to the work done by @clarkd on the JIRA Link extension (https://github.com/clarkd/MarkdigJiraLinker), now included with this project!
|
||||
## Author
|
||||
|
||||
Alexandre MUTEL aka [xoofx](http://xoofx.com)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Markdig.Benchmarks</RootNamespace>
|
||||
<AssemblyName>Markdig.Benchmarks</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<TargetFrameworkProfile />
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition="'$(NuGetPackageRoot)' == ''">
|
||||
<NuGetPackageRoot>$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
</PropertyGroup>
|
||||
<ImportGroup>
|
||||
<Import Project="$(NuGetPackageRoot)\Microsoft.Diagnostics.Tracing.TraceEvent\1.0.41\build\Microsoft.Diagnostics.Tracing.TraceEvent.targets" Condition="Exists('$(NuGetPackageRoot)\Microsoft.Diagnostics.Tracing.TraceEvent\1.0.41\build\Microsoft.Diagnostics.Tracing.TraceEvent.targets')" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -131,7 +131,7 @@ namespace Testamina.Markdig.Benchmarks
|
||||
var config = ManualConfig.Create(DefaultConfig.Instance);
|
||||
//var gcDiagnoser = new MemoryDiagnoser();
|
||||
//config.Add(new Job { Mode = Mode.SingleRun, LaunchCount = 2, WarmupCount = 2, IterationTime = 1024, TargetCount = 10 });
|
||||
config.Add(new Job { Mode = Mode.Throughput, LaunchCount = 2, WarmupCount = 2, TargetCount = 10 });
|
||||
//config.Add(new Job { Mode = Mode.Throughput, LaunchCount = 2, WarmupCount = 2, TargetCount = 10 });
|
||||
//config.Add(gcDiagnoser);
|
||||
|
||||
//var config = DefaultConfig.Instance;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1"/></startup></configuration>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup></configuration>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"win-x64": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net451": {
|
||||
"net46": {
|
||||
"compilationOptions": {
|
||||
"define": [
|
||||
"CLASSIC"
|
||||
@@ -19,9 +19,9 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"BenchmarkDotNet": "0.9.6",
|
||||
"BenchmarkDotNet.Diagnostics.Windows": "0.9.6",
|
||||
"CommonMark.NET": "0.11.0",
|
||||
"BenchmarkDotNet": "0.10.6",
|
||||
"BenchmarkDotNet.Diagnostics.Windows": "0.10.6",
|
||||
"CommonMark.NET": "0.15.1",
|
||||
"MarkdownSharp": "1.13.0.0",
|
||||
"Microsoft.Diagnostics.Runtime": "0.8.31-beta",
|
||||
"Microsoft.Diagnostics.Tracing.TraceEvent": "1.0.41.0"
|
||||
|
||||
@@ -39,10 +39,6 @@
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Markdig">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\Markdig\Bin\$(Configuration)\net40\Markdig.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
@@ -65,6 +61,9 @@
|
||||
<Compile Include="TestHtmlHelper.cs" />
|
||||
<Compile Include="TestLineReader.cs" />
|
||||
<Compile Include="TestLinkHelper.cs" />
|
||||
<Compile Include="TestNormalize.cs" />
|
||||
<Compile Include="TestOrderedList.cs" />
|
||||
<Compile Include="TestPlainText.cs" />
|
||||
<Compile Include="TestPragmaLines.cs" />
|
||||
<Compile Include="TestSourcePosition.cs" />
|
||||
<Compile Include="TestStringSliceList.cs" />
|
||||
@@ -75,6 +74,8 @@
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="project.json" />
|
||||
<None Include="Specs\JiraLinks.md" />
|
||||
<None Include="Specs\AutoLinks.md" />
|
||||
<None Include="Specs\AutoIdentifierSpecs.md" />
|
||||
<None Include="Specs\AbbreviationSpecs.md" />
|
||||
<None Include="Specs\FigureFooterAndCiteSpecs.md" />
|
||||
@@ -89,6 +90,7 @@
|
||||
<None Include="Specs\BootstrapSpecs.md" />
|
||||
<None Include="Specs\DiagramsSpecs.md" />
|
||||
<None Include="Specs\NoHtmlSpecs.md" />
|
||||
<None Include="Specs\readme.md" />
|
||||
<None Include="Specs\YamlSpecs.md" />
|
||||
<None Include="Specs\TaskListSpecs.md" />
|
||||
<None Include="Specs\SmartyPantsSpecs.md" />
|
||||
@@ -107,6 +109,12 @@
|
||||
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Markdig\Markdig.csproj">
|
||||
<Project>{8a58a7e2-627c-4f41-933f-5ac92adfab48}</Project>
|
||||
<Name>Markdig</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
||||
@@ -67,3 +67,34 @@ We should not abbreviate 1.1A or 11A!
|
||||
.
|
||||
<p>We should not abbreviate 1.1A or 11A!</p>
|
||||
````````````````````````````````
|
||||
|
||||
Abbreviations should match whole word only, even if the word is the entire content:
|
||||
|
||||
```````````````````````````````` example
|
||||
*[1A]: First
|
||||
|
||||
1.1A
|
||||
.
|
||||
<p>1.1A</p>
|
||||
````````````````````````````````
|
||||
|
||||
Abbreviations should match whole word only, even if there is another glossary term:
|
||||
|
||||
```````````````````````````````` example
|
||||
*[SCO]: First
|
||||
*[SCOM]: Second
|
||||
|
||||
SCOM
|
||||
.
|
||||
<p><abbr title="Second">SCOM</abbr></p>
|
||||
````````````````````````````````
|
||||
|
||||
Abbreviations should only match when surrounded by whitespace:
|
||||
|
||||
```````````````````````````````` example
|
||||
*[PR]: Pull Request
|
||||
|
||||
PRAA
|
||||
.
|
||||
<p>PRAA</p>
|
||||
````````````````````````````````
|
||||
78
src/Markdig.Tests/Specs/AutoLinks.md
Normal file
78
src/Markdig.Tests/Specs/AutoLinks.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Extensions
|
||||
|
||||
This section describes the different extensions supported:
|
||||
|
||||
## AutoLinks
|
||||
|
||||
Autolinks will format as a HTML link any string that starts by:
|
||||
|
||||
- `http://` or `https://`
|
||||
- `ftp://`
|
||||
- `mailto:`
|
||||
- `www.`
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a http://www.google.com URL and https://www.google.com
|
||||
This is a ftp://test.com
|
||||
And a mailto:email@toto.com
|
||||
And a plain www.google.com
|
||||
.
|
||||
<p>This is a <a href="http://www.google.com">http://www.google.com</a> URL and <a href="https://www.google.com">https://www.google.com</a>
|
||||
This is a <a href="ftp://test.com">ftp://test.com</a>
|
||||
And a <a href="mailto:email@toto.com">mailto:email@toto.com</a>
|
||||
And a plain <a href="http://www.google.com">www.google.com</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
But incomplete links will not be matched:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not a http:/www.google.com URL and https:/www.google.com
|
||||
This is not a ftp:/test.com
|
||||
And not a mailto:emailtoto.com
|
||||
And not a plain www. or a www.x
|
||||
.
|
||||
<p>This is not a http:/www.google.com URL and https:/www.google.com
|
||||
This is not a ftp:/test.com
|
||||
And not a mailto:emailtoto.com
|
||||
And not a plain www. or a www.x</p>
|
||||
````````````````````````````````
|
||||
|
||||
Previous character must be a punctuation or a valid space (tab, space, new line):
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not a nhttp://www.google.com URL but this is (https://www.google.com)
|
||||
.
|
||||
<p>This is not a nhttp://www.google.com URL but this is (<a href="https://www.google.com">https://www.google.com</a>)</p>
|
||||
````````````````````````````````
|
||||
|
||||
An autolink should not interfere with an `<a>` HTML inline:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is an HTML <a href="http://www.google.com">http://www.google.com</a> link
|
||||
.
|
||||
<p>This is an HTML <a href="http://www.google.com">http://www.google.com</a> link</p>
|
||||
````````````````````````````````
|
||||
or even within emphasis:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is an HTML <a href="http://www.google.com"> **http://www.google.com** </a> link
|
||||
.
|
||||
<p>This is an HTML <a href="http://www.google.com"> <strong>http://www.google.com</strong> </a> link</p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
An autolink should not interfere with a markdown link:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is an HTML [http://www.google.com](http://www.google.com) link
|
||||
.
|
||||
<p>This is an HTML <a href="http://www.google.com">http://www.google.com</a> link</p>
|
||||
````````````````````````````````
|
||||
|
||||
A link embraced by pending emphasis should let the emphasis takes precedence if characters are placed at the end of the matched link:
|
||||
|
||||
```````````````````````````````` example
|
||||
Check **http://www.a.com** or __http://www.b.com__
|
||||
.
|
||||
<p>Check <strong><a href="http://www.a.com">http://www.a.com</a></strong> or <strong><a href="http://www.b.com">http://www.b.com</a></strong></p>
|
||||
````````````````````````````````
|
||||
@@ -23,4 +23,35 @@ graph TD;
|
||||
</div>
|
||||
````````````````````````````````
|
||||
|
||||
## nomnoml diagrams
|
||||
|
||||
Using a fenced code block with the `nomnoml` language info will output a `<div class='nomnoml'>` instead of a `pre/code` block:
|
||||
|
||||
```````````````````````````````` example
|
||||
```nomnoml
|
||||
[example|
|
||||
propertyA: Int
|
||||
propertyB: string
|
||||
|
|
||||
methodA()
|
||||
methodB()
|
||||
|
|
||||
[subA]--[subB]
|
||||
[subA]-:>[sub C]
|
||||
]
|
||||
```
|
||||
.
|
||||
<div class="nomnoml">[example|
|
||||
propertyA: Int
|
||||
propertyB: string
|
||||
|
|
||||
methodA()
|
||||
methodB()
|
||||
|
|
||||
[subA]--[subB]
|
||||
[subA]-:>[sub C]
|
||||
]
|
||||
</div>
|
||||
````````````````````````````````
|
||||
|
||||
TODO: Add other text diagram languages
|
||||
@@ -12,10 +12,28 @@ This is a test with a :) and a :angry: smiley
|
||||
<p>This is a test with a 😃 and a 😠 smiley</p>
|
||||
````````````````````````````````
|
||||
|
||||
An emoji needs to be preceded by a space and followed by a space:
|
||||
An emoji needs to be preceded by a space:
|
||||
|
||||
```````````````````````````````` example
|
||||
These are not:) an :)emoji with a:) x:angry:x
|
||||
These are not:) an emoji with a:) x:angry:x
|
||||
.
|
||||
<p>These are not:) an :)emoji with a:) x:angry:x</p>
|
||||
<p>These are not:) an emoji with a:) x:angry:x</p>
|
||||
````````````````````````````````
|
||||
|
||||
Emoji can be followed by close ponctuation (or any other characters):
|
||||
|
||||
```````````````````````````````` example
|
||||
We all need :), it makes us :muscle:. (and :ok_hand:).
|
||||
.
|
||||
<p>We all need 😃, it makes us 💪. (and 👌).</p>
|
||||
````````````````````````````````
|
||||
|
||||
Sentences can end with Emoji:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a sentence :ok_hand:
|
||||
and keeps going to the next line :)
|
||||
.
|
||||
<p>This is a sentence 👌
|
||||
and keeps going to the next line 😃</p>
|
||||
````````````````````````````````
|
||||
|
||||
@@ -52,3 +52,16 @@ Marked text can be used to specify that a text has been marked in a document. T
|
||||
.
|
||||
<p><mark>Marked text</mark></p>
|
||||
````````````````````````````````
|
||||
## Emphasis on Html Entities
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
This is text MyBrand ^®^ and MyTrademark ^™^
|
||||
This is text MyBrand^®^ and MyTrademark^™^
|
||||
This is text MyBrand~®~ and MyCopyright^©^
|
||||
.
|
||||
<p>This is text MyBrand <sup>®</sup> and MyTrademark <sup>TM</sup>
|
||||
This is text MyBrand<sup>®</sup> and MyTrademark<sup>TM</sup>
|
||||
This is text MyBrand<sub>®</sub> and MyCopyright<sup>©</sup></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -27,13 +27,13 @@ The following shows that attributes is attached to the current block or the prev
|
||||
This is a heading{#heading-link2}
|
||||
-----------------
|
||||
|
||||
This is a paragraph with an attached attributes {#myparagraph attached-bool-property}
|
||||
This is a paragraph with an attached attributes {#myparagraph attached-bool-property attached-bool-property2}
|
||||
.
|
||||
<h1 id="heading-link">This is a heading with an an attribute</h1>
|
||||
<h1 id="heading-link2">This is a heading</h1>
|
||||
<p><a href="http://google.com" id="a-link" class="myclass" data-lang="fr" data-value="This is a value">This is a link</a></p>
|
||||
<h2 id="heading-link2">This is a heading</h2>
|
||||
<p id="myparagraph" attached-bool-property>This is a paragraph with an attached attributes </p>
|
||||
<p id="myparagraph" attached-bool-property attached-bool-property2>This is a paragraph with an attached attributes </p>
|
||||
````````````````````````````````
|
||||
|
||||
The following shows that attributes can be attached to the next block if they are used inside a single line just preceding the block (and preceded by a blank line or beginning of a block container):
|
||||
|
||||
@@ -272,4 +272,15 @@ A grid table may not have irregularly shaped cells:
|
||||
+---+---+---+
|
||||
| DDDDD | E |
|
||||
+---+---+---+</p>
|
||||
````````````````````````````````
|
||||
````````````````````````````````
|
||||
|
||||
An empty `+` on a line should result in a simple empty list output:
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
+
|
||||
.
|
||||
<ul>
|
||||
<li></li>
|
||||
</ul>
|
||||
````````````````````````````````
|
||||
|
||||
77
src/Markdig.Tests/Specs/JiraLinks.md
Normal file
77
src/Markdig.Tests/Specs/JiraLinks.md
Normal file
@@ -0,0 +1,77 @@
|
||||
## Jira Links
|
||||
|
||||
The JiraLinks extension will automatically add links to JIRA issue items within your markdown, e.g. XX-1234. For this to happen, you must configure the extension when adding to the pipeline, e.g.
|
||||
|
||||
```
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseJiraLinks(new JiraLinkOptions("http://your.company.abc"))
|
||||
.Build();
|
||||
```
|
||||
|
||||
The rules for detecting a link are:
|
||||
|
||||
- The project key must be composed of onre or more capitalised ASCII letter `[A-Z]+`
|
||||
- A single hypen `-` must separate the project key and issue number.
|
||||
- The issue number is composed of 1 or more digits `[0, 9]+`
|
||||
- The reference must be preceeded by either `(` or whitespace or EOF.
|
||||
- The reference must be followed by either `)` or whitespace or EOF.
|
||||
|
||||
The following are valid examples:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a ABCD-123 issue
|
||||
.
|
||||
<p>This is a <a href="http://your.company.abc/browse/ABCD-123" target="blank">ABCD-123</a> issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a KIRA-1 issue
|
||||
.
|
||||
<p>This is a <a href="http://your.company.abc/browse/KIRA-1" target="blank">KIRA-1</a> issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a Z-1 issue
|
||||
.
|
||||
<p>This is a <a href="http://your.company.abc/browse/Z-1" target="blank">Z-1</a> issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
These are also valid links with `(` and `)`:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a (ABCD-123) issue
|
||||
.
|
||||
<p>This is a (<a href="http://your.company.abc/browse/ABCD-123" target="blank">ABCD-123</a>) issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a (KIRA-1) issue
|
||||
.
|
||||
<p>This is a (<a href="http://your.company.abc/browse/KIRA-1" target="blank">KIRA-1</a>) issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a (Z-1) issue
|
||||
.
|
||||
<p>This is a (<a href="http://your.company.abc/browse/Z-1" target="blank">Z-1</a>) issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
These are not valid links:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not aJIRA-123 issue
|
||||
.
|
||||
<p>This is not aJIRA-123 issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not JIRA-123a issue
|
||||
.
|
||||
<p>This is not JIRA-123a issue</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not JIRA- issue
|
||||
.
|
||||
<p>This is not JIRA- issue</p>
|
||||
````````````````````````````````
|
||||
@@ -20,12 +20,47 @@ This is a $$math block$$
|
||||
<p>This is a <span class="math">math block</span></p>
|
||||
````````````````````````````````
|
||||
|
||||
The opening `$` and closing `$` is following the rules of the emphasis delimiter `_`:
|
||||
Newlines inside an inline math are not allowed:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not a $ math block $
|
||||
This is not a $$math
|
||||
block$$ and? this is a $$math block$$
|
||||
.
|
||||
<p>This is not a $ math block $</p>
|
||||
<p>This is not a $$math
|
||||
block$$ and? this is a <span class="math">math block</span></p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not a $math
|
||||
block$ and? this is a $math block$
|
||||
.
|
||||
<p>This is not a $math
|
||||
block$ and? this is a <span class="math">math block</span></p>
|
||||
````````````````````````````````
|
||||
An opening `$` can be followed by a space if the closing is also preceded by a space `$`:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a $ math block $
|
||||
.
|
||||
<p>This is a <span class="math">math block</span></p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a $ math block $ after
|
||||
.
|
||||
<p>This is a <span class="math">math block</span> after</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a $$ math block $$ after
|
||||
.
|
||||
<p>This is a <span class="math">math block</span> after</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a not $ math block$ because there is not a whitespace before the closing
|
||||
.
|
||||
<p>This is a not $ math block$ because there is not a whitespace before the closing</p>
|
||||
````````````````````````````````
|
||||
|
||||
For the opening `$` it requires a space or a punctuation before (but cannot be used within a word):
|
||||
@@ -82,6 +117,13 @@ This is *a $math* block$
|
||||
.
|
||||
<p>This is *a <span class="math">math* block</span></p>
|
||||
````````````````````````````````
|
||||
An opening $$ at the beginning of a line should not be interpreted as a Math block:
|
||||
|
||||
```````````````````````````````` example
|
||||
$$ math $$ starting at a line
|
||||
.
|
||||
<p><span class="math">math</span> starting at a line</p>
|
||||
````````````````````````````````
|
||||
|
||||
## Math Block
|
||||
|
||||
|
||||
@@ -10,7 +10,10 @@ Allows to embed audio/video links to popular website:
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
.
|
||||
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" width="500" height="281" frameborder="0" allowfullscreen></iframe></p>
|
||||
<p><iframe src="https://player.vimeo.com/video/8607834" width="500" height="281" frameborder="0" allowfullscreen></iframe></p>
|
||||
<p><video width="500" height="281" controls><source type="video/mp4" src="https://sample.com/video.mp4"></source></video></p>
|
||||
````````````````````````````````
|
||||
@@ -136,6 +136,7 @@ a | b
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -147,9 +148,12 @@ a | b
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>4</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -346,7 +350,8 @@ The first row is considered as a **header row** if it is separated from the regu
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
The text alignment is defined by default to be left.
|
||||
The text alignment is defined by default to be center for header and left for cells. If the left alignment is applied, it will force the column heading to be left aligned.
|
||||
There is no way to define a different alignment for heading and cells (apart from the default).
|
||||
The text alignment can be changed by using the character `:` with the header column separator:
|
||||
|
||||
```````````````````````````````` example
|
||||
@@ -358,19 +363,19 @@ The text alignment can be changed by using the character `:` with the header col
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th style="text-align: left;">a</th>
|
||||
<th style="text-align: center;">b</th>
|
||||
<th style="text-align: right;">c</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td style="text-align: left;">0</td>
|
||||
<td style="text-align: center;">1</td>
|
||||
<td style="text-align: right;">2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td style="text-align: left;">3</td>
|
||||
<td style="text-align: center;">4</td>
|
||||
<td style="text-align: right;">5</td>
|
||||
</tr>
|
||||
@@ -508,13 +513,46 @@ a | b
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
** Rule #9**
|
||||
|
||||
It is possible to have a single row header only:
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
-- | --
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
|a|b|c
|
||||
|---|---|---|
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
<th>c</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
** Tests **
|
||||
|
||||
Tests trailing spaces after pipes
|
||||
|
||||
```````````````````````````````` example
|
||||
| abc | def |
|
||||
|---|:---|
|
||||
|---|---|
|
||||
| cde| ddd|
|
||||
| eee| fff|
|
||||
| fff | fffff |
|
||||
@@ -547,3 +585,31 @@ Tests trailing spaces after pipes
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
** Normalized columns count **
|
||||
|
||||
The tables are normalized to the maximum number of columns found in a table
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
-- | -
|
||||
0 | 1 | 2
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
<#/*
|
||||
<#/*
|
||||
Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
This file is licensed under the BSD-Clause 2 license.
|
||||
See the license.txt file in the project root for more information.The MIT License (MIT)
|
||||
@@ -36,10 +36,10 @@ SOFTWARE.
|
||||
<#@ import namespace="System.Collections.Generic" #>
|
||||
<#@ import namespace="System.CodeDom" #>
|
||||
<#@ import namespace="System.CodeDom.Compiler" #>
|
||||
<#@ output extension=".cs" #><#
|
||||
<#@ output extension=".cs" encoding="utf-8"#>
|
||||
<#
|
||||
var specFiles = new KeyValuePair<string, string>[] {
|
||||
// new KeyValuePair<string, string>("https://raw.githubusercontent.com/jgm/CommonMark/master/spec.txt", string.Empty),
|
||||
new KeyValuePair<string, string>("https://raw.githubusercontent.com/jgm/CommonMark/cfc84164475d3bec8be9482c21a705adc93a54f5/spec.txt", string.Empty), // 0.26 specs
|
||||
new KeyValuePair<string, string>("https://raw.githubusercontent.com/jgm/CommonMark/4ec06917c3a3632be4a935ffa0973092bd2621be/spec.txt", string.Empty), // 0.28
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("PipeTableSpecs.md"), "pipetables|advanced"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("FootnotesSpecs.md"), "footnotes|advanced"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("GenericAttributesSpecs.md"), "attributes|advanced"),
|
||||
@@ -61,6 +61,8 @@ SOFTWARE.
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("DiagramsSpecs.md"), "diagrams|advanced"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("NoHtmlSpecs.md"), "nohtml"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("YamlSpecs.md"), "yaml"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("AutoLinks.md"), "autolinks|advanced"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("JiraLinks.md"), "jiralinks"),
|
||||
};
|
||||
var emptyLines = false;
|
||||
var displayEmptyLines = false;
|
||||
|
||||
@@ -13,13 +13,13 @@ namespace Markdig.Tests
|
||||
[Test]
|
||||
public void StrongNormal()
|
||||
{
|
||||
TestParser.TestSpec("***Strong emphasis*** normal", "<p><strong><em>Strong emphasis</em></strong> normal</p>", "");
|
||||
TestParser.TestSpec("***Strong emphasis*** normal", "<p><em><strong>Strong emphasis</strong></em> normal</p>", "");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NormalStrongNormal()
|
||||
{
|
||||
TestParser.TestSpec("normal ***Strong emphasis*** normal", "<p>normal <strong><em>Strong emphasis</em></strong> normal</p>", "");
|
||||
TestParser.TestSpec("normal ***Strong emphasis*** normal", "<p>normal <em><strong>Strong emphasis</strong></em> normal</p>", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,3 +42,62 @@ This is a text
|
||||
<p>This is a text</p>
|
||||
````````````````````````````````
|
||||
|
||||
It can end with three dots `...`:
|
||||
|
||||
```````````````````````````````` example
|
||||
---
|
||||
this: is a frontmatter
|
||||
...
|
||||
This is a text
|
||||
.
|
||||
<p>This is a text</p>
|
||||
````````````````````````````````
|
||||
|
||||
It expects exactly three dots `...`:
|
||||
|
||||
```````````````````````````````` example
|
||||
---
|
||||
this: is a frontmatter
|
||||
....
|
||||
This is a text
|
||||
.
|
||||
````````````````````````````````
|
||||
|
||||
Front matter ends with the first line containing three dots `...` or three dashes `...`:
|
||||
|
||||
```````````````````````````````` example
|
||||
---
|
||||
this: is a frontmatter
|
||||
....
|
||||
|
||||
Hello
|
||||
---
|
||||
This is a text
|
||||
.
|
||||
<p>This is a text</p>
|
||||
````````````````````````````````
|
||||
|
||||
It expects whitespace can exist after the leading characters
|
||||
|
||||
```````````````````````````````` example
|
||||
---
|
||||
this: is a frontmatter
|
||||
...
|
||||
This is a text
|
||||
.
|
||||
<p>This is a text</p>
|
||||
````````````````````````````````
|
||||
|
||||
It expects whitespace can exist after the trailing characters
|
||||
|
||||
```````````````````````````````` example
|
||||
---
|
||||
this: is a frontmatter
|
||||
...
|
||||
This is a text
|
||||
.
|
||||
<p>This is a text</p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
|
||||
|
||||
36
src/Markdig.Tests/Specs/readme.md
Normal file
36
src/Markdig.Tests/Specs/readme.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Documentation Tests/Specs
|
||||
|
||||
You will find from the following links the supported extensions in markdig and their usage:
|
||||
|
||||
- 2 kind of tables:
|
||||
- [**Pipe tables**](PipeTableSpecs.md)
|
||||
- [**Grid tables**](GridTableSpecs.md)
|
||||
- [**Extra emphasis**](EmphasisExtraSpecs.md)
|
||||
- strike through `~~`,
|
||||
- Subscript `~`
|
||||
- Superscript `^`
|
||||
- Inserted `++`
|
||||
- Marked `==`
|
||||
- [**Special attributes**](GenericAttributesSpecs.md)
|
||||
- [**Definition lists**](DefinitionListSpecs.md)
|
||||
- [**Footnotes**](FootnotesSpecs.md)
|
||||
- [**Auto-identifiers**](AutoIdentifierSpecs.md)
|
||||
- [**Auto-links**](AutoLinks.md)
|
||||
- [**Task Lists**](TaskListSpecs.md)
|
||||
- [**Extra bullet lists**](ListExtraSpecs.md)
|
||||
- [**Media support**](MediaSpecs.md)
|
||||
- [**Abbreviations**](AbbreviationSpecs.md)
|
||||
- [**Citation**](FigureFooterAndCiteSpecs.md)
|
||||
- [**Custom containers**](CustomContainerSpecs.md)
|
||||
- [**Figures**](FigureFooterAndCiteSpecs.md)
|
||||
- [**Footers**](FigureFooterAndCiteSpecs.md)
|
||||
- [**Mathematics**](MathSpecs.md)/Latex extension by enclosing `$$` for block and `$` for inline math (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/mathematics-extension/457/31))
|
||||
- [**Soft lines as hard lines**](HardlineBreakSpecs.md)
|
||||
- [**Emoji**](EmojiSpecs.md)
|
||||
- [**SmartyPants**](SmartyPantsSpecs.md)
|
||||
- [**Bootstrap**](BootstrapSpecs.md)
|
||||
- [**Diagrams**](DiagramsSpecs.md)
|
||||
- [**YAML frontmatter**](YamlSpecs.md)
|
||||
- [**JIRA links**](JiraLinks.md)
|
||||
|
||||
> Notice that the links above are not yet the final documentation but are "specification" files used for testing the correctness of markdig for each extension
|
||||
@@ -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.Security;
|
||||
using NUnit.Framework;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
@@ -30,6 +32,18 @@ namespace Markdig.Tests
|
||||
Assert.AreEqual(')', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("http://google.com.")]
|
||||
[TestCase("http://google.com. ")]
|
||||
public void TestUrlTrailingFullStop(string uri)
|
||||
{
|
||||
var text = new StringSlice(uri);
|
||||
string link;
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out link));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual('.', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUrlNestedParenthesis()
|
||||
{
|
||||
@@ -390,6 +404,7 @@ namespace Markdig.Tests
|
||||
[TestCase("b>r", "br")]
|
||||
[TestCase(@"b\r", "br")]
|
||||
[TestCase(@"b""r", "br")]
|
||||
[TestCase(@"Requirement 😀", "requirement")]
|
||||
public void TestUrilizeNonAscii_NonValidCharactersForFragments(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
|
||||
|
||||
452
src/Markdig.Tests/TestNormalize.cs
Normal file
452
src/Markdig.Tests/TestNormalize.cs
Normal file
@@ -0,0 +1,452 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using System.IO;
|
||||
using Markdig.Renderers.Normalize;
|
||||
using Markdig.Helpers;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestNormalize
|
||||
{
|
||||
[Test]
|
||||
public void SyntaxCodeBlock()
|
||||
{
|
||||
AssertSyntax("````csharp\npublic void HelloWorld()\n{\n}\n````", new FencedCodeBlock(null)
|
||||
{
|
||||
FencedChar = '`',
|
||||
FencedCharCount = 4,
|
||||
Info = "csharp",
|
||||
Lines = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("public void HelloWorld()"),
|
||||
new StringSlice("{"),
|
||||
new StringSlice("}"),
|
||||
}
|
||||
});
|
||||
|
||||
AssertSyntax(" public void HelloWorld()\n {\n }", new CodeBlock(null)
|
||||
{
|
||||
Lines = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("public void HelloWorld()"),
|
||||
new StringSlice("{"),
|
||||
new StringSlice("}"),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SyntaxHeadline()
|
||||
{
|
||||
AssertSyntax("## Headline", new HeadingBlock(null)
|
||||
{
|
||||
HeaderChar = '#',
|
||||
Level = 2,
|
||||
Inline = new ContainerInline().AppendChild(new LiteralInline("Headline")),
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SyntaxParagraph()
|
||||
{
|
||||
AssertSyntax("This is a normal paragraph", new ParagraphBlock()
|
||||
{
|
||||
Inline = new ContainerInline()
|
||||
.AppendChild(new LiteralInline("This is a normal paragraph")),
|
||||
});
|
||||
|
||||
AssertSyntax("This is a\nnormal\nparagraph", new ParagraphBlock()
|
||||
{
|
||||
Inline = new ContainerInline()
|
||||
.AppendChild(new LiteralInline("This is a"))
|
||||
.AppendChild(new LineBreakInline())
|
||||
.AppendChild(new LiteralInline("normal"))
|
||||
.AppendChild(new LineBreakInline())
|
||||
.AppendChild(new LiteralInline("paragraph")),
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CodeBlock()
|
||||
{
|
||||
AssertNormalizeNoTrim(" public void HelloWorld();\n {\n }");
|
||||
AssertNormalizeNoTrim(" public void HelloWorld();\n {\n }\n\ntext after two newlines");
|
||||
AssertNormalizeNoTrim("````\npublic void HelloWorld();\n{\n}\n````\n\ntext after two newlines");
|
||||
AssertNormalizeNoTrim("````csharp\npublic void HelloWorld();\n{\n}\n````");
|
||||
AssertNormalizeNoTrim("````csharp hideNewKeyword=true\npublic void HelloWorld();\n{\n}\n````");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Heading()
|
||||
{
|
||||
AssertNormalizeNoTrim("# Heading");
|
||||
AssertNormalizeNoTrim("## Heading");
|
||||
AssertNormalizeNoTrim("### Heading");
|
||||
AssertNormalizeNoTrim("#### Heading");
|
||||
AssertNormalizeNoTrim("##### Heading");
|
||||
AssertNormalizeNoTrim("###### Heading");
|
||||
AssertNormalizeNoTrim("###### Heading\n\ntext after two newlines");
|
||||
AssertNormalizeNoTrim("# Heading\nAnd Text1\n\nAndText2", options: new NormalizeOptions() { EmptyLineAfterHeading = false });
|
||||
|
||||
AssertNormalizeNoTrim("Heading\n=======\n\ntext after two newlines", "# Heading\n\ntext after two newlines");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Backslash()
|
||||
{
|
||||
AssertNormalizeNoTrim("This is a hardline \nAnd this is another hardline\\\nThis is standard newline");
|
||||
AssertNormalizeNoTrim("This is a line\nWith another line\nAnd a last line");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HtmlBlock()
|
||||
{
|
||||
/*AssertNormalizeNoTrim(@"<div id=""foo"" class=""bar
|
||||
baz"">
|
||||
</ div >");*/ // TODO: Bug: Throws Exception during emit
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Paragraph()
|
||||
{
|
||||
AssertNormalizeNoTrim("This is a plain paragraph");
|
||||
AssertNormalizeNoTrim(@"This
|
||||
is
|
||||
a
|
||||
plain
|
||||
paragraph");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParagraphMulti()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"line1
|
||||
|
||||
line2
|
||||
|
||||
line3");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ListUnordered()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"- a
|
||||
- b
|
||||
- c");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ListUnorderedLoose()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"- a
|
||||
|
||||
- b
|
||||
|
||||
- c");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ListOrderedLooseAndCodeBlock()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"1. ```
|
||||
foo
|
||||
```
|
||||
|
||||
bar");
|
||||
}
|
||||
|
||||
[Test, Ignore("Not sure this is the correct normalize for this one. Need to check the specs")]
|
||||
public void ListUnorderedLooseTop()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"* foo
|
||||
* bar
|
||||
|
||||
baz", options: new NormalizeOptions() { ListItemCharacter = '*' });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ListUnorderedLooseMultiParagraph()
|
||||
{
|
||||
AssertNormalizeNoTrim(
|
||||
@"- a
|
||||
|
||||
And another paragraph a
|
||||
|
||||
- b
|
||||
|
||||
And another paragraph b
|
||||
|
||||
- c");
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void ListOrdered()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"1. a
|
||||
2. b
|
||||
3. c");
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void ListOrderedAndIntended()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"1. a
|
||||
2. b
|
||||
- foo
|
||||
- bar
|
||||
a) 1234
|
||||
b) 1324
|
||||
3. c
|
||||
4. c
|
||||
5. c
|
||||
6. c
|
||||
7. c
|
||||
8. c
|
||||
9. c
|
||||
10. c
|
||||
- Foo
|
||||
- Bar
|
||||
11. c
|
||||
12. c");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HeaderAndParagraph()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"# heading
|
||||
|
||||
paragraph
|
||||
|
||||
paragraph2 without newlines");
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void QuoteBlock()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"> test1
|
||||
>
|
||||
> test2");
|
||||
|
||||
AssertNormalizeNoTrim(@"> test1
|
||||
This is a continuation
|
||||
> test2",
|
||||
@"> test1
|
||||
> This is a continuation
|
||||
> test2"
|
||||
);
|
||||
|
||||
AssertNormalizeNoTrim(@"> test1
|
||||
> -foobar
|
||||
|
||||
asdf
|
||||
|
||||
> test2
|
||||
> -foobar sen.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ThematicBreak()
|
||||
{
|
||||
AssertNormalizeNoTrim("***\n");
|
||||
|
||||
AssertNormalizeNoTrim("* * *\n", "***\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AutolinkInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("This has a <auto.link.com>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CodeInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("This has a `HelloWorld()` in it");
|
||||
AssertNormalizeNoTrim(@"This has a ``Hello`World()`` in it");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EmphasisInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("This is a plain **paragraph**");
|
||||
AssertNormalizeNoTrim("This is a plain *paragraph*");
|
||||
AssertNormalizeNoTrim("This is a plain _paragraph_");
|
||||
AssertNormalizeNoTrim("This is a plain __paragraph__");
|
||||
AssertNormalizeNoTrim("This is a pl*ai*n **paragraph**");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LineBreakInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("normal\nline break");
|
||||
AssertNormalizeNoTrim("hard \nline break");
|
||||
AssertNormalizeNoTrim("This is a hardline \nAnd this is another hardline\\\nThis is standard newline");
|
||||
AssertNormalizeNoTrim("This is a line\nWith another line\nAnd a last line");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LinkInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("This is a [link](http://company.com)");
|
||||
AssertNormalizeNoTrim("This is an ");
|
||||
|
||||
AssertNormalizeNoTrim(@"This is a [link](http://company.com ""Crazy Company"")");
|
||||
AssertNormalizeNoTrim(@"This is a [link](http://company.com ""Crazy \"" Company"")");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LinkReferenceDefinition()
|
||||
{
|
||||
// Full link
|
||||
AssertNormalizeNoTrim("This is a [link][MyLink]\n\n[MyLink]: http://company.com");
|
||||
|
||||
AssertNormalizeNoTrim("[MyLink]: http://company.com\nThis is a [link][MyLink]",
|
||||
"This is a [link][MyLink]\n\n[MyLink]: http://company.com");
|
||||
|
||||
AssertNormalizeNoTrim("This is a [link][MyLink] a normal link [link](http://google.com) and another def link [link2][MyLink2]\n\n[MyLink]: http://company.com\n[MyLink2]: http://company2.com");
|
||||
|
||||
// Collapsed link
|
||||
AssertNormalizeNoTrim("This is a [link][]\n\n[link]: http://company.com");
|
||||
|
||||
// Shortcut link
|
||||
AssertNormalizeNoTrim("This is a [link]\n\n[link]: http://company.com");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EscapeInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("This is an escape \\* with another \\[");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HtmlEntityInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("This is a ä blank");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HtmlInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("foo <hr/> bar");
|
||||
AssertNormalizeNoTrim(@"foo <hr foo=""bar""/> bar");
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void SpaceBetweenNodes()
|
||||
{
|
||||
AssertNormalizeNoTrim("# Hello World\nFoobar is a better bar.",
|
||||
"# Hello World\n\nFoobar is a better bar.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SpaceBetweenNodesEvenForHeadlines()
|
||||
{
|
||||
AssertNormalizeNoTrim("# Hello World\n## Chapter 1\nFoobar is a better bar.",
|
||||
"# Hello World\n\n## Chapter 1\n\nFoobar is a better bar.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SpaceRemoveAtStartAndEnd()
|
||||
{
|
||||
AssertNormalizeNoTrim("\n\n# Hello World\n## Chapter 1\nFoobar is a better bar.\n\n",
|
||||
"# Hello World\n\n## Chapter 1\n\nFoobar is a better bar.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SpaceShortenBetweenNodes()
|
||||
{
|
||||
AssertNormalizeNoTrim("# Hello World\n\n\n\nFoobar is a better bar.",
|
||||
"# Hello World\n\nFoobar is a better bar.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BiggerSample()
|
||||
{
|
||||
var input = @"# Heading 1
|
||||
|
||||
This is a paragraph
|
||||
|
||||
This is another paragraph
|
||||
|
||||
- This is a list item 1
|
||||
- This is a list item 2
|
||||
- This is a list item 3
|
||||
|
||||
```C#
|
||||
This is a code block
|
||||
```
|
||||
|
||||
> This is a quote block
|
||||
|
||||
This is an indented code block
|
||||
line 2 of indented
|
||||
|
||||
This is a last line";
|
||||
AssertNormalizeNoTrim(input);
|
||||
}
|
||||
|
||||
private static void AssertSyntax(string expected, MarkdownObject syntax)
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
var normalizer = new NormalizeRenderer(writer);
|
||||
var document = new MarkdownDocument();
|
||||
if (syntax is Block)
|
||||
{
|
||||
document.Add(syntax as Block);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
normalizer.Render(document);
|
||||
|
||||
var actual = writer.ToString();
|
||||
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
public void AssertNormalizeNoTrim(string input, string expected = null, NormalizeOptions options = null)
|
||||
=> AssertNormalize(input, expected, false, options);
|
||||
|
||||
public void AssertNormalize(string input, string expected = null, bool trim = true, NormalizeOptions options = null)
|
||||
{
|
||||
expected = expected ?? input;
|
||||
input = NormText(input, trim);
|
||||
expected = NormText(expected, trim);
|
||||
|
||||
var result = Markdown.Normalize(input, options);
|
||||
result = NormText(result, trim);
|
||||
|
||||
Console.WriteLine("```````````````````Source");
|
||||
Console.WriteLine(TestParser.DisplaySpaceAndTabs(input));
|
||||
Console.WriteLine("```````````````````Result");
|
||||
Console.WriteLine(TestParser.DisplaySpaceAndTabs(result));
|
||||
Console.WriteLine("```````````````````Expected");
|
||||
Console.WriteLine(TestParser.DisplaySpaceAndTabs(expected));
|
||||
Console.WriteLine("```````````````````");
|
||||
Console.WriteLine();
|
||||
|
||||
TextAssert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
private static string NormText(string text, bool trim)
|
||||
{
|
||||
if (trim)
|
||||
{
|
||||
text = text.Trim();
|
||||
}
|
||||
return text.Replace("\r\n", "\n").Replace('\r', '\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/Markdig.Tests/TestOrderedList.cs
Normal file
43
src/Markdig.Tests/TestOrderedList.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestOrderedList
|
||||
{
|
||||
[Test]
|
||||
public void TestReplace()
|
||||
{
|
||||
var list = new OrderedList<ITest>
|
||||
{
|
||||
new A(),
|
||||
new B(),
|
||||
new C(),
|
||||
};
|
||||
|
||||
// Replacing B with D. Order should now be A, D, B.
|
||||
var result = list.Replace<B>(new D());
|
||||
Assert.That(result, Is.True);
|
||||
Assert.That(list.Count, Is.EqualTo(3));
|
||||
Assert.That(list[0], Is.InstanceOf<A>());
|
||||
Assert.That(list[1], Is.InstanceOf<D>());
|
||||
Assert.That(list[2], Is.InstanceOf<C>());
|
||||
|
||||
// Replacing B again should fail, as it's no longer in the list.
|
||||
Assert.That(list.Replace<B>(new D()), Is.False);
|
||||
}
|
||||
|
||||
#region Test fixtures
|
||||
private interface ITest { }
|
||||
private class A : ITest { }
|
||||
private class B : ITest { }
|
||||
private class C : ITest { }
|
||||
private class D : ITest { }
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,53 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Markdig.Extensions.JiraLinks;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class TestParser
|
||||
{
|
||||
[Test]
|
||||
public void TestEmphasisAndHtmlEntity()
|
||||
{
|
||||
var markdownText = "*Unlimited-Fun®*®";
|
||||
TestSpec(markdownText, "<p><em>Unlimited-Fun®</em>®</p>");
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null)
|
||||
{
|
||||
foreach (var pipeline in GetPipeline(extensions))
|
||||
{
|
||||
Console.WriteLine($"Pipeline configured with extensions: {pipeline.Key}");
|
||||
// Uncomment this line to get more debug information for process inlines.
|
||||
//pipeline.DebugLog = Console.Out;
|
||||
var result = Markdown.ToHtml(inputText, pipeline.Value);
|
||||
|
||||
result = Compact(result);
|
||||
expectedOutputText = Compact(expectedOutputText);
|
||||
|
||||
Console.WriteLine("```````````````````Source");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(inputText));
|
||||
Console.WriteLine("```````````````````Result");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(result));
|
||||
Console.WriteLine("```````````````````Expected");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(expectedOutputText));
|
||||
Console.WriteLine("```````````````````");
|
||||
Console.WriteLine();
|
||||
TextAssert.AreEqual(expectedOutputText, result);
|
||||
TestSpec(inputText, expectedOutputText, pipeline.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, MarkdownPipeline pipeline)
|
||||
{
|
||||
// Uncomment this line to get more debug information for process inlines.
|
||||
//pipeline.DebugLog = Console.Out;
|
||||
var result = Markdown.ToHtml(inputText, pipeline);
|
||||
|
||||
result = Compact(result);
|
||||
expectedOutputText = Compact(expectedOutputText);
|
||||
|
||||
Console.WriteLine("```````````````````Source");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(inputText));
|
||||
Console.WriteLine("```````````````````Result");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(result));
|
||||
Console.WriteLine("```````````````````Expected");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(expectedOutputText));
|
||||
Console.WriteLine("```````````````````");
|
||||
Console.WriteLine();
|
||||
TextAssert.AreEqual(expectedOutputText, result);
|
||||
}
|
||||
|
||||
private static IEnumerable<KeyValuePair<string, MarkdownPipeline>> GetPipeline(string extensionsGroupText)
|
||||
{
|
||||
// For the standard case, we make sure that both the CommmonMark core and Extra/Advanced are CommonMark compliant!
|
||||
@@ -66,8 +80,15 @@ namespace Markdig.Tests
|
||||
{
|
||||
var builder = new MarkdownPipelineBuilder();
|
||||
builder.DebugLog = Console.Out;
|
||||
var pipeline = extensionsText == "self" ? builder.UseSelfPipeline() : builder.Configure(extensionsText);
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>(extensionsText, pipeline.Build());
|
||||
if (extensionsText == "jiralinks")
|
||||
{
|
||||
builder.UseJiraLinks(new JiraLinkOptions("http://your.company.abc"));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder = extensionsText == "self" ? builder.UseSelfPipeline() : builder.Configure(extensionsText);
|
||||
}
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>(extensionsText, builder.Build());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
17
src/Markdig.Tests/TestPlainText.cs
Normal file
17
src/Markdig.Tests/TestPlainText.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestPlainText
|
||||
{
|
||||
[Test]
|
||||
public void TestPlain()
|
||||
{
|
||||
var markdownText = "*Hello*, [world](http://example.com)!";
|
||||
var expected = "Hello, world!";
|
||||
var actual = Markdown.ToPlainText(markdownText);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,18 @@ namespace Markdig.Tests
|
||||
[TestFixture]
|
||||
public class TestPlayParser
|
||||
{
|
||||
[Test]
|
||||
public void TestListBug2()
|
||||
{
|
||||
TestParser.TestSpec("10.\t*test* – test\n\n11.\t__test__ test\n\n", @"<ol start=""10"">
|
||||
<li><p><em>test</em> – test</p>
|
||||
</li>
|
||||
<li><p><strong>test</strong> test</p>
|
||||
</li>
|
||||
</ol>
|
||||
");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSimple()
|
||||
{
|
||||
@@ -109,6 +121,11 @@ blabla
|
||||
<h1>header2</h1>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHtmlh4Bug()
|
||||
{
|
||||
TestParser.TestSpec(@"<h4>foobar</h4>", @"<h4>foobar</h4>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStandardUriEscape()
|
||||
@@ -118,7 +135,7 @@ blabla
|
||||
|
||||
|
||||
[Test]
|
||||
public void TestBugAdvancaed()
|
||||
public void TestBugAdvanced()
|
||||
{
|
||||
TestParser.TestSpec(@"`https://{domain}/callbacks`
|
||||
#### HEADING
|
||||
@@ -126,6 +143,69 @@ Paragraph
|
||||
", "<p><code>https://{domain}/callbacks</code></p>\n<h4 id=\"heading\">HEADING</h4>\n<p>Paragraph</p>", "advanced");
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void TestBugEmphAttribute()
|
||||
{
|
||||
// https://github.com/lunet-io/markdig/issues/108
|
||||
TestParser.TestSpec(@"*test*{name=value}", "<p><em name=\"value\">test</em></p>", "advanced");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBugPipeTables()
|
||||
{
|
||||
// https://github.com/lunet-io/markdig/issues/73
|
||||
TestParser.TestSpec(@"| abc | def |
|
||||
| --- | --- |
|
||||
| 1 | ~3 |
|
||||
", @"<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>abc</th>
|
||||
<th>def</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>~3</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>", "advanced");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGridTableWithCustomAttributes() {
|
||||
|
||||
var input = @"
|
||||
{.table}
|
||||
+---+---+
|
||||
| a | b |
|
||||
+===+===+
|
||||
| 1 | 2 |
|
||||
+---+---+
|
||||
";
|
||||
|
||||
var expected = @"<table class=""table"">
|
||||
<col style=""width:50%"">
|
||||
<col style=""width:50%"">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
";
|
||||
TestParser.TestSpec(input, expected, "advanced");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSamePipelineAllExtensions()
|
||||
{
|
||||
|
||||
@@ -659,6 +659,20 @@ code ( 2, 0) 3-13
|
||||
");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIndentedCodeAfterList()
|
||||
{
|
||||
// 0 1 2 3 4 5
|
||||
// 012345678901234567 8 901234567890123456 789012345678901234 56789
|
||||
Check("1) Some list item\n\n some code\n more code\n", @"
|
||||
list ( 0, 0) 0-53
|
||||
listitem ( 0, 0) 0-53
|
||||
paragraph ( 0, 3) 3-16
|
||||
literal ( 0, 3) 3-16
|
||||
code ( 2, 0) 19-53
|
||||
");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIndentedCodeWithTabs()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
@@ -86,7 +86,7 @@ namespace Markdig.Tests
|
||||
//output.WriteLine();
|
||||
}
|
||||
|
||||
Assert.True(string.CompareOrdinal(expectedValue, actualValue) == 0, "strings are differing");
|
||||
Assert.AreEqual(expectedValue, actualValue);
|
||||
}
|
||||
|
||||
private static string ToSafeString(this char c)
|
||||
|
||||
36
src/Markdig.WebApp/Markdig.WebApp.csproj
Normal file
36
src/Markdig.WebApp/Markdig.WebApp.csproj
Normal file
@@ -0,0 +1,36 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp1.0</TargetFramework>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<AssemblyName>Markdig.WebApp</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>Markdig.WebApp</PackageId>
|
||||
<RuntimeFrameworkVersion>1.0.4</RuntimeFrameworkVersion>
|
||||
<PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;dnxcore50;portable-net45+win8</PackageTargetFallback>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Views">
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Markdig\Markdig.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="1.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="1.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="1.0.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="1.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="1.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="1.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>3cad9801-9976-46be-baca-f6d0d21fdc00</ProjectGuid>
|
||||
<RootNamespace>Markdig.WebApp</RootNamespace>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
@@ -1,58 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"version": "1.0.0",
|
||||
"type": "platform"
|
||||
},
|
||||
"Microsoft.ApplicationInsights.AspNetCore": "1.0.0",
|
||||
"Microsoft.AspNetCore.Mvc": "1.0.0",
|
||||
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
|
||||
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
|
||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
|
||||
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
|
||||
"Microsoft.Extensions.Configuration.Json": "1.0.0",
|
||||
"Microsoft.Extensions.Logging": "1.0.0",
|
||||
"Microsoft.Extensions.Logging.Console": "1.0.0",
|
||||
"Microsoft.Extensions.Logging.Debug": "1.0.0",
|
||||
"Markdig": "0.7.2"
|
||||
},
|
||||
|
||||
"tools": {
|
||||
"Microsoft.AspNetCore.Server.IISIntegration.Tools": {
|
||||
"version": "1.0.0-preview2-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%" ]
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
pipeline.BlockParsers.AddIfNotAlready<AbbreviationParser>();
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null && !htmlRenderer.ObjectRenderers.Contains<HtmlAbbreviationRenderer>())
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
/// </summary>
|
||||
public AbbreviationParser()
|
||||
{
|
||||
OpeningCharacters = new[] {'*'};
|
||||
OpeningCharacters = new[] { '*' };
|
||||
}
|
||||
|
||||
public override BlockState TryOpen(BlockProcessor processor)
|
||||
@@ -90,7 +90,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
|
||||
inlineProcessor.LiteralInlineParser.PostMatch += (InlineProcessor processor, ref StringSlice slice) =>
|
||||
{
|
||||
var literal = (LiteralInline) processor.Inline;
|
||||
var literal = (LiteralInline)processor.Inline;
|
||||
var originalLiteral = literal;
|
||||
|
||||
ContainerInline container = null;
|
||||
@@ -145,11 +145,13 @@ namespace Markdig.Extensions.Abbreviations
|
||||
container.AppendChild(literal);
|
||||
}
|
||||
|
||||
literal.Span.End = abbrInline.Span.Start - 1;
|
||||
// Truncate it before the abbreviation
|
||||
literal.Content.End = i - 1;
|
||||
}
|
||||
|
||||
literal.Span.End = abbrInline.Span.Start - 1;
|
||||
// Truncate it before the abbreviation
|
||||
literal.Content.End = i - 1;
|
||||
|
||||
|
||||
// Appned the abbreviation
|
||||
container.AppendChild(abbrInline);
|
||||
|
||||
@@ -190,28 +192,44 @@ namespace Markdig.Extensions.Abbreviations
|
||||
{
|
||||
// The word matched must be embraced by punctuation or whitespace or \0.
|
||||
var index = matchIndex - 1;
|
||||
while (index > content.Start)
|
||||
while (index >= content.Start)
|
||||
{
|
||||
var c = content.PeekCharAbsolute(index);
|
||||
if (!(c == '\0' || c.IsAsciiPunctuation() || c.IsWhitespace()))
|
||||
if (!(c == '\0' || c.IsWhitespace() || c.IsAsciiPunctuation()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!c.IsAsciiPunctuation())
|
||||
|
||||
if (c.IsAlphaNumeric())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!c.IsAsciiPunctuation() || c.IsWhitespace())
|
||||
{
|
||||
break;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
|
||||
// This will check if the next char at the end of the StringSlice is whitespace, punctuation or \0.
|
||||
var contentNew = content;
|
||||
contentNew.End = content.End + 1;
|
||||
index = matchIndex + match.Length;
|
||||
while (index < content.End)
|
||||
while (index <= contentNew.End)
|
||||
{
|
||||
var c = content.PeekCharAbsolute(index);
|
||||
if (!(c == '\0' || c.IsAsciiPunctuation() || c.IsWhitespace()))
|
||||
var c = contentNew.PeekCharAbsolute(index);
|
||||
if (!(c == '\0' || c.IsWhitespace() || c.IsAsciiPunctuation()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!c.IsAsciiPunctuation())
|
||||
|
||||
if (c.IsAlphaNumeric())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c.IsWhitespace())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -143,7 +143,12 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
stripRenderer.Render(headingBlock.Inline);
|
||||
var headingText = headingWriter.ToString();
|
||||
headingWriter.GetStringBuilder().Length = 0;
|
||||
headingText = LinkHelper.Urilize(headingText, (options & AutoIdentifierOptions.AllowOnlyAscii) != 0);
|
||||
|
||||
// TODO: Should we have a struct with more configure optionss for LinkHelper.Urilize?
|
||||
headingText = LinkHelper.Urilize(headingText,
|
||||
(options & AutoIdentifierOptions.AllowOnlyAscii) != 0,
|
||||
(options & AutoIdentifierOptions.KeepOpeningDigits) != 0,
|
||||
(options & AutoIdentifierOptions.DiscardDots) != 0);
|
||||
|
||||
var baseHeadingId = string.IsNullOrEmpty(headingText) ? "section" : headingText;
|
||||
int index = 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
@@ -21,6 +21,11 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
/// </summary>
|
||||
Default = AutoLink | AllowOnlyAscii,
|
||||
|
||||
/// <summary>
|
||||
/// Renders auto identifiers like GitHub.
|
||||
/// </summary>
|
||||
GitHub = Default | KeepOpeningDigits | DiscardDots,
|
||||
|
||||
/// <summary>
|
||||
/// Allows to link to a header by using the same text as the header for the link label. Default is <c>true</c>
|
||||
/// </summary>
|
||||
@@ -30,5 +35,15 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
/// Allows only ASCII characters in the url (HTML 5 allows to have UTF8 characters). Default is <c>true</c>
|
||||
/// </summary>
|
||||
AllowOnlyAscii = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Allows to keep digits starting a heading (by default, it keeps only characters starting from the first letter)
|
||||
/// </summary>
|
||||
KeepOpeningDigits = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Discard dots when computing an identifier.
|
||||
/// </summary>
|
||||
DiscardDots = 8
|
||||
}
|
||||
}
|
||||
29
src/Markdig/Extensions/AutoLinks/AutoLinkExtension.cs
Normal file
29
src/Markdig/Extensions/AutoLinks/AutoLinkExtension.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.AutoLinks
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension to automatically create <see cref="LinkInline"/> when a link url http: or mailto: is found.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.IMarkdownExtension" />
|
||||
public class AutoLinkExtension : IMarkdownExtension
|
||||
{
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
if (!pipeline.InlineParsers.Contains<AutoLinkParser>())
|
||||
{
|
||||
// Insert the parser before any other parsers
|
||||
pipeline.InlineParsers.Insert(0, new AutoLinkParser());
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
239
src/Markdig/Extensions/AutoLinks/AutoLinkParser.cs
Normal file
239
src/Markdig/Extensions/AutoLinks/AutoLinkParser.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.AutoLinks
|
||||
{
|
||||
/// <summary>
|
||||
/// The inline parser used to for autolinks.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Parsers.InlineParser" />
|
||||
public class AutoLinkParser : InlineParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AutoLinkParser"/> class.
|
||||
/// </summary>
|
||||
public AutoLinkParser()
|
||||
{
|
||||
OpeningCharacters = new char[]
|
||||
{
|
||||
'h', // for http:// and https://
|
||||
'f', // for ftp://
|
||||
'm', // for mailto:
|
||||
'w', // for www.
|
||||
};
|
||||
}
|
||||
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
// Previous char must be a whitespace or a punctuation
|
||||
var previousChar = slice.PeekCharExtra(-1);
|
||||
if (!previousChar.IsAsciiPunctuation() && !previousChar.IsWhiteSpaceOrZero())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
List<char> pendingEmphasis;
|
||||
// Check that an autolink is possible in the current context
|
||||
if (!IsAutoLinkValidInCurrentContext(processor, out pendingEmphasis))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var startPosition = slice.Start;
|
||||
|
||||
var c = slice.CurrentChar;
|
||||
// Precheck URL
|
||||
switch (c)
|
||||
{
|
||||
case 'h':
|
||||
if (!slice.MatchLowercase("ttp://", 1) && !slice.MatchLowercase("ttps://", 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
if (!slice.MatchLowercase("tp://", 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (!slice.MatchLowercase("ailto:", 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
if (!slice.MatchLowercase("ww.", 1) || previousChar == '/') // We won't match http:/www. or /www.xxx
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse URL
|
||||
string link;
|
||||
if (!LinkHelper.TryParseUrl(ref slice, out link))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// If we have any pending emphasis, remove any pending emphasis characters from the end of the link
|
||||
if (pendingEmphasis != null)
|
||||
{
|
||||
for (int i = link.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (pendingEmphasis.Contains(link[i]))
|
||||
{
|
||||
slice.Start--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i < link.Length - 1)
|
||||
{
|
||||
link = link.Substring(0, i + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Post-check URL
|
||||
switch (c)
|
||||
{
|
||||
case 'h':
|
||||
if (string.Equals(link, "http://", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(link, "https://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
if (string.Equals(link, "ftp://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (string.Equals(link, "mailto:", StringComparison.OrdinalIgnoreCase) || !link.Contains("@"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
// We require at least two .
|
||||
if (link.Length <= "www.x.y".Length || link.IndexOf(".", 4, StringComparison.Ordinal) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int line;
|
||||
int column;
|
||||
var inline = new LinkInline()
|
||||
{
|
||||
Span =
|
||||
{
|
||||
Start = processor.GetSourcePosition(startPosition, out line, out column),
|
||||
},
|
||||
Line = line,
|
||||
Column = column,
|
||||
Url = c == 'w' ? "http://" + link : link,
|
||||
IsClosed = true,
|
||||
};
|
||||
inline.Span.End = inline.Span.Start + link.Length - 1;
|
||||
inline.UrlSpan = inline.Span;
|
||||
inline.AppendChild(new LiteralInline()
|
||||
{
|
||||
Span = inline.Span,
|
||||
Line = line,
|
||||
Column = column,
|
||||
Content = new StringSlice(slice.Text, startPosition, startPosition + link.Length - 1),
|
||||
IsClosed = true
|
||||
});
|
||||
processor.Inline = inline;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsAutoLinkValidInCurrentContext(InlineProcessor processor, out List<char> pendingEmphasis)
|
||||
{
|
||||
pendingEmphasis = null;
|
||||
|
||||
// Case where there is a pending HtmlInline <a>
|
||||
var currentInline = processor.Inline;
|
||||
while (currentInline != null)
|
||||
{
|
||||
var htmlInline = currentInline as HtmlInline;
|
||||
if (htmlInline != null)
|
||||
{
|
||||
// If we have a </a> we don't expect nested <a>
|
||||
if (htmlInline.Tag.StartsWith("</a", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// If there is a pending <a>, we can't allow a link
|
||||
if (htmlInline.Tag.StartsWith("<a", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check previous sibling and parents in the tree
|
||||
currentInline = currentInline.PreviousSibling ?? currentInline.Parent;
|
||||
}
|
||||
|
||||
// Check that we don't have any pending brackets opened (where we could have a possible markdown link)
|
||||
// NOTE: This assume that [ and ] are used for links, otherwise autolink will not work properly
|
||||
currentInline = processor.Inline;
|
||||
int countBrackets = 0;
|
||||
while (currentInline != null)
|
||||
{
|
||||
var linkDelimiterInline = currentInline as LinkDelimiterInline;
|
||||
if (linkDelimiterInline != null && linkDelimiterInline.IsActive)
|
||||
{
|
||||
if (linkDelimiterInline.Type == DelimiterType.Open)
|
||||
{
|
||||
countBrackets++;
|
||||
}
|
||||
else if (linkDelimiterInline.Type == DelimiterType.Close)
|
||||
{
|
||||
countBrackets--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Record all pending characters for emphasis
|
||||
var emphasisDelimiter = currentInline as EmphasisDelimiterInline;
|
||||
if (emphasisDelimiter != null)
|
||||
{
|
||||
if (pendingEmphasis == null)
|
||||
{
|
||||
// Not optimized for GC, but we don't expect this case much
|
||||
pendingEmphasis = new List<char>();
|
||||
}
|
||||
if (!pendingEmphasis.Contains(emphasisDelimiter.DelimiterChar))
|
||||
{
|
||||
pendingEmphasis.Add(emphasisDelimiter.DelimiterChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
currentInline = currentInline.Parent;
|
||||
}
|
||||
|
||||
return countBrackets <= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace Markdig.Extensions.Bootstrap
|
||||
pipeline.DocumentProcessed += PipelineOnDocumentProcessed;
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Markdig.Extensions.Citations
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace Markdig.Extensions.CustomContainers
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Markdig.Extensions.DefinitionLists
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Markdig.Extensions.Diagrams
|
||||
{
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
@@ -25,6 +25,7 @@ namespace Markdig.Extensions.Diagrams
|
||||
var codeRenderer = htmlRenderer.ObjectRenderers.FindExact<CodeBlockRenderer>();
|
||||
// TODO: Add other well known diagram languages
|
||||
codeRenderer.BlocksAsDiv.Add("mermaid");
|
||||
codeRenderer.BlocksAsDiv.Add("nomnoml");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
@@ -12,16 +12,23 @@ namespace Markdig.Extensions.Emoji
|
||||
/// <seealso cref="Markdig.IMarkdownExtension" />
|
||||
public class EmojiExtension : IMarkdownExtension
|
||||
{
|
||||
private readonly bool _enableSmiley;
|
||||
|
||||
public EmojiExtension(bool enableSmiley = true)
|
||||
{
|
||||
_enableSmiley = enableSmiley;
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
if (!pipeline.InlineParsers.Contains<EmojiParser>())
|
||||
{
|
||||
// Insert the parser before any other parsers
|
||||
pipeline.InlineParsers.Insert(0, new EmojiParser());
|
||||
pipeline.InlineParsers.Insert(0, new EmojiParser(_enableSmiley));
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
@@ -24,13 +24,19 @@ namespace Markdig.Extensions.Emoji
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EmojiParser"/> class.
|
||||
/// </summary>
|
||||
public EmojiParser()
|
||||
public EmojiParser(bool enableSmiley = true)
|
||||
{
|
||||
EnableSmiley = enableSmiley;
|
||||
OpeningCharacters = null;
|
||||
EmojiToUnicode = new Dictionary<string, string>(EmojiToUnicodeDefault);
|
||||
SmileyToEmoji = new Dictionary<string, string>(SmileyToEmojiDefault);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether to process smiley.
|
||||
/// </summary>
|
||||
public bool EnableSmiley { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the emoji to unicode mapping. This can be modified before this parser is initialized.
|
||||
/// </summary>
|
||||
@@ -81,17 +87,14 @@ namespace Markdig.Extensions.Emoji
|
||||
return false;
|
||||
}
|
||||
|
||||
// Following char must be a space
|
||||
if (!slice.PeekCharExtra(match.Length).IsWhiteSpaceOrZero())
|
||||
string emoji = match;
|
||||
if (EnableSmiley)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we have a smiley, we decode it to emoji
|
||||
string emoji;
|
||||
if (!SmileyToEmoji.TryGetValue(match, out emoji))
|
||||
{
|
||||
emoji = match;
|
||||
// If we have a smiley, we decode it to emoji
|
||||
if (!SmileyToEmoji.TryGetValue(match, out emoji))
|
||||
{
|
||||
emoji = match;
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the eomji to unicode
|
||||
@@ -997,7 +1000,16 @@ namespace Markdig.Extensions.Emoji
|
||||
{":large_orange_diamond:", "🔶"},
|
||||
{":large_blue_diamond:", "🔷"},
|
||||
{":small_orange_diamond:", "🔸"},
|
||||
{":small_blue_diamond:", "🔹"}
|
||||
{":small_blue_diamond:", "🔹"},
|
||||
|
||||
// Custom additions
|
||||
{ ":custom_arrow_left:", "←"},
|
||||
{ ":custom_arrow_right:", "→"},
|
||||
{ ":custom_arrow_left_right:", "↔"},
|
||||
|
||||
{ ":custom_arrow_left_strong:", "⇐"},
|
||||
{ ":custom_arrow_right_strong:", "⇒"},
|
||||
{ ":custom_arrow_left_right_strong:", "⇔"},
|
||||
};
|
||||
|
||||
SmileyToEmojiDefault = new Dictionary<string, string>()
|
||||
@@ -1067,6 +1079,15 @@ namespace Markdig.Extensions.Emoji
|
||||
{":-$", ":unamused:"},
|
||||
{";)", ":wink:"},
|
||||
{";-)", ":wink:"},
|
||||
|
||||
// Custom arrows
|
||||
{"<-", ":custom_arrow_left:" },
|
||||
{"->", ":custom_arrow_rigth:" },
|
||||
{"<->", ":custom_arrow_left_rigth:" },
|
||||
|
||||
{"<=", ":custom_arrow_left_strong:" },
|
||||
{"=>", ":custom_arrow_rigth_strong:" },
|
||||
{"<=>", ":custom_arrow_left_rigth_strong:" },
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Markdig.Extensions.EmphasisExtras
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Markdig.Extensions.Figures
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Markdig.Extensions.Footers
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Markdig.Extensions.Footnotes
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
{
|
||||
objectToAttach = parent[indexOfParagraph + 1];
|
||||
// We can remove the paragraph as it is empty
|
||||
parent.RemoveAt(indexOfParagraph);
|
||||
paragraph.RemoveAfterProcessInlines = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,9 +171,10 @@ namespace Markdig.Extensions.GenericAttributes
|
||||
|
||||
// Skip any whitespaces
|
||||
line.TrimStart();
|
||||
c = line.CurrentChar;
|
||||
|
||||
// Handle boolean properties that are not followed by =
|
||||
if ((hasSpace && (line.CurrentChar == '.' || line.CurrentChar == '#' || IsStartAttributeName(line.CurrentChar))) || line.CurrentChar == '}')
|
||||
if ((hasSpace && (c == '.' || c == '#' || IsStartAttributeName(c))) || c == '}')
|
||||
{
|
||||
if (properties == null)
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Markdig.Extensions.Hardlines
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
31
src/Markdig/Extensions/JiraLinks/JiraLink.cs
Normal file
31
src/Markdig/Extensions/JiraLinks/JiraLink.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System.Diagnostics;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.JiraLinks
|
||||
{
|
||||
/// <summary>
|
||||
/// Model for a JIRA link item
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{ProjectKey}-{Issue}")]
|
||||
public class JiraLink : LinkInline
|
||||
{
|
||||
public JiraLink()
|
||||
{
|
||||
IsClosed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// JIRA Project Key
|
||||
/// </summary>
|
||||
public StringSlice ProjectKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// JIRA Issue Number
|
||||
/// </summary>
|
||||
public StringSlice Issue { get; set; }
|
||||
}
|
||||
}
|
||||
37
src/Markdig/Extensions/JiraLinks/JiraLinkExtension.cs
Normal file
37
src/Markdig/Extensions/JiraLinks/JiraLinkExtension.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using Markdig.Parsers.Inlines;
|
||||
using Markdig.Renderers;
|
||||
|
||||
namespace Markdig.Extensions.JiraLinks
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple inline parser extension for Markdig to find, and
|
||||
/// automatically add links to JIRA issue numbers.
|
||||
/// </summary>
|
||||
public class JiraLinkExtension : IMarkdownExtension
|
||||
{
|
||||
private readonly JiraLinkOptions _options;
|
||||
|
||||
public JiraLinkExtension(JiraLinkOptions options)
|
||||
{
|
||||
_options = options;
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
if (!pipeline.InlineParsers.Contains<JiraLinkInlineParser>())
|
||||
{
|
||||
// Insert the parser before the link inline parser
|
||||
pipeline.InlineParsers.InsertBefore<LinkInlineParser>(new JiraLinkInlineParser(_options));
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
// Nothing to setup, JiraLinks used a normal LinkInlineRenderer
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
115
src/Markdig/Extensions/JiraLinks/JiraLinkInlineParser.cs
Normal file
115
src/Markdig/Extensions/JiraLinks/JiraLinkInlineParser.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
using System.Text;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.JiraLinks
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds and replaces JIRA links inline
|
||||
/// </summary>
|
||||
public class JiraLinkInlineParser : InlineParser
|
||||
{
|
||||
private readonly JiraLinkOptions _options;
|
||||
private readonly string _baseUrl;
|
||||
|
||||
public JiraLinkInlineParser(JiraLinkOptions options)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_baseUrl = _options.GetUrl();
|
||||
//look for uppercase chars at the start (for the project key)
|
||||
OpeningCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
|
||||
}
|
||||
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
// Allow preceding whitespace or `(`
|
||||
var pc = slice.PeekCharExtra(-1);
|
||||
if (!pc.IsWhiteSpaceOrZero() && pc != '(')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var current = slice.CurrentChar;
|
||||
|
||||
var startKey = slice.Start;
|
||||
var endKey = slice.Start;
|
||||
|
||||
//read as many uppercase characters as required - project key
|
||||
while (current.IsAlphaUpper())
|
||||
{
|
||||
endKey = slice.Start;
|
||||
current = slice.NextChar();
|
||||
}
|
||||
|
||||
//require a '-' between key and issue number
|
||||
if (!current.Equals('-'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
current = slice.NextChar(); // skip -
|
||||
|
||||
//read as many numbers as required - issue number
|
||||
if (!current.IsDigit())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var startIssue = slice.Start;
|
||||
var endIssue = slice.Start;
|
||||
|
||||
while (current.IsDigit())
|
||||
{
|
||||
endIssue = slice.Start;
|
||||
current = slice.NextChar();
|
||||
}
|
||||
|
||||
if (!current.IsWhiteSpaceOrZero() && current != ')') //can be followed only by a whitespace or `)`
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int line;
|
||||
int column;
|
||||
|
||||
var jiraLink = new JiraLink() //create the link at the relevant position
|
||||
{
|
||||
Span =
|
||||
{
|
||||
Start = processor.GetSourcePosition(slice.Start, out line, out column)
|
||||
},
|
||||
Line = line,
|
||||
Column = column,
|
||||
Issue = new StringSlice(slice.Text, startIssue, endIssue),
|
||||
ProjectKey = new StringSlice(slice.Text, startKey, endKey),
|
||||
};
|
||||
jiraLink.Span.End = jiraLink.Span.Start + (endIssue - startKey);
|
||||
|
||||
// Builds the Url
|
||||
var builder = new StringBuilder();
|
||||
builder.Append(_baseUrl).Append('/').Append(jiraLink.ProjectKey).Append('-').Append(jiraLink.Issue);
|
||||
jiraLink.Url = builder.ToString();
|
||||
|
||||
// Builds the Label
|
||||
builder.Length = 0;
|
||||
builder.Append(jiraLink.ProjectKey).Append('-').Append(jiraLink.Issue);
|
||||
jiraLink.AppendChild(new LiteralInline(builder.ToString()));
|
||||
|
||||
if (_options.OpenInNewWindow)
|
||||
{
|
||||
jiraLink.GetAttributes().AddProperty("target", "blank");
|
||||
}
|
||||
|
||||
processor.Inline = jiraLink;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
57
src/Markdig/Extensions/JiraLinks/JiraLinkOptions.cs
Normal file
57
src/Markdig/Extensions/JiraLinks/JiraLinkOptions.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System.Text;
|
||||
|
||||
namespace Markdig.Extensions.JiraLinks
|
||||
{
|
||||
/// <summary>
|
||||
/// Available options for replacing JIRA links
|
||||
/// </summary>
|
||||
public class JiraLinkOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The base Url (e.g. `https://mycompany.atlassian.net`)
|
||||
/// </summary>
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The base path after the base url (default is `/browse`)
|
||||
/// </summary>
|
||||
public string BasePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the link open in a new window when clicked
|
||||
/// </summary>
|
||||
public bool OpenInNewWindow { get; set; }
|
||||
|
||||
public JiraLinkOptions(string baseUrl)
|
||||
{
|
||||
OpenInNewWindow = true; //default
|
||||
BaseUrl = baseUrl;
|
||||
BasePath = "/browse";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full url composed of the <see cref="BaseUrl"/> and <see cref="BasePath"/> with no trailing `/`
|
||||
/// </summary>
|
||||
public virtual string GetUrl()
|
||||
{
|
||||
var url = new StringBuilder();
|
||||
var baseUrl = BaseUrl;
|
||||
if (baseUrl != null)
|
||||
{
|
||||
url.Append(baseUrl.TrimEnd('/'));
|
||||
}
|
||||
|
||||
url.Append("/");
|
||||
|
||||
if (BasePath != null)
|
||||
{
|
||||
url.Append(BasePath.Trim('/'));
|
||||
}
|
||||
|
||||
return url.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace Markdig.Extensions.ListExtras
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Markdig.Extensions.ListExtras
|
||||
{
|
||||
// otherwise we expect a regular alpha lettered list with a single character.
|
||||
var isUpper = c.IsAlphaUpper();
|
||||
result.OrderedStart = (Char.ToUpper(c) - 64).ToString();
|
||||
result.OrderedStart = (Char.ToUpperInvariant(c) - 64).ToString();
|
||||
result.BulletType = isUpper ? 'A' : 'a';
|
||||
result.DefaultOrderedStart = isUpper ? "A" : "a";
|
||||
state.NextChar();
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Extensions.Mathematics
|
||||
{
|
||||
/// <summary>
|
||||
/// The block parser for a <see cref="MathBlock"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Parsers.FencedBlockParserBase{Markdig.Extensions.Mathematics.MathBlock}" />
|
||||
/// <seealso cref="MathBlock" />
|
||||
public class MathBlockParser : FencedBlockParserBase<MathBlock>
|
||||
{
|
||||
/// <summary>
|
||||
@@ -22,6 +25,8 @@ namespace Markdig.Extensions.Mathematics
|
||||
MinimumMatchCount = 2;
|
||||
MaximumMatchCount = 2;
|
||||
|
||||
InfoParser = NoInfoParser;
|
||||
|
||||
DefaultClass = "math";
|
||||
|
||||
// We don't need a prefix
|
||||
@@ -39,5 +44,19 @@ namespace Markdig.Extensions.Mathematics
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
private static bool NoInfoParser(BlockProcessor state, ref StringSlice line, IFencedBlock fenced)
|
||||
{
|
||||
var c = line.CurrentChar;
|
||||
for (int i = line.Start; i <= line.End; i++)
|
||||
{
|
||||
c = line.Text[i];
|
||||
if (!c.IsSpaceOrTab())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace Markdig.Extensions.Mathematics
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
|
||||
@@ -50,11 +50,18 @@ namespace Markdig.Extensions.Mathematics
|
||||
c = slice.NextChar();
|
||||
}
|
||||
|
||||
bool canOpen;
|
||||
bool canClose;
|
||||
// Check that opening $/$$ is correct, using the same heuristics than for emphasis delimiters
|
||||
CharHelper.CheckOpenCloseDelimiter(pc, c, false, out canOpen, out canClose);
|
||||
if (!canOpen)
|
||||
bool openPrevIsPunctuation;
|
||||
bool openPrevIsWhiteSpace;
|
||||
bool openNextIsPunctuation;
|
||||
bool openNextIsWhiteSpace;
|
||||
bool openNextIsDigit = c.IsDigit();
|
||||
pc.CheckUnicodeCategory(out openPrevIsWhiteSpace, out openPrevIsPunctuation);
|
||||
c.CheckUnicodeCategory(out openNextIsWhiteSpace, out openNextIsPunctuation);
|
||||
|
||||
// Check that opening $/$$ is correct, using the different heuristics than for emphasis delimiters
|
||||
// If a $/$$ is not preceded by a whitespace or punctuation, or followed by a digit
|
||||
// this is a not a math block
|
||||
if ((!openPrevIsWhiteSpace && !openPrevIsPunctuation) || openNextIsDigit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -62,25 +69,60 @@ namespace Markdig.Extensions.Mathematics
|
||||
bool isMatching = false;
|
||||
int closeDollars = 0;
|
||||
|
||||
// Eat any leading spaces
|
||||
while (c.IsSpaceOrTab())
|
||||
{
|
||||
c = slice.NextChar();
|
||||
}
|
||||
|
||||
var start = slice.Start;
|
||||
var end = 0;
|
||||
|
||||
pc = match;
|
||||
var lastWhiteSpace = -1;
|
||||
while (c != '\0')
|
||||
{
|
||||
// Don't allow newline in an inline math expression
|
||||
if (c == '\r' || c == '\n')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't process sticks if we have a '\' as a previous char
|
||||
if (pc != '\\' )
|
||||
{
|
||||
while (c == match)
|
||||
// Record continous whitespaces at the end
|
||||
if (c.IsSpaceOrTab())
|
||||
{
|
||||
closeDollars++;
|
||||
c = slice.NextChar();
|
||||
if (lastWhiteSpace < 0)
|
||||
{
|
||||
lastWhiteSpace = slice.Start;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasClosingDollars = c == match;
|
||||
if (hasClosingDollars)
|
||||
{
|
||||
while (c == match)
|
||||
{
|
||||
closeDollars++;
|
||||
c = slice.NextChar();
|
||||
}
|
||||
}
|
||||
|
||||
if (closeDollars >= openDollars)
|
||||
{
|
||||
break;
|
||||
if (closeDollars >= openDollars)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
lastWhiteSpace = -1;
|
||||
if (hasClosingDollars)
|
||||
{
|
||||
pc = match;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
pc = match;
|
||||
}
|
||||
|
||||
if (closeDollars > 0)
|
||||
@@ -96,13 +138,30 @@ namespace Markdig.Extensions.Mathematics
|
||||
|
||||
if (closeDollars >= openDollars)
|
||||
{
|
||||
// Check that closing $/$$ is correct
|
||||
CharHelper.CheckOpenCloseDelimiter(pc, c, false, out canOpen, out canClose);
|
||||
if (!canClose || c.IsDigit())
|
||||
bool closePrevIsPunctuation;
|
||||
bool closePrevIsWhiteSpace;
|
||||
bool closeNextIsPunctuation;
|
||||
bool closeNextIsWhiteSpace;
|
||||
pc.CheckUnicodeCategory(out closePrevIsWhiteSpace, out closePrevIsPunctuation);
|
||||
c.CheckUnicodeCategory(out closeNextIsWhiteSpace, out closeNextIsPunctuation);
|
||||
|
||||
// A closing $/$$ should be followed by at least a punctuation or a whitespace
|
||||
// and if the character after an openning $/$$ was a whitespace, it should be
|
||||
// a whitespace as well for the character preceding the closing of $/$$
|
||||
if ((!closeNextIsPunctuation && !closeNextIsWhiteSpace) || (openNextIsWhiteSpace != closePrevIsWhiteSpace))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
end = slice.Start - 1;
|
||||
|
||||
if (closePrevIsWhiteSpace && lastWhiteSpace > 0)
|
||||
{
|
||||
end = lastWhiteSpace + openDollars - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
end = slice.Start - 1;
|
||||
}
|
||||
|
||||
// Create a new MathInline
|
||||
int line;
|
||||
int column;
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Markdig.Extensions.MediaLinks
|
||||
{
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
@@ -115,7 +115,6 @@ namespace Markdig.Extensions.MediaLinks
|
||||
renderer.WriteAttributes(htmlAttributes);
|
||||
|
||||
renderer.Write($"><source type=\"{mimeType}\" src=\"{linkInline.Url}\"></source></{tagType}>");
|
||||
renderer.Write("</iframe>");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Markdig.Extensions.NoRefLinks
|
||||
{
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var linkRenderer = renderer.ObjectRenderers.Find<LinkInlineRenderer>();
|
||||
if (linkRenderer != null)
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Markdig.Extensions.NonAsciiNoEscape
|
||||
{
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Markdig.Extensions.PragmaLines
|
||||
pipeline.DocumentProcessed += PipelineOnDocumentProcessed;
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Markdig.Extensions.SelfPipeline
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Markdig.Extensions.SmartyPants
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null && !htmlRenderer.ObjectRenderers.Contains<HtmlTableRenderer>())
|
||||
|
||||
@@ -27,32 +27,36 @@ namespace Markdig.Extensions.Tables
|
||||
var line = processor.Line;
|
||||
GridTableState tableState = null;
|
||||
|
||||
// Match the first row that should be of the minimal form: +---------------
|
||||
var c = line.CurrentChar;
|
||||
var lineStart = line.Start;
|
||||
int startPosition = -1;
|
||||
bool hasLeft = false,
|
||||
hasRight = false;
|
||||
|
||||
while (line.Length > 0)
|
||||
while (c == '+')
|
||||
{
|
||||
if (c == '+')
|
||||
var columnStart = line.Start;
|
||||
line.NextChar();
|
||||
line.TrimStart();
|
||||
|
||||
// if we have reached the end of the line, exit
|
||||
c = line.CurrentChar;
|
||||
if (c == 0)
|
||||
{
|
||||
tableState = tableState ?? new GridTableState { Start = processor.Start, ExpectRow = true };
|
||||
if (startPosition != -1)
|
||||
{
|
||||
hasRight = line.PeekCharAbsolute(line.Start - 1) == ':';
|
||||
tableState.AddColumn(startPosition - lineStart, line.Start - lineStart, GetAlignment(hasLeft, hasRight));
|
||||
}
|
||||
hasLeft = line.PeekChar(1) == ':';
|
||||
startPosition = line.Start;
|
||||
break;
|
||||
}
|
||||
else if (c != ':' && c != '-')
|
||||
|
||||
// Parse a column alignment
|
||||
TableColumnAlign? columnAlign;
|
||||
if (!TableHelper.ParseColumnHeader(ref line, '-', out columnAlign))
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
c = line.NextChar();
|
||||
|
||||
tableState = tableState ?? new GridTableState { Start = processor.Start, ExpectRow = true };
|
||||
tableState.AddColumn(columnStart - lineStart, line.Start - lineStart, columnAlign);
|
||||
|
||||
c = line.CurrentChar;
|
||||
}
|
||||
if (tableState == null || tableState.ColumnSlices.Count == (processor.Line.Length - 1))
|
||||
|
||||
if (c != 0 || tableState == null)
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
@@ -85,13 +89,6 @@ namespace Markdig.Extensions.Tables
|
||||
return BlockState.ContinueDiscard;
|
||||
}
|
||||
|
||||
private static TableColumnAlign GetAlignment(bool hasLeft, bool hasRight)
|
||||
{
|
||||
return hasLeft && hasRight
|
||||
? TableColumnAlign.Center
|
||||
: hasRight ? TableColumnAlign.Right : TableColumnAlign.Left;
|
||||
}
|
||||
|
||||
public override BlockState TryContinue(BlockProcessor processor, Block block)
|
||||
{
|
||||
var gridTable = (Table)block;
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace Markdig.Extensions.Tables
|
||||
Lines.Add(line);
|
||||
}
|
||||
|
||||
public void AddColumn(int start, int end, TableColumnAlign align)
|
||||
public void AddColumn(int start, int end, TableColumnAlign? align)
|
||||
{
|
||||
if (ColumnSlices == null)
|
||||
{
|
||||
@@ -60,7 +60,7 @@ namespace Markdig.Extensions.Tables
|
||||
|
||||
public int End { get; set; }
|
||||
|
||||
public TableColumnAlign Align { get; set; }
|
||||
public TableColumnAlign? Align { get; set; }
|
||||
|
||||
public int CurrentColumnSpan { get; set; }
|
||||
|
||||
|
||||
@@ -85,20 +85,27 @@ namespace Markdig.Extensions.Tables
|
||||
{
|
||||
renderer.Write($" rowspan=\"{cell.RowSpan}\"");
|
||||
}
|
||||
if (table.ColumnDefinitions != null)
|
||||
if (table.ColumnDefinitions.Count > 0)
|
||||
{
|
||||
var columnIndex = cell.ColumnIndex < 0 || cell.ColumnIndex >= table.ColumnDefinitions.Count
|
||||
? i
|
||||
: cell.ColumnIndex;
|
||||
columnIndex = columnIndex >= table.ColumnDefinitions.Count ? table.ColumnDefinitions.Count - 1 : columnIndex;
|
||||
switch (table.ColumnDefinitions[columnIndex].Alignment)
|
||||
var alignment = table.ColumnDefinitions[columnIndex].Alignment;
|
||||
if (alignment.HasValue)
|
||||
{
|
||||
case TableColumnAlign.Center:
|
||||
renderer.Write(" style=\"text-align: center;\"");
|
||||
break;
|
||||
case TableColumnAlign.Right:
|
||||
renderer.Write(" style=\"text-align: right;\"");
|
||||
break;
|
||||
switch (alignment)
|
||||
{
|
||||
case TableColumnAlign.Center:
|
||||
renderer.Write(" style=\"text-align: center;\"");
|
||||
break;
|
||||
case TableColumnAlign.Right:
|
||||
renderer.Write(" style=\"text-align: right;\"");
|
||||
break;
|
||||
case TableColumnAlign.Left:
|
||||
renderer.Write(" style=\"text-align: left;\"");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
renderer.WriteAttributes(cell);
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null && !htmlRenderer.ObjectRenderers.Contains<HtmlTableRenderer>())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
namespace Markdig.Extensions.Tables
|
||||
|
||||
@@ -274,7 +274,16 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
|
||||
var endOfTable = new LineBreakInline();
|
||||
lastElement.InsertAfter(endOfTable);
|
||||
// If the last element is a container, we have to add the EOL to its child
|
||||
// otherwise only next sibling
|
||||
if (lastElement is ContainerInline)
|
||||
{
|
||||
((ContainerInline)lastElement).AppendChild(endOfTable);
|
||||
}
|
||||
else
|
||||
{
|
||||
lastElement.InsertAfter(endOfTable);
|
||||
}
|
||||
delimiters.Add(endOfTable);
|
||||
tableState.EndOfLines.Add(endOfTable);
|
||||
}
|
||||
@@ -431,12 +440,13 @@ namespace Markdig.Extensions.Tables
|
||||
|
||||
// If we have a header row, we can remove it
|
||||
// TODO: we could optimize this by merging FindHeaderRow and the previous loop
|
||||
var tableRow = (TableRow)table[0];
|
||||
tableRow.IsHeader = Options.RequireHeaderSeparator;
|
||||
if (aligns != null)
|
||||
{
|
||||
table.RemoveAt(1);
|
||||
var tableRow = (TableRow) table[0];
|
||||
table.ColumnDefinitions.AddRange(aligns);
|
||||
tableRow.IsHeader = true;
|
||||
table.RemoveAt(1);
|
||||
table.ColumnDefinitions.AddRange(aligns);
|
||||
}
|
||||
|
||||
// Perform delimiter processor that are coming after this processor
|
||||
@@ -449,11 +459,14 @@ namespace Markdig.Extensions.Tables
|
||||
// Clear cells when we are done
|
||||
cells.Clear();
|
||||
|
||||
// Normalize the table
|
||||
table.Normalize();
|
||||
|
||||
// We don't want to continue procesing delimiters, as we are already processing them here
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool ParseHeaderString(Inline inline, out TableColumnAlign align)
|
||||
private static bool ParseHeaderString(Inline inline, out TableColumnAlign? align)
|
||||
{
|
||||
align = 0;
|
||||
var literal = inline as LiteralInline;
|
||||
@@ -500,7 +513,7 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
|
||||
// Check the left side of a `|` delimiter
|
||||
TableColumnAlign align = TableColumnAlign.Left;
|
||||
TableColumnAlign? align = null;
|
||||
if (delimiter.PreviousSibling != null && !ParseHeaderString(delimiter.PreviousSibling, out align))
|
||||
{
|
||||
break;
|
||||
@@ -520,6 +533,13 @@ namespace Markdig.Extensions.Tables
|
||||
? columnDelimiter.FirstChild
|
||||
: delimiter.NextSibling;
|
||||
|
||||
// If there is no content after
|
||||
if (IsNullOrSpace(nextSibling))
|
||||
{
|
||||
isValidRow = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ParseHeaderString(nextSibling, out align))
|
||||
{
|
||||
break;
|
||||
@@ -594,6 +614,20 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsNullOrSpace(Inline inline)
|
||||
{
|
||||
if (inline == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
var literal = inline as LiteralInline;
|
||||
if (literal != null)
|
||||
{
|
||||
return literal.Content.IsEmptyOrWhitespace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private class TableState
|
||||
{
|
||||
public TableState()
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Markdig.Extensions.Tables
|
||||
/// <summary>
|
||||
/// Gets or sets the column alignments. May be null.
|
||||
/// </summary>
|
||||
public List<TableColumnDefinition> ColumnDefinitions { get; private set; }
|
||||
public List<TableColumnDefinition> ColumnDefinitions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the table structure is valid.
|
||||
@@ -69,5 +69,33 @@ namespace Markdig.Extensions.Tables
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the number of columns of this table by taking the maximum columns and appending empty cells.
|
||||
/// </summary>
|
||||
public void Normalize()
|
||||
{
|
||||
var maxColumn = 0;
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
{
|
||||
var row = this[i] as TableRow;
|
||||
if (row != null && row.Count > maxColumn)
|
||||
{
|
||||
maxColumn = row.Count;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
{
|
||||
var row = this[i] as TableRow;
|
||||
if (row != null)
|
||||
{
|
||||
for (int j = row.Count; j < maxColumn; j++)
|
||||
{
|
||||
row.Add(new TableCell());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,6 @@ namespace Markdig.Extensions.Tables
|
||||
/// <summary>
|
||||
/// Gets or sets the column alignment.
|
||||
/// </summary>
|
||||
public TableColumnAlign Alignment { get; set; }
|
||||
public TableColumnAlign? Alignment { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace Markdig.Extensions.Tables
|
||||
/// <returns>
|
||||
/// <c>true</c> if parsing was successfull
|
||||
/// </returns>
|
||||
public static bool ParseColumnHeader(ref StringSlice slice, char delimiterChar, out TableColumnAlign align)
|
||||
public static bool ParseColumnHeader(ref StringSlice slice, char delimiterChar, out TableColumnAlign? align)
|
||||
{
|
||||
return ParseColumnHeaderDetect(ref slice, ref delimiterChar, out align);
|
||||
}
|
||||
@@ -34,7 +34,7 @@ namespace Markdig.Extensions.Tables
|
||||
/// <returns>
|
||||
/// <c>true</c> if parsing was successfull
|
||||
/// </returns>
|
||||
public static bool ParseColumnHeaderAuto(ref StringSlice slice, out char delimiterChar, out TableColumnAlign align)
|
||||
public static bool ParseColumnHeaderAuto(ref StringSlice slice, out char delimiterChar, out TableColumnAlign? align)
|
||||
{
|
||||
delimiterChar = '\0';
|
||||
return ParseColumnHeaderDetect(ref slice, ref delimiterChar, out align);
|
||||
@@ -49,11 +49,10 @@ namespace Markdig.Extensions.Tables
|
||||
/// <returns>
|
||||
/// <c>true</c> if parsing was successfull
|
||||
/// </returns>
|
||||
public static bool ParseColumnHeaderDetect(ref StringSlice slice, ref char delimiterChar, out TableColumnAlign align)
|
||||
public static bool ParseColumnHeaderDetect(ref StringSlice slice, ref char delimiterChar, out TableColumnAlign? align)
|
||||
{
|
||||
align = TableColumnAlign.Left;
|
||||
align = null;
|
||||
|
||||
// Work on a copy of the slice
|
||||
slice.TrimStart();
|
||||
var c = slice.CurrentChar;
|
||||
bool hasLeft = false;
|
||||
@@ -87,6 +86,7 @@ namespace Markdig.Extensions.Tables
|
||||
count++;
|
||||
}
|
||||
|
||||
// We expect at least one `-` delimiter char
|
||||
if (count == 0)
|
||||
{
|
||||
return false;
|
||||
@@ -104,7 +104,7 @@ namespace Markdig.Extensions.Tables
|
||||
|
||||
align = hasLeft && hasRight
|
||||
? TableColumnAlign.Center
|
||||
: hasRight ? TableColumnAlign.Right : TableColumnAlign.Left;
|
||||
: hasRight ? TableColumnAlign.Right : hasLeft ? TableColumnAlign.Left : (TableColumnAlign?) null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Markdig.Extensions.TaskLists
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Markdig.Extensions.Yaml
|
||||
/// A YAML frontmatter block.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Syntax.CodeBlock" />
|
||||
public class YamlFrontMatterBlock : CodeBlock, IFencedBlock
|
||||
public class YamlFrontMatterBlock : CodeBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="YamlFrontMatterBlock"/> class.
|
||||
@@ -19,13 +19,5 @@ namespace Markdig.Extensions.Yaml
|
||||
public YamlFrontMatterBlock(BlockParser parser) : base(parser)
|
||||
{
|
||||
}
|
||||
|
||||
public string Info { get; set; }
|
||||
|
||||
public string Arguments { get; set; }
|
||||
|
||||
public int FencedCharCount { get; set; }
|
||||
|
||||
public char FencedChar { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace Markdig.Extensions.Yaml
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
if (!renderer.ObjectRenderers.Contains<YamlFrontMatterRenderer>())
|
||||
{
|
||||
|
||||
@@ -1,44 +1,128 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Extensions.Yaml
|
||||
{
|
||||
/// <summary>
|
||||
/// Block parser for a YAML frontmatter.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Parsers.FencedBlockParserBase{YamlFrontMatterBlock}" />
|
||||
public class YamlFrontMatterParser : FencedBlockParserBase<YamlFrontMatterBlock>
|
||||
/// <seealso cref="YamlFrontMatterBlock" />
|
||||
public class YamlFrontMatterParser : BlockParser
|
||||
{
|
||||
// We reuse a FencedCodeBlock parser to grab a frontmatter, only active if it happens on the first line of the document.
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FencedCodeBlockParser"/> class.
|
||||
/// Initializes a new instance of the <see cref="YamlFrontMatterParser"/> class.
|
||||
/// </summary>
|
||||
public YamlFrontMatterParser()
|
||||
{
|
||||
OpeningCharacters = new[] { '-' };
|
||||
InfoPrefix = null;
|
||||
// We expect only 3 --- at the beginning of the file no more, no less
|
||||
MinimumMatchCount = 3;
|
||||
MaximumMatchCount = 3;
|
||||
this.OpeningCharacters = new[] { '-' };
|
||||
}
|
||||
|
||||
protected override YamlFrontMatterBlock CreateFencedBlock(BlockProcessor processor)
|
||||
/// <summary>
|
||||
/// Creates the front matter block.
|
||||
/// </summary>
|
||||
/// <param name="processor">The block processor</param>
|
||||
/// <returns>The front matter block</returns>
|
||||
protected virtual YamlFrontMatterBlock CreateFrontMatterBlock(BlockProcessor processor)
|
||||
{
|
||||
return new YamlFrontMatterBlock(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to match a block opening.
|
||||
/// </summary>
|
||||
/// <param name="processor">The parser processor.</param>
|
||||
/// <returns>The result of the match</returns>
|
||||
public override BlockState TryOpen(BlockProcessor processor)
|
||||
{
|
||||
// Only accept a frontmatter at the beginning of the file
|
||||
if (processor.LineIndex != 0)
|
||||
// We expect no indentation for a fenced code block.
|
||||
if (processor.IsCodeIndent)
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
return base.TryOpen(processor);
|
||||
// Only accept a frontmatter at the beginning of the file
|
||||
if (processor.Start != 0)
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
var line = processor.Line;
|
||||
char c = line.CurrentChar;
|
||||
|
||||
// Must consist of exactly three dashes
|
||||
while (c == '-' && count < 4)
|
||||
{
|
||||
count++;
|
||||
c = line.NextChar();
|
||||
}
|
||||
|
||||
// If three dashes (optionally followed by whitespace)
|
||||
// this is a YAML front matter blcok
|
||||
if (count == 3 && (c == '\0' || c.IsWhitespace()) && line.TrimEnd())
|
||||
{
|
||||
// Create a front matter block
|
||||
var block = this.CreateFrontMatterBlock(processor);
|
||||
block.Column = processor.Column;
|
||||
block.Span.Start = 0;
|
||||
block.Span.End = line.Start;
|
||||
|
||||
// Store the number of matched string into the context
|
||||
processor.NewBlocks.Push(block);
|
||||
|
||||
// Discard the current line as it is already parsed
|
||||
return BlockState.ContinueDiscard;
|
||||
}
|
||||
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to continue matching a block already opened.
|
||||
/// </summary>
|
||||
/// <param name="processor">The parser processor.</param>
|
||||
/// <param name="block">The block already opened.</param>
|
||||
/// <returns>The result of the match. By default, don't expect any newline</returns>
|
||||
public override BlockState TryContinue(BlockProcessor processor, Block block)
|
||||
{
|
||||
char matchChar;
|
||||
int count = 0;
|
||||
var c = processor.CurrentChar;
|
||||
|
||||
// Determine if we have a closing fence.
|
||||
// It can start or end with either <c>---</c> or <c>...</c>
|
||||
var line = processor.Line;
|
||||
if (processor.Column == 0 && (c == '-' || c == '.'))
|
||||
{
|
||||
matchChar = c;
|
||||
|
||||
while (c == matchChar)
|
||||
{
|
||||
c = line.NextChar();
|
||||
count++;
|
||||
}
|
||||
|
||||
// If we have a closing fence, close it and discard the current line
|
||||
// The line must contain only fence characters and optional following whitespace.
|
||||
if (count == 3 && !processor.IsCodeIndent && (c == '\0' || c.IsWhitespace()) && line.TrimEnd())
|
||||
{
|
||||
block.UpdateSpanEnd(line.Start - 1);
|
||||
|
||||
// Don't keep the last line
|
||||
return BlockState.BreakDiscard;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the indentation to the column before the indent
|
||||
processor.GoToColumn(processor.ColumnBeforeIndent);
|
||||
|
||||
return BlockState.Continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,9 +96,9 @@ namespace Markdig.Helpers
|
||||
int result = 0;
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
var character = Char.ToUpper(text[i]);
|
||||
var character = Char.ToUpperInvariant(text[i]);
|
||||
var candidate = romanMap[character];
|
||||
if (i + 1 < text.Length && candidate < romanMap[Char.ToUpper(text[i + 1])])
|
||||
if (i + 1 < text.Length && candidate < romanMap[Char.ToUpperInvariant(text[i + 1])])
|
||||
{
|
||||
result -= candidate;
|
||||
}
|
||||
@@ -162,6 +162,9 @@ namespace Markdig.Helpers
|
||||
return IsWhitespace(c) || IsZero(c);
|
||||
}
|
||||
|
||||
// Note that we are not considering the character & as a punctuation in HTML
|
||||
// as it is used for HTML entities, print unicode, so we assume that when we have a `&`
|
||||
// it is more likely followed by a valid HTML Entity that represents a non punctuation
|
||||
public static void CheckUnicodeCategory(this char c, out bool space, out bool punctuation)
|
||||
{
|
||||
// Credits: code from CommonMark.NET
|
||||
@@ -170,7 +173,7 @@ namespace Markdig.Helpers
|
||||
if (c <= 'ÿ')
|
||||
{
|
||||
space = c == '\0' || c == ' ' || (c >= '\t' && c <= '\r') || c == '\u00a0' || c == '\u0085';
|
||||
punctuation = c == '\0' || (c >= 33 && c <= 47) || (c >= 58 && c <= 64) || (c >= 91 && c <= 96) || (c >= 123 && c <= 126);
|
||||
punctuation = c == '\0' || (c >= 33 && c <= 47 && c != 38) || (c >= 58 && c <= 64) || (c >= 91 && c <= 96) || (c >= 123 && c <= 126);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
1304
src/Markdig/Helpers/CharNormalizer.cs
Normal file
1304
src/Markdig/Helpers/CharNormalizer.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -115,35 +115,44 @@ namespace Markdig.Helpers
|
||||
/// <param name="start">The start.</param>
|
||||
/// <param name="end">The end.</param>
|
||||
/// <returns>Index position within the string of the first opening character found in the specified text; if not found, returns -1</returns>
|
||||
public unsafe int IndexOfOpeningCharacter(string text, int start, int end)
|
||||
public int IndexOfOpeningCharacter(string text, int start, int end)
|
||||
{
|
||||
var maxChar = isOpeningCharacter.Length;
|
||||
#if SUPPORT_UNSAFE
|
||||
unsafe
|
||||
#endif
|
||||
{
|
||||
#if SUPPORT_FIXED_STRING
|
||||
fixed (char* pText = text)
|
||||
#else
|
||||
var pText = text;
|
||||
var pText = text;
|
||||
#endif
|
||||
#if SUPPORT_UNSAFE
|
||||
fixed (bool* openingChars = isOpeningCharacter)
|
||||
#else
|
||||
var openingChars = isOpeningCharacter;
|
||||
#endif
|
||||
fixed (bool* openingChars = isOpeningCharacter)
|
||||
{
|
||||
if (nonAsciiMap == null)
|
||||
{
|
||||
for (int i = start; i <= end; i++)
|
||||
if (nonAsciiMap == null)
|
||||
{
|
||||
var c = pText[i];
|
||||
if (c < maxChar && openingChars[c])
|
||||
for (int i = start; i <= end; i++)
|
||||
{
|
||||
return i;
|
||||
var c = pText[i];
|
||||
if (c < maxChar && openingChars[c])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = start; i <= end; i++)
|
||||
else
|
||||
{
|
||||
var c = pText[i];
|
||||
if ((c < maxChar && openingChars[c]) || nonAsciiMap.ContainsKey(c))
|
||||
for (int i = start; i <= end; i++)
|
||||
{
|
||||
return i;
|
||||
var c = pText[i];
|
||||
if ((c < maxChar && openingChars[c]) || nonAsciiMap.ContainsKey(c))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,7 +617,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
// expect a letter and 1-31 letters or digits
|
||||
c = s[pos + 1];
|
||||
if ((c < 'A' || c > 'Z') && (c < 'a' && c > 'z'))
|
||||
if (!((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')))
|
||||
return 0;
|
||||
|
||||
for (i = pos + 2; i < lastPos; i++)
|
||||
|
||||
@@ -31,6 +31,12 @@ namespace Markdig.Helpers
|
||||
/// <returns>The next character. `\0` is end of the iteration.</returns>
|
||||
char NextChar();
|
||||
|
||||
/// <summary>
|
||||
/// Peeks at the next character, without incrementing the <see cref="Start"/> position.
|
||||
/// </summary>
|
||||
/// <returns>The next character. `\0` is end of the iteration.</returns>
|
||||
char PeekChar();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is empty.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
@@ -18,62 +18,61 @@ namespace Markdig.Helpers
|
||||
return TryParseAutolink(ref text, out link, out isEmail);
|
||||
}
|
||||
|
||||
public static string Urilize(string headingText, bool allowOnlyAscii)
|
||||
public static string Urilize(string headingText, bool allowOnlyAscii, bool keepOpeningDigits = false, bool discardDots = false)
|
||||
{
|
||||
#if SUPPORT_NORMALIZE
|
||||
// Normalzie the string if we don't allow UTF8
|
||||
if (allowOnlyAscii)
|
||||
{
|
||||
headingText = headingText.Normalize(NormalizationForm.FormD);
|
||||
}
|
||||
#endif
|
||||
|
||||
var headingBuffer = StringBuilderCache.Local();
|
||||
bool hasLetter = false;
|
||||
bool hasLetter = keepOpeningDigits && headingText.Length > 0 && char.IsLetterOrDigit(headingText[0]);
|
||||
bool previousIsSpace = false;
|
||||
for (int i = 0; i < headingText.Length; i++)
|
||||
{
|
||||
var c = headingText[i];
|
||||
if (char.IsLetter(c))
|
||||
var normalized = allowOnlyAscii ? CharNormalizer.ConvertToAscii(c) : null;
|
||||
for (int j = 0; j < (normalized?.Length ?? 1); j++)
|
||||
{
|
||||
#if SUPPORT_NORMALIZE
|
||||
if (allowOnlyAscii && (c < ' ' || c >= 127))
|
||||
if (normalized != null)
|
||||
{
|
||||
continue;
|
||||
c = normalized[j];
|
||||
}
|
||||
#endif
|
||||
c = char.IsUpper(c) ? char.ToLowerInvariant(c) : c;
|
||||
headingBuffer.Append(c);
|
||||
hasLetter = true;
|
||||
previousIsSpace = false;
|
||||
}
|
||||
else if (hasLetter)
|
||||
{
|
||||
if (IsReservedPunctuation(c))
|
||||
|
||||
if (char.IsLetter(c))
|
||||
{
|
||||
if (previousIsSpace)
|
||||
if (allowOnlyAscii && (c < ' ' || c >= 127))
|
||||
{
|
||||
headingBuffer.Length--;
|
||||
continue;
|
||||
}
|
||||
if (headingBuffer[headingBuffer.Length - 1] != c)
|
||||
c = char.IsUpper(c) ? char.ToLowerInvariant(c) : c;
|
||||
headingBuffer.Append(c);
|
||||
hasLetter = true;
|
||||
previousIsSpace = false;
|
||||
}
|
||||
else if (hasLetter)
|
||||
{
|
||||
if (IsReservedPunctuation(c, discardDots))
|
||||
{
|
||||
if (previousIsSpace)
|
||||
{
|
||||
headingBuffer.Length--;
|
||||
}
|
||||
if (headingBuffer[headingBuffer.Length - 1] != c)
|
||||
{
|
||||
headingBuffer.Append(c);
|
||||
}
|
||||
previousIsSpace = false;
|
||||
}
|
||||
else if (c.IsDigit())
|
||||
{
|
||||
headingBuffer.Append(c);
|
||||
previousIsSpace = false;
|
||||
}
|
||||
previousIsSpace = false;
|
||||
}
|
||||
else if (c.IsDigit())
|
||||
{
|
||||
headingBuffer.Append(c);
|
||||
previousIsSpace = false;
|
||||
}
|
||||
else if (!previousIsSpace && c.IsWhitespace())
|
||||
{
|
||||
var pc = headingBuffer[headingBuffer.Length - 1];
|
||||
if (!IsReservedPunctuation(pc))
|
||||
else if (!previousIsSpace && c.IsWhitespace())
|
||||
{
|
||||
headingBuffer.Append('-');
|
||||
var pc = headingBuffer[headingBuffer.Length - 1];
|
||||
if (!IsReservedPunctuation(pc, discardDots))
|
||||
{
|
||||
headingBuffer.Append('-');
|
||||
}
|
||||
previousIsSpace = true;
|
||||
}
|
||||
previousIsSpace = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +81,7 @@ namespace Markdig.Helpers
|
||||
while (headingBuffer.Length > 0)
|
||||
{
|
||||
var c = headingBuffer[headingBuffer.Length - 1];
|
||||
if (IsReservedPunctuation(c))
|
||||
if (IsReservedPunctuation(c, false))
|
||||
{
|
||||
headingBuffer.Length--;
|
||||
}
|
||||
@@ -98,9 +97,9 @@ namespace Markdig.Helpers
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
private static bool IsReservedPunctuation(char c)
|
||||
private static bool IsReservedPunctuation(char c, bool discardDots)
|
||||
{
|
||||
return c == '_' || c == '-' || c == '.';
|
||||
return c == '_' || c == '-' || (!discardDots && c == '.');
|
||||
}
|
||||
|
||||
public static bool TryParseAutolink(ref StringSlice text, out string link, out bool isEmail)
|
||||
@@ -117,6 +116,7 @@ namespace Markdig.Helpers
|
||||
// An absolute URI, for these purposes, consists of a scheme followed by a colon (:)
|
||||
// followed by zero or more characters other than ASCII whitespace and control characters, <, and >.
|
||||
// If the URI includes these characters, they must be percent-encoded (e.g. %20 for a space).
|
||||
// A URI that would end with a full stop (.) is treated instead as ending immediately before the full stop.
|
||||
|
||||
// a scheme is any sequence of 2–32 characters
|
||||
// beginning with an ASCII letter
|
||||
@@ -259,6 +259,7 @@ namespace Markdig.Helpers
|
||||
break;
|
||||
}
|
||||
builder.Append(c);
|
||||
pc = c;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -560,10 +561,6 @@ namespace Markdig.Helpers
|
||||
{
|
||||
if (!hasEscape)
|
||||
{
|
||||
if (openedParent > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
openedParent++;
|
||||
}
|
||||
}
|
||||
@@ -596,7 +593,13 @@ namespace Markdig.Helpers
|
||||
|
||||
hasEscape = false;
|
||||
|
||||
if (c == '\0' || c.IsSpaceOrTab() || c.IsControl()) // TODO: specs unclear. space is strict or relaxed? (includes tabs?)
|
||||
if (IsEndOfUri(c))
|
||||
{
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == '.' && IsEndOfUri(text.PeekChar()))
|
||||
{
|
||||
isValid = true;
|
||||
break;
|
||||
@@ -606,6 +609,11 @@ namespace Markdig.Helpers
|
||||
|
||||
c = text.NextChar();
|
||||
}
|
||||
|
||||
if (openedParent > 0)
|
||||
{
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
link = isValid ? buffer.ToString() : null;
|
||||
@@ -613,6 +621,11 @@ namespace Markdig.Helpers
|
||||
return isValid;
|
||||
}
|
||||
|
||||
private static bool IsEndOfUri(char c)
|
||||
{
|
||||
return c == '\0' || c.IsSpaceOrTab() || c.IsControl(); // TODO: specs unclear. space is strict or relaxed? (includes tabs?)
|
||||
}
|
||||
|
||||
public static bool TryParseLinkReferenceDefinition<T>(T text, out string label, out string url,
|
||||
out string title) where T : ICharIterator
|
||||
{
|
||||
|
||||
@@ -101,5 +101,25 @@ namespace Markdig.Helpers
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces <typeparamref name="TElement"/> with <paramref name="replacement"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TElement">Element type to find in the list</typeparam>
|
||||
/// <param name="replacement">Object to replace this element with</param>
|
||||
/// <returns><c>true</c> if a replacement was made; otherwise <c>false</c>.</returns>
|
||||
public bool Replace<TElement>(T replacement) where TElement : T
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
if (this[i] is TElement)
|
||||
{
|
||||
RemoveAt(i);
|
||||
Insert(i, replacement);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/Markdig/Helpers/StringBuilderExtensions.cs
Normal file
23
src/Markdig/Helpers/StringBuilderExtensions.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System.Text;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for StringBuilder with <see cref="StringSlice"/>
|
||||
/// </summary>
|
||||
public static class StringBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Appends the specified slice to this <see cref="StringBuilder"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="slice">The slice.</param>
|
||||
public static StringBuilder Append(this StringBuilder builder, StringSlice slice)
|
||||
{
|
||||
return builder.Append(slice.Text, slice.Start, slice.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,6 +253,22 @@ namespace Markdig.Helpers
|
||||
return CurrentChar;
|
||||
}
|
||||
|
||||
public char PeekChar()
|
||||
{
|
||||
if (Start + 1 > End)
|
||||
{
|
||||
return '\0';
|
||||
}
|
||||
|
||||
var slice = (StringSlice)lines.Lines[SliceIndex];
|
||||
if (offset + 1 >= slice.Length)
|
||||
{
|
||||
return '\n';
|
||||
}
|
||||
|
||||
return slice[slice.Start + offset + 1];
|
||||
}
|
||||
|
||||
public bool TrimStart()
|
||||
{
|
||||
var c = CurrentChar;
|
||||
|
||||
@@ -112,6 +112,17 @@ namespace Markdig.Helpers
|
||||
return index >= Start && index <= End ? Text[index] : (char) 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Peeks the character immediately after the current <see cref="Start"/> position
|
||||
/// or returns `\0` if after the <see cref="End"/> position.
|
||||
/// </summary>
|
||||
/// <returns>The next character, returns `\0` if none.</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
public char PeekChar()
|
||||
{
|
||||
return PeekChar(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Peeks a character at the specified offset from the current beginning of the string, without taking into account <see cref="Start"/> and <see cref="End"/>
|
||||
/// </summary>
|
||||
@@ -168,6 +179,17 @@ namespace Markdig.Helpers
|
||||
return i == text.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Matches the specified text using lowercase comparison.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns><c>true</c> if the text matches; <c>false</c> otherwise</returns>
|
||||
public bool MatchLowercase(string text, int offset = 0)
|
||||
{
|
||||
return MatchLowercase(text, End, offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Matches the specified text using lowercase comparison.
|
||||
/// </summary>
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace Markdig
|
||||
/// <summary>
|
||||
/// Setups this extension for the specified renderer.
|
||||
/// </summary>
|
||||
/// <param name="pipeline">The pipeline used to parse the document.</param>
|
||||
/// <param name="renderer">The renderer.</param>
|
||||
void Setup(IMarkdownRenderer renderer);
|
||||
void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer);
|
||||
}
|
||||
}
|
||||
101
src/Markdig/Markdig.csproj
Normal file
101
src/Markdig/Markdig.csproj
Normal file
@@ -0,0 +1,101 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>A fast, powerfull, CommonMark compliant, extensible Markdown processor for .NET with 20+ builtin extensions (pipetables, footnotes, definition lists... etc.)</Description>
|
||||
<Copyright>Alexandre Mutel</Copyright>
|
||||
<AssemblyTitle>Markdig</AssemblyTitle>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<VersionPrefix>0.14.2</VersionPrefix>
|
||||
<Authors>Alexandre Mutel</Authors>
|
||||
<TargetFrameworks>net35;net40;portable40-net40+sl5+win8+wp8+wpa81;netstandard1.1;uap10.0</TargetFrameworks>
|
||||
<AssemblyName>Markdig</AssemblyName>
|
||||
<PackageId>Markdig</PackageId>
|
||||
<PackageId Condition="'$(SignAssembly)' == 'true'">Markdig.Signed</PackageId>
|
||||
<PackageTags>Markdown CommonMark md html md2html</PackageTags>
|
||||
<PackageReleaseNotes>
|
||||
> 0.14.2
|
||||
- Fix issue with emphasis preceded/followed by an HTML entity (#157)
|
||||
- Add support for link reference definitions for Normalize renderer (#155)
|
||||
- Add option to disable smiley parsing in EmojiAndSmiley extension
|
||||
> 0.14.1
|
||||
- Fix crash in Markdown.Normalize to handle HtmlBlock correctly
|
||||
- Add better handling of bullet character for lists in Markdown.Normalize
|
||||
> 0.14.0
|
||||
- Add Markdown.ToPlainText, Add option HtmlRenderer.EnableHtmlForBlock.
|
||||
- Add Markdown.Normalize, to allow to normalize a markdown document. Add NormalizeRenderer, to render a MarkdownDocument back to markdown.
|
||||
-
|
||||
> 0.13.4
|
||||
- Add support for single table header row without a table body rows (#141)
|
||||
- ADd support for `nomnoml` diagrams
|
||||
</PackageReleaseNotes>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/lunet-io/markdig/master/img/markdig.png</PackageIconUrl>
|
||||
<PackageProjectUrl>https://github.com/lunet-io/markdig</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/lunet-io/markdig/blob/master/license.txt</PackageLicenseUrl>
|
||||
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.1' ">1.6.0</NetStandardImplicitPackageVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net35' ">
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'portable40-net40+sl5+win8+wp8+wpa81' ">
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net35' ">
|
||||
<DefineConstants>$(DefineConstants);SUPPORT_FIXED_STRING;SUPPORT_UNSAFE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net40' ">
|
||||
<DefineConstants>$(DefineConstants);SUPPORT_FIXED_STRING;SUPPORT_UNSAFE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard1.1' ">
|
||||
<DefineConstants>$(DefineConstants);NETSTANDARD_11;SUPPORT_UNSAFE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'uap10.0' ">
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion Condition="'$(TargetPlatformVersion)' == ''">10.0.10240.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion Condition="'$(TargetPlatformMinVersion)' == ''">10.0.10240.0</TargetPlatformMinVersion>
|
||||
<DefineConstants>$(DefineConstants);NETSTANDARD_11;SUPPORT_UNSAFE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)' == 'portable40-net40+sl5+win8+wp8+wpa81'">
|
||||
<TargetFrameworkIdentifier>.NETPortable</TargetFrameworkIdentifier>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkProfile>Profile328</TargetFrameworkProfile>
|
||||
<LanguageTargets>$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets</LanguageTargets>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(SignAssembly)' == 'true' ">
|
||||
<AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
|
||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Special packages and imports for UWP support -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MSBuild.Sdk.Extras" Version="1.0.9" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'uap10.0' ">
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform " Version="5.2.2" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildSDKExtrasTargets)" Condition="Exists('$(MSBuildSDKExtrasTargets)')" />
|
||||
</Project>
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>8A58A7E2-627C-4F41-933F-5AC92ADFAB48</ProjectGuid>
|
||||
<RootNamespace>Markdig</RootNamespace>
|
||||
<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)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
@@ -1,2 +0,0 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=syntax_005Cinlines/@EntryIndexedValue">False</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -1,11 +1,13 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Markdig.Extensions.SelfPipeline;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Normalize;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig
|
||||
@@ -15,6 +17,50 @@ namespace Markdig
|
||||
/// </summary>
|
||||
public static partial class Markdown
|
||||
{
|
||||
#if NETSTANDARD_11
|
||||
public static readonly string Version = typeof(Markdown).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyFileVersionAttribute>().Version;
|
||||
#else
|
||||
public static readonly string Version = ((AssemblyFileVersionAttribute) typeof(Markdown).Assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false)[0]).Version;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the specified markdown to a normalized markdown text.
|
||||
/// </summary>
|
||||
/// <param name="markdown">The markdown.</param>
|
||||
/// <param name="options">The normalize options</param>
|
||||
/// <param name="pipeline">The pipeline.</param>
|
||||
/// <returns>A normalized markdown text.</returns>
|
||||
public static string Normalize(string markdown, NormalizeOptions options = null, MarkdownPipeline pipeline = null)
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
Normalize(markdown, writer, options, pipeline);
|
||||
return writer.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the specified markdown to a normalized markdown text.
|
||||
/// </summary>
|
||||
/// <param name="markdown">The markdown.</param>
|
||||
/// <param name="writer">The destination <see cref="TextWriter"/> that will receive the result of the conversion.</param>
|
||||
/// <param name="options">The normalize options</param>
|
||||
/// <param name="pipeline">The pipeline.</param>
|
||||
/// <returns>A normalized markdown text.</returns>
|
||||
public static MarkdownDocument Normalize(string markdown, TextWriter writer, NormalizeOptions options = null, MarkdownPipeline pipeline = null)
|
||||
{
|
||||
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
|
||||
pipeline = CheckForSelfPipeline(pipeline, markdown);
|
||||
|
||||
// We override the renderer with our own writer
|
||||
var renderer = new NormalizeRenderer(writer, options);
|
||||
pipeline.Setup(renderer);
|
||||
|
||||
var document = Parse(markdown, pipeline);
|
||||
renderer.Render(document);
|
||||
writer.Flush();
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Markdown string to HTML.
|
||||
/// </summary>
|
||||
@@ -112,5 +158,50 @@ namespace Markdig
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Markdown string to Plain text and output to the specified writer.
|
||||
/// </summary>
|
||||
/// <param name="markdown">A Markdown text.</param>
|
||||
/// <param name="writer">The destination <see cref="TextWriter"/> that will receive the result of the conversion.</param>
|
||||
/// <param name="pipeline">The pipeline used for the conversion.</param>
|
||||
/// <returns>The Markdown document that has been parsed</returns>
|
||||
/// <exception cref="System.ArgumentNullException">if reader or writer variable are null</exception>
|
||||
public static MarkdownDocument ToPlainText(string markdown, TextWriter writer, MarkdownPipeline pipeline = null)
|
||||
{
|
||||
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
|
||||
if (writer == null) throw new ArgumentNullException(nameof(writer));
|
||||
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
|
||||
pipeline = CheckForSelfPipeline(pipeline, markdown);
|
||||
|
||||
// We override the renderer with our own writer
|
||||
var renderer = new HtmlRenderer(writer)
|
||||
{
|
||||
EnableHtmlForBlock = false,
|
||||
EnableHtmlForInline = false
|
||||
};
|
||||
pipeline.Setup(renderer);
|
||||
|
||||
var document = Parse(markdown, pipeline);
|
||||
renderer.Render(document);
|
||||
writer.Flush();
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Markdown string to HTML.
|
||||
/// </summary>
|
||||
/// <param name="markdown">A Markdown text.</param>
|
||||
/// <param name="pipeline">The pipeline used for the conversion.</param>
|
||||
/// <returns>The result of the conversion</returns>
|
||||
/// <exception cref="System.ArgumentNullException">if markdown variable is null</exception>
|
||||
public static string ToPlainText(string markdown, MarkdownPipeline pipeline = null)
|
||||
{
|
||||
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
|
||||
var writer = new StringWriter();
|
||||
ToPlainText(markdown, writer, pipeline);
|
||||
return writer.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user