mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-04 05:44:50 +00:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7fd04d14c | ||
|
|
2147e434f7 | ||
|
|
7bd00d115f | ||
|
|
80ef9d2799 | ||
|
|
d6a705d76c | ||
|
|
42472085a6 | ||
|
|
3897e875ee | ||
|
|
3628dc3b17 | ||
|
|
89ff42805d | ||
|
|
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 |
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
|
||||
@@ -29,7 +29,12 @@ build:
|
||||
project: src/Markdig.sln
|
||||
verbosity: minimal
|
||||
before_package:
|
||||
- cmd: msbuild /t:pack /p:VersionSuffix="%MARKDIG_VERSION_SUFFIX%" /p:Configuration=Release Markdig/Markdig.csproj
|
||||
- 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
|
||||
|
||||
57
readme.md
57
readme.md
@@ -14,54 +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 (0.27)](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))
|
||||
- **Auto-links** generates links if a text starts with `http://` or `https://` or `ftp://` or `mailto:` or `www.xxx.yyy`
|
||||
- **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:
|
||||
@@ -73,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
|
||||
@@ -192,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,18 +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=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">C:\Code\lunet\markdig\src\Markdig.Benchmarks\project.lock.json</ProjectAssetsFile>
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\alexa\.nuget\packages\</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">ProjectJson</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">4.1.0</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
</PropertyGroup>
|
||||
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<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"
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
<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" />
|
||||
@@ -87,6 +88,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" />
|
||||
|
||||
@@ -77,3 +77,24 @@ Abbreviations should match whole word only, even if the word is the entire conte
|
||||
.
|
||||
<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>
|
||||
````````````````````````````````
|
||||
@@ -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
|
||||
@@ -31,9 +31,9 @@ We all need :), it makes us :muscle:. (and :ok_hand:).
|
||||
Sentences can end with Emoji:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a sentance :ok_hand:
|
||||
This is a sentence :ok_hand:
|
||||
and keeps going to the next line :)
|
||||
.
|
||||
<p>This is a sentance 👌
|
||||
<p>This is a sentence 👌
|
||||
and keeps going to the next line 😃</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):
|
||||
|
||||
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>
|
||||
````````````````````````````````
|
||||
@@ -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>
|
||||
````````````````````````````````
|
||||
@@ -513,6 +513,39 @@ 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
|
||||
|
||||
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,9 +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/791b1c121f16d3d7e80837c6f52917e57bbb2f61/spec.txt", string.Empty), // 0.27
|
||||
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 +62,7 @@ SOFTWARE.
|
||||
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
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Markdig.Extensions.JiraLinks;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
@@ -71,8 +72,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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -157,6 +174,37 @@ Paragraph
|
||||
</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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -193,25 +195,41 @@ namespace Markdig.Extensions.Abbreviations
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -440,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
|
||||
@@ -532,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;
|
||||
@@ -606,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.
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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++)
|
||||
|
||||
@@ -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,10 +18,10 @@ 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)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
@@ -47,7 +47,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
else if (hasLetter)
|
||||
{
|
||||
if (IsReservedPunctuation(c))
|
||||
if (IsReservedPunctuation(c, discardDots))
|
||||
{
|
||||
if (previousIsSpace)
|
||||
{
|
||||
@@ -67,7 +67,7 @@ namespace Markdig.Helpers
|
||||
else if (!previousIsSpace && c.IsWhitespace())
|
||||
{
|
||||
var pc = headingBuffer[headingBuffer.Length - 1];
|
||||
if (!IsReservedPunctuation(pc))
|
||||
if (!IsReservedPunctuation(pc, discardDots))
|
||||
{
|
||||
headingBuffer.Append('-');
|
||||
}
|
||||
@@ -81,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--;
|
||||
}
|
||||
@@ -97,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)
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
@@ -612,6 +609,11 @@ namespace Markdig.Helpers
|
||||
|
||||
c = text.NextChar();
|
||||
}
|
||||
|
||||
if (openedParent > 0)
|
||||
{
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
link = isValid ? buffer.ToString() : null;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,18 +5,26 @@
|
||||
<Copyright>Alexandre Mutel</Copyright>
|
||||
<AssemblyTitle>Markdig</AssemblyTitle>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<VersionPrefix>0.11.0</VersionPrefix>
|
||||
<VersionPrefix>0.13.4</VersionPrefix>
|
||||
<Authors>Alexandre Mutel</Authors>
|
||||
<TargetFrameworks>net35;net40;portable40-net40+sl5+win8+wp8+wpa81;netstandard1.1</TargetFrameworks>
|
||||
<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.11.0
|
||||
- Fix issue with math extension and $$ block parsing not handling correctly beginning of a $$ as a inline math instead (issue #107)
|
||||
- Fix issue with custom attributes for emphasis
|
||||
- Add support for new special custom arrows emoji (-> <- <-> <= => and <==>)
|
||||
</PackageReleaseNotes>
|
||||
> 0.13.4
|
||||
- Add support for single table header row without a table body rows (#141)
|
||||
- ADd support for `nomnoml` diagrams
|
||||
> 0.13.3
|
||||
- Add support for Pandoc YAML frontmatter (#138)
|
||||
> 0.13.2
|
||||
- Add support for UAP10.0 (#137)
|
||||
> 0.13.1
|
||||
- Fix indenting issue after a double digit list block using a tab (#134)
|
||||
> 0.13.0
|
||||
- Update to latest CommonMark specs 0.28
|
||||
</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>
|
||||
@@ -44,22 +52,47 @@
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net35' ">
|
||||
<DefineConstants>$(DefineConstants);SUPPORT_FIXED_STRING</DefineConstants>
|
||||
<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</DefineConstants>
|
||||
<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>
|
||||
|
||||
@@ -18,6 +18,7 @@ using Markdig.Extensions.Footers;
|
||||
using Markdig.Extensions.Footnotes;
|
||||
using Markdig.Extensions.GenericAttributes;
|
||||
using Markdig.Extensions.Hardlines;
|
||||
using Markdig.Extensions.JiraLinks;
|
||||
using Markdig.Extensions.ListExtras;
|
||||
using Markdig.Extensions.Mathematics;
|
||||
using Markdig.Extensions.MediaLinks;
|
||||
@@ -426,6 +427,21 @@ namespace Markdig
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Automatically link references to JIRA issues
|
||||
/// </summary>
|
||||
/// <param name="pipeline">The pipeline</param>
|
||||
/// <param name="options">Set of required options</param>
|
||||
/// <returns>The modified pipeline</returns>
|
||||
public static MarkdownPipelineBuilder UseJiraLinks(this MarkdownPipelineBuilder pipeline, JiraLinkOptions options)
|
||||
{
|
||||
if (!pipeline.Extensions.Contains<JiraLinkExtension>())
|
||||
{
|
||||
pipeline.Extensions.Add(new JiraLinkExtension(options));
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will disable the HTML support in the markdown processor (for constraint/safe parsing).
|
||||
/// </summary>
|
||||
|
||||
@@ -617,12 +617,6 @@ namespace Markdig.Parsers
|
||||
{
|
||||
leaf.AppendLine(ref Line, Column, LineIndex, CurrentLineStartPosition);
|
||||
}
|
||||
|
||||
if (NewBlocks.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The NewBlocks is not empty. This is happening if a LeafBlock is not the last to be pushed");
|
||||
}
|
||||
}
|
||||
|
||||
// A block is open only if it has a Continue state.
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace Markdig.Parsers
|
||||
}
|
||||
|
||||
// Cannot start with </script </pre or </style
|
||||
if ((tagIndex == 45 || tagIndex == 46 || tagIndex == 49))
|
||||
if ((tagIndex == 50 || tagIndex == 51 || tagIndex == 54))
|
||||
{
|
||||
if (c == '/' || hasLeadingClose)
|
||||
{
|
||||
@@ -281,67 +281,72 @@ namespace Markdig.Parsers
|
||||
|
||||
private static readonly string[] HtmlTags =
|
||||
{
|
||||
"address", // 0
|
||||
"article", // 1
|
||||
"aside", // 2
|
||||
"base", // 3
|
||||
"basefont", // 4
|
||||
"blockquote", // 5
|
||||
"body", // 6
|
||||
"caption", // 7
|
||||
"center", // 8
|
||||
"col", // 9
|
||||
"colgroup", // 10
|
||||
"dd", // 11
|
||||
"details", // 12
|
||||
"dialog", // 13
|
||||
"dir", // 14
|
||||
"div", // 15
|
||||
"dl", // 16
|
||||
"dt", // 17
|
||||
"fieldset", // 18
|
||||
"figcaption", // 19
|
||||
"figure", // 20
|
||||
"footer", // 21
|
||||
"form", // 22
|
||||
"frame", // 23
|
||||
"frameset", // 24
|
||||
"h1", // 25
|
||||
"head", // 26
|
||||
"header", // 27
|
||||
"hr", // 28
|
||||
"html", // 29
|
||||
"iframe", // 30
|
||||
"legend", // 31
|
||||
"li", // 32
|
||||
"link", // 33
|
||||
"main", // 34
|
||||
"menu", // 35
|
||||
"menuitem", // 36
|
||||
"meta", // 37
|
||||
"nav", // 38
|
||||
"noframes", // 39
|
||||
"ol", // 40
|
||||
"optgroup", // 41
|
||||
"option", // 42
|
||||
"p", // 43
|
||||
"param", // 44
|
||||
"pre", // 45 <- special group 1
|
||||
"script", // 46 <- special group 1
|
||||
"section", // 47
|
||||
"source", // 48
|
||||
"style", // 49 <- special group 1
|
||||
"summary", // 50
|
||||
"table", // 51
|
||||
"tbody", // 52
|
||||
"td", // 53
|
||||
"tfoot", // 54
|
||||
"th", // 55
|
||||
"thead", // 56
|
||||
"title", // 57
|
||||
"tr", // 58
|
||||
"track", // 59
|
||||
"ul", // 60
|
||||
"address", // 0
|
||||
"article", // 1
|
||||
"aside", // 2
|
||||
"base", // 3
|
||||
"basefont", // 4
|
||||
"blockquote", // 5
|
||||
"body", // 6
|
||||
"caption", // 7
|
||||
"center", // 8
|
||||
"col", // 9
|
||||
"colgroup", // 10
|
||||
"dd", // 11
|
||||
"details", // 12
|
||||
"dialog", // 13
|
||||
"dir", // 14
|
||||
"div", // 15
|
||||
"dl", // 16
|
||||
"dt", // 17
|
||||
"fieldset", // 18
|
||||
"figcaption", // 19
|
||||
"figure", // 20
|
||||
"footer", // 21
|
||||
"form", // 22
|
||||
"frame", // 23
|
||||
"frameset", // 24
|
||||
"h1", // 25
|
||||
"h2", // 26
|
||||
"h3", // 27
|
||||
"h4", // 28
|
||||
"h5", // 29
|
||||
"h6", // 30
|
||||
"head", // 31
|
||||
"header", // 32
|
||||
"hr", // 33
|
||||
"html", // 34
|
||||
"iframe", // 35
|
||||
"legend", // 36
|
||||
"li", // 37
|
||||
"link", // 38
|
||||
"main", // 39
|
||||
"menu", // 40
|
||||
"menuitem", // 41
|
||||
"meta", // 42
|
||||
"nav", // 43
|
||||
"noframes", // 44
|
||||
"ol", // 45
|
||||
"optgroup", // 46
|
||||
"option", // 47
|
||||
"p", // 48
|
||||
"param", // 49
|
||||
"pre", // 50 <=== special group 1
|
||||
"script", // 51 <=== special group 1
|
||||
"section", // 52
|
||||
"source", // 53
|
||||
"style", // 54 <=== special group 1
|
||||
"summary", // 55
|
||||
"table", // 56
|
||||
"tbody", // 57
|
||||
"td", // 58
|
||||
"tfoot", // 59
|
||||
"th", // 60
|
||||
"thead", // 61
|
||||
"title", // 62
|
||||
"tr", // 63
|
||||
"track", // 64
|
||||
"ul", // 65
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -274,20 +274,6 @@ namespace Markdig.Parsers.Inlines
|
||||
|
||||
var embracer = (ContainerInline)openDelimiter;
|
||||
|
||||
// Go down to the first emphasis with a lower level
|
||||
while (true)
|
||||
{
|
||||
var previousEmphasis = embracer.FirstChild as EmphasisInline;
|
||||
if (previousEmphasis != null && previousEmphasis.IsDouble && !isStrong && embracer.FirstChild == embracer.LastChild)
|
||||
{
|
||||
embracer = previousEmphasis;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy attributes attached to delimiter to the emphasis
|
||||
var attributes = closeDelimiter.TryGetAttributes();
|
||||
if (attributes != null)
|
||||
|
||||
@@ -230,17 +230,16 @@ namespace Markdig.Parsers
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
// We require at least one char
|
||||
state.NextColumn();
|
||||
|
||||
// Parse the following indent
|
||||
state.RestartIndent();
|
||||
var columnBeforeIndent = state.Column;
|
||||
state.ParseIndent();
|
||||
|
||||
if (state.IsCodeIndent)
|
||||
// We expect at most 4 columns after
|
||||
// If we have more, we reset the position
|
||||
if (state.Indent > 4)
|
||||
{
|
||||
state.GoToColumn(columnBeforeIndent);
|
||||
state.GoToColumn(columnBeforeIndent + 1);
|
||||
}
|
||||
|
||||
// Number of spaces required for the following content to be part of this list item
|
||||
|
||||
BIN
src/Markdig/key.snk
Normal file
BIN
src/Markdig/key.snk
Normal file
Binary file not shown.
@@ -1,10 +1,11 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26403.7
|
||||
VisualStudioVersion = 15.0.26730.16
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{061866E2-005C-4D13-A338-EA464BBEC60F}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\appveyor.yml = ..\appveyor.yml
|
||||
..\license.txt = ..\license.txt
|
||||
..\readme.md = ..\readme.md
|
||||
EndProjectSection
|
||||
@@ -25,6 +26,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig.WebApp", "Markdig.W
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnicodeNormDApp", "UnicodeNormDApp\UnicodeNormDApp.csproj", "{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mdtoc", "mdtoc\mdtoc.csproj", "{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -51,8 +54,15 @@ Global
|
||||
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D068F7B6-6ACC-456C-A2E1-10EA746D956D}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
6
src/mdtoc/App.config
Normal file
6
src/mdtoc/App.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
||||
</startup>
|
||||
</configuration>
|
||||
101
src/mdtoc/Program.cs
Normal file
101
src/mdtoc/Program.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using Markdig;
|
||||
using Markdig.Extensions.AutoIdentifiers;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace mdtoc
|
||||
{
|
||||
/// <summary>
|
||||
/// A tool to generate a markdown TOC from a markdown local file or a github link to a markdown file.
|
||||
/// </summary>
|
||||
class Program
|
||||
{
|
||||
static void Error(string message)
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
if (args.Length != 1 || args[0] == "--help" || args[0] == "-help" || args[0] == "/?" || args[0] == "/help")
|
||||
{
|
||||
Error("Usage: mdtoc [markdown file path | http github URL]");
|
||||
return;
|
||||
}
|
||||
|
||||
var path = args[0];
|
||||
string markdown = null;
|
||||
if (path.StartsWith("https:"))
|
||||
{
|
||||
Uri uri;
|
||||
if (!Uri.TryCreate(path, UriKind.Absolute, out uri))
|
||||
{
|
||||
Error($"Unable to parse Uri `{path}`");
|
||||
return;
|
||||
}
|
||||
// Special handling of github URL to access the raw content instead
|
||||
if (uri.Host == "github.com")
|
||||
{
|
||||
// https://github.com/lunet-io/scriban/blob/master/doc/language.md
|
||||
// https://raw.githubusercontent.com/lunet-io/scriban/master/doc/language.md
|
||||
var newPath = uri.AbsolutePath;
|
||||
var paths = new List<string>(newPath.Split(new char[] {'/'}, StringSplitOptions.RemoveEmptyEntries));
|
||||
if (paths.Count < 5 || paths[2] != "blob")
|
||||
{
|
||||
Error($"Invalid github.com URL `{path}`");
|
||||
return;
|
||||
}
|
||||
paths.RemoveAt(2); // remove blob
|
||||
uri = new Uri($"https://raw.githubusercontent.com/{(string.Join("/", paths))}");
|
||||
}
|
||||
|
||||
var httpClient = new HttpClient();
|
||||
markdown = httpClient.GetStringAsync(uri).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
markdown = File.ReadAllText(path);
|
||||
}
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAutoIdentifiers(AutoIdentifierOptions.GitHub).Build();
|
||||
var doc = Markdown.Parse(markdown, pipeline);
|
||||
|
||||
// Precomputes the minHeading
|
||||
var headings = doc.Descendants<HeadingBlock>().ToList();
|
||||
int minHeading = int.MaxValue;
|
||||
int maxHeading = int.MinValue;
|
||||
foreach (var heading in headings)
|
||||
{
|
||||
minHeading = Math.Min(minHeading, heading.Level);
|
||||
maxHeading = Math.Max(maxHeading, heading.Level);
|
||||
}
|
||||
|
||||
var writer = Console.Out;
|
||||
// Use this htmlWriter to write content of headings into link label
|
||||
var htmlWriter = new HtmlRenderer(writer) {EnableHtmlForInline = true};
|
||||
foreach (var heading in headings)
|
||||
{
|
||||
var indent = heading.Level - minHeading;
|
||||
for (int i = 0; i < indent; i++)
|
||||
{
|
||||
// - Start Of Heading
|
||||
writer.Write(" ");
|
||||
}
|
||||
writer.Write("- [");
|
||||
htmlWriter.WriteLeafInline(heading);
|
||||
writer.Write($"](#{heading.GetAttributes().Id})");
|
||||
writer.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/mdtoc/Properties/AssemblyInfo.cs
Normal file
39
src/mdtoc/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
// 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.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("mdtoc")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Alexandre Mutel")]
|
||||
[assembly: AssemblyProduct("mdtoc")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017 - Alexandre Mutel")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("e3cdff0f-5bfc-42e9-bdba-2797651900a2")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
58
src/mdtoc/mdtoc.csproj
Normal file
58
src/mdtoc/mdtoc.csproj
Normal file
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>mdtoc</RootNamespace>
|
||||
<AssemblyName>mdtoc</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</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" />
|
||||
</Project>
|
||||
Reference in New Issue
Block a user