mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-04 13:54:44 +00:00
Compare commits
223 Commits
example-lu
...
0.41.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09a4b81a6e | ||
|
|
7b14e2e091 | ||
|
|
1e17dcdd08 | ||
|
|
40e5ab1514 | ||
|
|
2953b026fc | ||
|
|
42ab98968d | ||
|
|
b15cf582a5 | ||
|
|
61e9be290b | ||
|
|
a9ce0eb438 | ||
|
|
023d93c091 | ||
|
|
bbefce3b1f | ||
|
|
0d6343b421 | ||
|
|
f4effc25c0 | ||
|
|
7a83a1fd3d | ||
|
|
8269ff1af5 | ||
|
|
0e6d0f4cb2 | ||
|
|
8484420b72 | ||
|
|
c82a36884d | ||
|
|
da3d7f4f3a | ||
|
|
eceb70c16a | ||
|
|
7a9c192d7d | ||
|
|
8cfa0cf0ae | ||
|
|
a82c3bd705 | ||
|
|
ecfda373b9 | ||
|
|
d8f69218db | ||
|
|
adfcf42529 | ||
|
|
dab1ca5483 | ||
|
|
55f770cc07 | ||
|
|
8b84542527 | ||
|
|
086440bcd3 | ||
|
|
97470bd61f | ||
|
|
90c73b7754 | ||
|
|
ee403ce28f | ||
|
|
8b403918b9 | ||
|
|
39b07d6bc5 | ||
|
|
fb3fe8b261 | ||
|
|
abb19ecf37 | ||
|
|
9dac60df73 | ||
|
|
148278417f | ||
|
|
5b32391348 | ||
|
|
5528023158 | ||
|
|
f93b9d79d9 | ||
|
|
d53fd0e870 | ||
|
|
c488aca96c | ||
|
|
9b3f442765 | ||
|
|
7b6d659bbd | ||
|
|
bc8ba4fecb | ||
|
|
d87bb7292d | ||
|
|
118d28f886 | ||
|
|
3e0c72f043 | ||
|
|
f2590e7b80 | ||
|
|
88c5b5cb41 | ||
|
|
d1233ffe66 | ||
|
|
ab8e85b06e | ||
|
|
90bc15c016 | ||
|
|
7f604bef30 | ||
|
|
54783b8f65 | ||
|
|
ad0770a594 | ||
|
|
90365bfeee | ||
|
|
c35f7fff17 | ||
|
|
fdaef77474 | ||
|
|
733c028311 | ||
|
|
bc41b0c2a3 | ||
|
|
a8de2087d8 | ||
|
|
2cff6c5194 | ||
|
|
5e4a917dbd | ||
|
|
aff8a6823a | ||
|
|
b8a3c270cc | ||
|
|
68659f4037 | ||
|
|
e92a8097d0 | ||
|
|
57fad6fc1a | ||
|
|
260f4d5acc | ||
|
|
102d02a6c1 | ||
|
|
5ae8ab7a74 | ||
|
|
eb28f76588 | ||
|
|
d0311b4cea | ||
|
|
a11899a350 | ||
|
|
40781737c3 | ||
|
|
455f8f333d | ||
|
|
98a060f2a3 | ||
|
|
49cf59b819 | ||
|
|
310a55c724 | ||
|
|
f734e91568 | ||
|
|
090e6d791a | ||
|
|
41bdb0f0ab | ||
|
|
b27ef11240 | ||
|
|
dfa2c94b88 | ||
|
|
89330f3524 | ||
|
|
1a1bbecc46 | ||
|
|
68bd3074b2 | ||
|
|
e486903687 | ||
|
|
8e22754db4 | ||
|
|
93d88ab994 | ||
|
|
000393f46a | ||
|
|
a5796890e1 | ||
|
|
c19ba5b0eb | ||
|
|
03390e4f71 | ||
|
|
42bd65caaf | ||
|
|
b7ae04bdba | ||
|
|
391f376fa2 | ||
|
|
f9e96bc9c9 | ||
|
|
c75a11ec32 | ||
|
|
fd226d53e9 | ||
|
|
7132584996 | ||
|
|
f48331d6c7 | ||
|
|
6549d3b726 | ||
|
|
d434f00355 | ||
|
|
b62a12d32d | ||
|
|
3c7edaa82d | ||
|
|
fcb56fb037 | ||
|
|
50bc6cadfc | ||
|
|
201aa4ef73 | ||
|
|
adce9797d6 | ||
|
|
fb71dff0ec | ||
|
|
399570941a | ||
|
|
2f903697e2 | ||
|
|
eb8fe15679 | ||
|
|
8f008e45ca | ||
|
|
0014ec4138 | ||
|
|
2ca05ccad7 | ||
|
|
6a15c804bc | ||
|
|
0446959623 | ||
|
|
e6afddbaa0 | ||
|
|
a377239e91 | ||
|
|
35aa304faf | ||
|
|
e4568979ec | ||
|
|
3470ec0d54 | ||
|
|
113ef7f215 | ||
|
|
4cb4b68883 | ||
|
|
64ae344b74 | ||
|
|
b5f3c9fc67 | ||
|
|
8a88fd0557 | ||
|
|
cc7623989d | ||
|
|
b6a7acf5fc | ||
|
|
804a6f0dbc | ||
|
|
342e264988 | ||
|
|
f52ecee0b9 | ||
|
|
a092ec23b3 | ||
|
|
6f1dce6306 | ||
|
|
040a778d87 | ||
|
|
2ae2cf9263 | ||
|
|
ba1e562d2f | ||
|
|
65a02e44ec | ||
|
|
e78833ae30 | ||
|
|
2ab716bec1 | ||
|
|
feeb1867ce | ||
|
|
f3aa7e73e3 | ||
|
|
dce5572356 | ||
|
|
dbbabd2221 | ||
|
|
22145c2fb0 | ||
|
|
2517003edc | ||
|
|
50a3d02c2c | ||
|
|
40fb2b8249 | ||
|
|
5c54968807 | ||
|
|
58ea46d58b | ||
|
|
f557e57ab1 | ||
|
|
87aa32e1bd | ||
|
|
4f1cb9da08 | ||
|
|
5cff880c90 | ||
|
|
c7aec822b0 | ||
|
|
b0bde46cc1 | ||
|
|
7803417e5c | ||
|
|
047c4cbcbb | ||
|
|
e4f57ca21e | ||
|
|
1f1364e69b | ||
|
|
4eea9db35c | ||
|
|
cce7284b84 | ||
|
|
8e1e0b9bf3 | ||
|
|
7d40bc118b | ||
|
|
dba94a2371 | ||
|
|
6d75eed3bb | ||
|
|
ccb75fd5f0 | ||
|
|
06eb6ba774 | ||
|
|
f15e9f020e | ||
|
|
a70ca6304f | ||
|
|
d26822be05 | ||
|
|
5e3416f8b7 | ||
|
|
012a57d361 | ||
|
|
053a18c684 | ||
|
|
13265453ac | ||
|
|
8ea0783834 | ||
|
|
3d29430337 | ||
|
|
81bc58c6c9 | ||
|
|
bfe3800130 | ||
|
|
b7cb169fd3 | ||
|
|
512b28256a | ||
|
|
cd5d11eeff | ||
|
|
a9118774a8 | ||
|
|
8155a1e3d6 | ||
|
|
0a167248fd | ||
|
|
9df67b7934 | ||
|
|
8f8a145f0e | ||
|
|
a6cd283183 | ||
|
|
851713fad9 | ||
|
|
9422764f98 | ||
|
|
74f978ed2d | ||
|
|
66aaffaef1 | ||
|
|
a18d8dee4f | ||
|
|
96934214db | ||
|
|
ca03b7df4f | ||
|
|
4660426719 | ||
|
|
7ef2959d6e | ||
|
|
78c4efb9cc | ||
|
|
641f3fe0c1 | ||
|
|
83145a323b | ||
|
|
1abc804228 | ||
|
|
25323f080c | ||
|
|
38a15ef2a9 | ||
|
|
f9bed5d270 | ||
|
|
3f7d5c68c2 | ||
|
|
936fe35460 | ||
|
|
0dc4857213 | ||
|
|
956f109419 | ||
|
|
b1f3229812 | ||
|
|
3ceff1d076 | ||
|
|
e2e557b1c4 | ||
|
|
e155792be5 | ||
|
|
98c687b4ed | ||
|
|
8e4a732efe | ||
|
|
bce4b70dc6 | ||
|
|
1f71520de9 | ||
|
|
bfd7b6460c | ||
|
|
0e26ec5382 |
27
.github/workflows/ci.yml
vendored
27
.github/workflows/ci.yml
vendored
@@ -11,22 +11,11 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install .NET 6.0
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
- name: Build, Test, Pack, Publish
|
||||
shell: bash
|
||||
run: |
|
||||
dotnet tool install -g dotnet-releaser
|
||||
dotnet-releaser run --nuget-token "${{secrets.NUGET_TOKEN}}" --github-token "${{secrets.GITHUB_TOKEN}}" src/dotnet-releaser.toml
|
||||
uses: xoofx/.github/.github/workflows/dotnet.yml@main
|
||||
with:
|
||||
dotnet-version: |
|
||||
6.0
|
||||
8.0
|
||||
9.0
|
||||
secrets:
|
||||
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
|
||||
7
.github/workflows/nuget_org_only.config
vendored
Normal file
7
.github/workflows/nuget_org_only.config
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
36
contributing.md
Normal file
36
contributing.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# How to Contribute
|
||||
|
||||
Thanks for your interest in contributing to `Markdig`! Here are a few general guidelines on contributing and
|
||||
reporting bugs that we ask you to review. Following these guidelines helps to communicate that you respect the time of
|
||||
the contributors managing and developing this open source project.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
Before reporting a new issue, please ensure that the issue was not already reported or fixed by searching through our
|
||||
[issues list](https://github.com/xoofx/markdig/issues).
|
||||
|
||||
When creating a new issue, please be sure to include a **title and clear description**, as much relevant information as
|
||||
possible, and, if possible, a test case.
|
||||
|
||||
## Sending Pull Requests
|
||||
|
||||
Before sending a new pull request, take a look at existing pull requests and issues to see if the proposed change or fix
|
||||
has been discussed in the past, or if the change was already implemented but not yet released.
|
||||
|
||||
We expect new pull requests to include tests for any affected behavior, and, as we follow semantic versioning, we may
|
||||
reserve breaking changes until the next major version release.
|
||||
|
||||
## Other Ways to Contribute
|
||||
|
||||
We welcome anyone that wants to contribute to `Markdig` to triage and reply to open issues to help troubleshoot
|
||||
and fix existing bugs. Here is what you can do:
|
||||
|
||||
- Help ensure that existing issues follows the recommendations from the _[Reporting Issues](#reporting-issues)_ section,
|
||||
providing feedback to the issue's author on what might be missing.
|
||||
instructions and code samples.
|
||||
- Review existing pull requests, and testing patches against real existing applications that use `Markdig`.
|
||||
- Write a test, or add a missing test case to an existing test.
|
||||
|
||||
Thanks again for your interest on contributing to `Markdig`!
|
||||
|
||||
:heart:
|
||||
@@ -1,6 +1,6 @@
|
||||
# Extensions and Parsers
|
||||
|
||||
Markdig was [implemented in such a way](http://xoofx.com/blog/2016/06/13/implementing-a-markdown-processor-for-dotnet/) as to be extremely pluggable, with even basic behaviors being mutable and extendable.
|
||||
Markdig was [implemented in such a way](http://xoofx.github.io/blog/2016/06/13/implementing-a-markdown-processor-for-dotnet/) as to be extremely pluggable, with even basic behaviors being mutable and extendable.
|
||||
|
||||
The basic mechanism for extension of Markdig is the `IMarkdownExtension` interface, which allows any implementing class to be registered with the pipeline builder and thus to directly modify the collections of `BlockParser` and `InlineParser` objects which end up in the pipeline.
|
||||
|
||||
|
||||
24
readme.md
24
readme.md
@@ -1,4 +1,4 @@
|
||||
# Markdig [](https://github.com/lunet-io/markdig/actions) [](https://coveralls.io/github/xoofx/markdig?branch=master) [](https://www.nuget.org/packages/Markdig/) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRGHXBTP442JL)
|
||||
# Markdig [](https://github.com/xoofx/markdig/actions/workflows/ci.yml) [](https://coveralls.io/github/xoofx/markdig?branch=master) [](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">
|
||||
|
||||
@@ -12,9 +12,9 @@ You can **try Markdig online** and compare it to other implementations on [babel
|
||||
|
||||
- **Very fast parser and html renderer** (no-regexp), very lightweight in terms of GC pressure. See benchmarks
|
||||
- **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!
|
||||
- Checkout [Markdown Editor v2 for Visual Studio 2022](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.MarkdownEditor2) powered by Markdig!
|
||||
- Converter to **HTML**
|
||||
- Passing more than **600+ tests** from the latest [CommonMark specs (0.30)](http://spec.commonmark.org/)
|
||||
- Passing more than **600+ tests** from the latest [CommonMark specs (0.31.2)](http://spec.commonmark.org/)
|
||||
- Includes all the core elements of CommonMark:
|
||||
- including **GFM fenced code blocks**.
|
||||
- **Extensible** architecture
|
||||
@@ -48,7 +48,7 @@ You can **try Markdig online** and compare it to other implementations on [babel
|
||||
- [**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)
|
||||
- [**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://mermaid.js.org) and [`nomnoml`](https://github.com/skanaar/nomnoml) diagrams)
|
||||
- [**YAML Front Matter**](src/Markdig.Tests/Specs/YamlSpecs.md) to parse without evaluating the front matter and to discard it from the HTML output (typically used for previewing without the front matter 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)
|
||||
- Starting with Markdig version `0.20.0+`, Markdig is compatible only with `NETStandard 2.0`, `NETStandard 2.1`, `NETCoreApp 2.1` and `NETCoreApp 3.1`.
|
||||
@@ -70,7 +70,7 @@ If you are looking for support for an old .NET Framework 3.5 or 4.0, you can dow
|
||||
|
||||
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/)
|
||||
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.github.io/blog/2016/06/13/implementing-a-markdown-processor-for-dotnet/)
|
||||
|
||||
## Download
|
||||
|
||||
@@ -101,6 +101,10 @@ var result = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
|
||||
|
||||
You can have a look at the [MarkdownExtensions](https://github.com/lunet-io/markdig/blob/master/src/Markdig/MarkdownExtensions.cs) that describes all actionable extensions (by modifying the MarkdownPipeline)
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated. For detailed contributing guidelines, please see [contributing.md](contributing.md).
|
||||
|
||||
## Build
|
||||
|
||||
In order to build Markdig, you need to install [.NET 6.0](https://dotnet.microsoft.com/en-us/download)
|
||||
@@ -140,12 +144,12 @@ AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
|
||||
- Markdig is roughly **x100 times faster than MarkdownSharp**
|
||||
- **20% faster than the reference cmark C implementation**
|
||||
|
||||
## Sponsors
|
||||
|
||||
## Donate
|
||||
Supports this project with a monthly donation and help me continue improving it. \[[Become a sponsor](https://github.com/sponsors/xoofx)\]
|
||||
|
||||
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)
|
||||
[<img src="https://github.com/lilith.png?size=200" width="64px;" style="border-radius: 50%" alt="lilith"/>](https://github.com/lilith) Lilith River, author of [Imageflow Server, an easy on-demand
|
||||
image editing, optimization, and delivery server](https://github.com/imazen/imageflow-server)
|
||||
|
||||
## Credits
|
||||
|
||||
@@ -160,4 +164,4 @@ Some decoding part (e.g HTML [EntityHelper.cs](https://github.com/lunet-io/markd
|
||||
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)
|
||||
Alexandre MUTEL aka [xoofx](http://xoofx.github.io)
|
||||
|
||||
@@ -1,40 +1,38 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace Testamina.Markdig.Benchmarks
|
||||
{
|
||||
public static class CommonMarkLib
|
||||
{
|
||||
public static string ToHtml(string text)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var textAsArray = Encoding.UTF8.GetBytes(text);
|
||||
namespace Testamina.Markdig.Benchmarks;
|
||||
|
||||
fixed (void* ptext = textAsArray)
|
||||
public static class CommonMarkLib
|
||||
{
|
||||
public static string ToHtml(string text)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var textAsArray = Encoding.UTF8.GetBytes(text);
|
||||
|
||||
fixed (void* ptext = textAsArray)
|
||||
{
|
||||
var ptr = (byte*)cmark_markdown_to_html(new IntPtr(ptext), text.Length);
|
||||
int length = 0;
|
||||
while (ptr[length] != 0)
|
||||
{
|
||||
var ptr = (byte*)cmark_markdown_to_html(new IntPtr(ptext), text.Length);
|
||||
int length = 0;
|
||||
while (ptr[length] != 0)
|
||||
{
|
||||
length++;
|
||||
}
|
||||
var buffer = new byte[length];
|
||||
Marshal.Copy(new IntPtr(ptr), buffer, 0, length);
|
||||
var result = Encoding.UTF8.GetString(buffer);
|
||||
Marshal.FreeHGlobal(new IntPtr(ptr));
|
||||
return result;
|
||||
length++;
|
||||
}
|
||||
var buffer = new byte[length];
|
||||
Marshal.Copy(new IntPtr(ptr), buffer, 0, length);
|
||||
var result = Encoding.UTF8.GetString(buffer);
|
||||
Marshal.FreeHGlobal(new IntPtr(ptr));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// char *cmark_markdown_to_html(const char *text, size_t len, int options);
|
||||
[DllImport("cmark", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern IntPtr cmark_markdown_to_html(IntPtr charBuffer, int len, int options = 0);
|
||||
}
|
||||
|
||||
// char *cmark_markdown_to_html(const char *text, size_t len, int options);
|
||||
[DllImport("cmark", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern IntPtr cmark_markdown_to_html(IntPtr charBuffer, int len, int options = 0);
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="spec.md" />
|
||||
@@ -18,12 +19,12 @@
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
||||
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.13.1" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.14.0" />
|
||||
<PackageReference Include="CommonMark.NET" Version="0.15.1" />
|
||||
<PackageReference Include="Markdown" Version="2.2.1" />
|
||||
<PackageReference Include="MarkdownSharp" Version="2.0.5" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="2.0.226801" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="3.1.512801" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Markdig\Markdig.csproj" />
|
||||
|
||||
@@ -2,76 +2,75 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
using Markdig;
|
||||
|
||||
|
||||
namespace Testamina.Markdig.Benchmarks
|
||||
namespace Testamina.Markdig.Benchmarks;
|
||||
|
||||
//[BenchmarkTask(platform: BenchmarkPlatform.X64, jitVersion: BenchmarkJitVersion.RyuJit, processCount: 1, warmupIterationCount: 2)]
|
||||
public class Program
|
||||
{
|
||||
//[BenchmarkTask(platform: BenchmarkPlatform.X64, jitVersion: BenchmarkJitVersion.RyuJit, processCount: 1, warmupIterationCount: 2)]
|
||||
public class Program
|
||||
private string text;
|
||||
|
||||
public Program()
|
||||
{
|
||||
private string text;
|
||||
//text = File.ReadAllText("progit.md");
|
||||
text = File.ReadAllText("spec.md");
|
||||
}
|
||||
|
||||
public Program()
|
||||
{
|
||||
//text = File.ReadAllText("progit.md");
|
||||
text = File.ReadAllText("spec.md");
|
||||
}
|
||||
//[Benchmark(Description = "TestMarkdig", OperationsPerInvoke = 4096)]
|
||||
[Benchmark(Description = "markdig")]
|
||||
public void TestMarkdig()
|
||||
{
|
||||
//var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
|
||||
Markdown.ToHtml(text);
|
||||
//File.WriteAllText("spec.html", writer.ToString());
|
||||
}
|
||||
|
||||
//[Benchmark(Description = "TestMarkdig", OperationsPerInvoke = 4096)]
|
||||
[Benchmark(Description = "markdig")]
|
||||
public void TestMarkdig()
|
||||
{
|
||||
//var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
|
||||
Markdown.ToHtml(text);
|
||||
//File.WriteAllText("spec.html", writer.ToString());
|
||||
}
|
||||
[Benchmark(Description = "cmark")]
|
||||
public void TestCommonMarkCpp()
|
||||
{
|
||||
//var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
|
||||
CommonMarkLib.ToHtml(text);
|
||||
//File.WriteAllText("spec.html", writer.ToString());
|
||||
}
|
||||
|
||||
[Benchmark(Description = "cmark")]
|
||||
public void TestCommonMarkCpp()
|
||||
{
|
||||
//var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
|
||||
CommonMarkLib.ToHtml(text);
|
||||
//File.WriteAllText("spec.html", writer.ToString());
|
||||
}
|
||||
[Benchmark(Description = "CommonMark.NET")]
|
||||
public void TestCommonMarkNet()
|
||||
{
|
||||
////var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
|
||||
// var reader = new StringReader(text);
|
||||
//CommonMark.CommonMarkConverter.Parse(reader);
|
||||
//CommonMark.CommonMarkConverter.Parse(reader);
|
||||
//reader.Dispose();
|
||||
//var writer = new StringWriter();
|
||||
CommonMark.CommonMarkConverter.Convert(text);
|
||||
//writer.Flush();
|
||||
//writer.ToString();
|
||||
}
|
||||
|
||||
[Benchmark(Description = "CommonMark.NET")]
|
||||
public void TestCommonMarkNet()
|
||||
{
|
||||
////var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
|
||||
// var reader = new StringReader(text);
|
||||
//CommonMark.CommonMarkConverter.Parse(reader);
|
||||
//CommonMark.CommonMarkConverter.Parse(reader);
|
||||
//reader.Dispose();
|
||||
//var writer = new StringWriter();
|
||||
CommonMark.CommonMarkConverter.Convert(text);
|
||||
//writer.Flush();
|
||||
//writer.ToString();
|
||||
}
|
||||
[Benchmark(Description = "MarkdownSharp")]
|
||||
public void TestMarkdownSharp()
|
||||
{
|
||||
new MarkdownSharp.Markdown().Transform(text);
|
||||
}
|
||||
|
||||
[Benchmark(Description = "MarkdownSharp")]
|
||||
public void TestMarkdownSharp()
|
||||
{
|
||||
new MarkdownSharp.Markdown().Transform(text);
|
||||
}
|
||||
static void Main(string[] args)
|
||||
{
|
||||
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(gcDiagnoser);
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
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(gcDiagnoser);
|
||||
|
||||
//var config = DefaultConfig.Instance;
|
||||
BenchmarkRunner.Run<Program>(config);
|
||||
//BenchmarkRunner.Run<TestDictionary>(config);
|
||||
//BenchmarkRunner.Run<TestMatchPerf>();
|
||||
//BenchmarkRunner.Run<TestStringPerf>();
|
||||
}
|
||||
//var config = DefaultConfig.Instance;
|
||||
BenchmarkRunner.Run<Program>(config);
|
||||
//BenchmarkRunner.Run<TestDictionary>(config);
|
||||
//BenchmarkRunner.Run<TestMatchPerf>();
|
||||
//BenchmarkRunner.Run<TestStringPerf>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,81 +2,79 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Testamina.Markdig.Benchmarks
|
||||
namespace Testamina.Markdig.Benchmarks;
|
||||
|
||||
public class TextRegexHelper
|
||||
{
|
||||
public class TextRegexHelper
|
||||
private readonly Dictionary<string, string> replacers;
|
||||
private readonly Regex regex;
|
||||
|
||||
public TextRegexHelper(Dictionary<string, string> replacers)
|
||||
{
|
||||
private readonly Dictionary<string, string> replacers;
|
||||
private readonly Regex regex;
|
||||
this.replacers = replacers;
|
||||
var builder = new StringBuilder();
|
||||
|
||||
public TextRegexHelper(Dictionary<string, string> replacers)
|
||||
// (?<1>:value:?)|(?<1>:noo:?)
|
||||
foreach (var replace in replacers)
|
||||
{
|
||||
this.replacers = replacers;
|
||||
var builder = new StringBuilder();
|
||||
|
||||
// (?<1>:value:?)|(?<1>:noo:?)
|
||||
foreach (var replace in replacers)
|
||||
var matchStr = Regex.Escape(replace.Key);
|
||||
if (builder.Length > 0)
|
||||
{
|
||||
var matchStr = Regex.Escape(replace.Key);
|
||||
if (builder.Length > 0)
|
||||
{
|
||||
builder.Append('|');
|
||||
}
|
||||
builder.Append("(?<1>").Append(matchStr).Append("?)");
|
||||
builder.Append('|');
|
||||
}
|
||||
|
||||
regex = new Regex(builder.ToString());
|
||||
builder.Append("(?<1>").Append(matchStr).Append("?)");
|
||||
}
|
||||
|
||||
public bool TryMatch(string text, int offset, out string matchText, out string replaceText)
|
||||
{
|
||||
replaceText = null;
|
||||
matchText = null;
|
||||
var result = regex.Match(text, offset);
|
||||
if (!result.Success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
matchText = result.Groups[1].Value;
|
||||
replaceText = replacers[matchText];
|
||||
return true;
|
||||
}
|
||||
regex = new Regex(builder.ToString());
|
||||
}
|
||||
|
||||
/*
|
||||
public class TestMatchPerf
|
||||
public bool TryMatch(string text, int offset, out string matchText, out string replaceText)
|
||||
{
|
||||
private readonly TextMatchHelper matcher;
|
||||
|
||||
public TestMatchPerf()
|
||||
replaceText = null;
|
||||
matchText = null;
|
||||
var result = regex.Match(text, offset);
|
||||
if (!result.Success)
|
||||
{
|
||||
var replacers = new Dictionary<string, string>();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
replacers.Add($":z{i}:", i.ToString());
|
||||
}
|
||||
replacers.Add(":abc:", "yes");
|
||||
|
||||
matcher = new TextMatchHelper(new HashSet<string>(replacers.Keys));
|
||||
return false;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TestMatch()
|
||||
{
|
||||
matchText = result.Groups[1].Value;
|
||||
replaceText = replacers[matchText];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
string matchText;
|
||||
//var text = ":z150: this is a long string";
|
||||
var text = ":z1:";
|
||||
matcher.TryMatch(text, 0, text.Length, out matchText);
|
||||
}
|
||||
/*
|
||||
public class TestMatchPerf
|
||||
{
|
||||
private readonly TextMatchHelper matcher;
|
||||
|
||||
public TestMatchPerf()
|
||||
{
|
||||
var replacers = new Dictionary<string, string>();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
replacers.Add($":z{i}:", i.ToString());
|
||||
}
|
||||
replacers.Add(":abc:", "yes");
|
||||
|
||||
matcher = new TextMatchHelper(new HashSet<string>(replacers.Keys));
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TestMatch()
|
||||
{
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
string matchText;
|
||||
//var text = ":z150: this is a long string";
|
||||
var text = ":z1:";
|
||||
matcher.TryMatch(text, 0, text.Length, out matchText);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -1,58 +1,57 @@
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
using Markdig.Renderers;
|
||||
|
||||
namespace Testamina.Markdig.Benchmarks
|
||||
namespace Testamina.Markdig.Benchmarks;
|
||||
|
||||
public class TestStringPerf
|
||||
{
|
||||
public class TestStringPerf
|
||||
private string text;
|
||||
|
||||
public TestStringPerf()
|
||||
{
|
||||
private string text;
|
||||
|
||||
public TestStringPerf()
|
||||
{
|
||||
text = new string('a', 1000);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TestIndexOfAny()
|
||||
{
|
||||
var writer = new HtmlRenderer(new StringWriter());
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//[Benchmark]
|
||||
//public void TestCustomIndexOfAny()
|
||||
//{
|
||||
// var writer = new HtmlRenderer(new StringWriter());
|
||||
// for (int i = 0; i < 100; i++)
|
||||
// {
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// }
|
||||
//}
|
||||
|
||||
text = new string('a', 1000);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TestIndexOfAny()
|
||||
{
|
||||
var writer = new HtmlRenderer(new StringWriter());
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
writer.WriteEscape(text, 0, text.Length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//[Benchmark]
|
||||
//public void TestCustomIndexOfAny()
|
||||
//{
|
||||
// var writer = new HtmlRenderer(new StringWriter());
|
||||
// for (int i = 0; i < 100; i++)
|
||||
// {
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// writer.WriteEscapeOptimized(text, 0, text.Length);
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
5
src/Markdig.Tests/GlobalUsings.cs
Normal file
5
src/Markdig.Tests/GlobalUsings.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
// 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.
|
||||
|
||||
global using Assert = NUnit.Framework.Legacy.ClassicAssert;
|
||||
@@ -1,18 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFrameworks>net6.0;net8.0;net9.0</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<IsPackable>false</IsPackable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<LangVersion>13.0</LangVersion>
|
||||
<StartupObject>Markdig.Tests.Program</StartupObject>
|
||||
<SpecExecutable>$(MSBuildProjectDirectory)\..\SpecFileGen\bin\$(Configuration)\net6.0\SpecFileGen.dll</SpecExecutable>
|
||||
<SpecTimestamp>$(MSBuildProjectDirectory)\..\SpecFileGen\bin\$(Configuration)\net6.0\SpecFileGen.timestamp</SpecTimestamp>
|
||||
<SpecExecutable>$(MSBuildProjectDirectory)\..\SpecFileGen\bin\$(Configuration)\$(TargetFramework)\SpecFileGen.dll</SpecExecutable>
|
||||
<SpecTimestamp>$(MSBuildProjectDirectory)\..\SpecFileGen\bin\$(Configuration)\$(TargetFramework)\SpecFileGen.timestamp</SpecTimestamp>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="NUnit" Version="4.3.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -20,6 +22,10 @@
|
||||
<ProjectReference Include="..\SpecFileGen\SpecFileGen.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="NUnit.Framework" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ItemSpecExecutable Include="$(SpecExecutable)" />
|
||||
<InputSpecFiles Include="Specs\*.md" />
|
||||
|
||||
@@ -1,192 +1,192 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using Markdig.Extensions.AutoLinks;
|
||||
using Markdig.Extensions.Tables;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class MiscTests
|
||||
{
|
||||
public class MiscTests
|
||||
[Test]
|
||||
public void LinkWithInvalidNonAsciiDomainNameIsIgnored()
|
||||
{
|
||||
[Test]
|
||||
public void LinkWithInvalidNonAsciiDomainNameIsIgnored()
|
||||
// Url from https://github.com/lunet-io/markdig/issues/438
|
||||
_ = Markdown.ToHtml("[minulém díle](http://V%20minulém%20díle%20jsme%20nainstalovali%20SQL%20Server,%20který%20je%20nutný%20pro%20běh%20Configuration%20Manageru.%20Dnes%20nás%20čeká%20instalace%20WSUS,%20což%20je%20produkt,%20jež%20je%20možné%20používat%20i%20jako%20samostatnou%20funkci%20ve%20Windows%20Serveru,%20který%20se%20stará%20o%20stažení%20a%20instalaci%20aktualizací%20z%20Microsoft%20Update%20na%20klientské%20počítače.%20Stejně%20jako%20v%20předchozích%20dílech,%20tak%20i%20v%20tomto%20si%20ukážeme%20obě%20varianty%20instalace%20–%20a%20to%20jak%20instalaci%20z%20PowerShellu,%20tak%20instalaci%20pomocí%20GUI.) ");
|
||||
|
||||
// Valid IDN
|
||||
TestParser.TestSpec("[foo](http://ünicode.com)", "<p><a href=\"http://xn--nicode-2ya.com\">foo</a></p>");
|
||||
TestParser.TestSpec("[foo](http://ünicode.ünicode.com)", "<p><a href=\"http://xn--nicode-2ya.xn--nicode-2ya.com\">foo</a></p>");
|
||||
|
||||
// Invalid IDN
|
||||
TestParser.TestSpec("[foo](http://ünicode..com)", "<p><a href=\"http://%C3%BCnicode..com\">foo</a></p>");
|
||||
}
|
||||
|
||||
[TestCase("link [foo [bar]]")] // https://spec.commonmark.org/0.29/#example-508
|
||||
[TestCase("link [foo][bar]")]
|
||||
[TestCase("link [][foo][bar][]")]
|
||||
[TestCase("link [][foo][bar][[]]")]
|
||||
[TestCase("link [foo] [bar]")]
|
||||
[TestCase("link [[foo] [] [bar] [[abc]def]]")]
|
||||
[TestCase("[]")]
|
||||
[TestCase("[ ]")]
|
||||
[TestCase("[bar][]")]
|
||||
[TestCase("[bar][ foo]")]
|
||||
[TestCase("[bar][foo ][]")]
|
||||
[TestCase("[bar][fo[ ]o ][][]")]
|
||||
[TestCase("[a]b[c[d[e]f]g]h")]
|
||||
[TestCase("a[b[c[d]e]f[g]h]i foo [j]k[l[m]n]o")]
|
||||
[TestCase("a[b[c[d]e]f[g]h]i[] [][foo][bar][] foo [j]k[l[m]n]o")]
|
||||
[TestCase("a[b[c[d]e]f[g]h]i foo [j]k[l[m]n]o[][]")]
|
||||
public void LinkTextMayContainBalancedBrackets(string linkText)
|
||||
{
|
||||
string markdown = $"[{linkText}](/uri)";
|
||||
string expected = $@"<p><a href=""/uri"">{linkText}</a></p>";
|
||||
|
||||
TestParser.TestSpec(markdown, expected);
|
||||
|
||||
// Make the link text unbalanced
|
||||
foreach (var bracketIndex in linkText
|
||||
.Select((c, i) => new Tuple<char, int>(c, i))
|
||||
.Where(t => t.Item1 == '[' || t.Item1 == ']')
|
||||
.Select(t => t.Item2))
|
||||
{
|
||||
// Url from https://github.com/lunet-io/markdig/issues/438
|
||||
_ = Markdown.ToHtml("[minulém díle](http://V%20minulém%20díle%20jsme%20nainstalovali%20SQL%20Server,%20který%20je%20nutný%20pro%20běh%20Configuration%20Manageru.%20Dnes%20nás%20čeká%20instalace%20WSUS,%20což%20je%20produkt,%20jež%20je%20možné%20používat%20i%20jako%20samostatnou%20funkci%20ve%20Windows%20Serveru,%20který%20se%20stará%20o%20stažení%20a%20instalaci%20aktualizací%20z%20Microsoft%20Update%20na%20klientské%20počítače.%20Stejně%20jako%20v%20předchozích%20dílech,%20tak%20i%20v%20tomto%20si%20ukážeme%20obě%20varianty%20instalace%20–%20a%20to%20jak%20instalaci%20z%20PowerShellu,%20tak%20instalaci%20pomocí%20GUI.) ");
|
||||
string brokenLinkText = linkText.Remove(bracketIndex, 1);
|
||||
|
||||
// Valid IDN
|
||||
TestParser.TestSpec("[foo](http://ünicode.com)", "<p><a href=\"http://xn--nicode-2ya.com\">foo</a></p>");
|
||||
TestParser.TestSpec("[foo](http://ünicode.ünicode.com)", "<p><a href=\"http://xn--nicode-2ya.xn--nicode-2ya.com\">foo</a></p>");
|
||||
markdown = $"[{brokenLinkText}](/uri)";
|
||||
expected = $@"<p><a href=""/uri"">{brokenLinkText}</a></p>";
|
||||
|
||||
// Invalid IDN
|
||||
TestParser.TestSpec("[foo](http://ünicode..com)", "<p><a href=\"http://%C3%BCnicode..com\">foo</a></p>");
|
||||
string actual = Markdown.ToHtml(markdown);
|
||||
Assert.AreNotEqual(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("link [foo [bar]]")] // https://spec.commonmark.org/0.29/#example-508
|
||||
[TestCase("link [foo][bar]")]
|
||||
[TestCase("link [][foo][bar][]")]
|
||||
[TestCase("link [][foo][bar][[]]")]
|
||||
[TestCase("link [foo] [bar]")]
|
||||
[TestCase("link [[foo] [] [bar] [[abc]def]]")]
|
||||
[TestCase("[]")]
|
||||
[TestCase("[ ]")]
|
||||
[TestCase("[bar][]")]
|
||||
[TestCase("[bar][ foo]")]
|
||||
[TestCase("[bar][foo ][]")]
|
||||
[TestCase("[bar][fo[ ]o ][][]")]
|
||||
[TestCase("[a]b[c[d[e]f]g]h")]
|
||||
[TestCase("a[b[c[d]e]f[g]h]i foo [j]k[l[m]n]o")]
|
||||
[TestCase("a[b[c[d]e]f[g]h]i[] [][foo][bar][] foo [j]k[l[m]n]o")]
|
||||
[TestCase("a[b[c[d]e]f[g]h]i foo [j]k[l[m]n]o[][]")]
|
||||
public void LinkTextMayContainBalancedBrackets(string linkText)
|
||||
[Theory]
|
||||
[TestCase('[', 9 * 1024, true, false)]
|
||||
[TestCase('[', 11 * 1024, true, true)]
|
||||
[TestCase('[', 100, false, false)]
|
||||
[TestCase('[', 150, false, true)]
|
||||
[TestCase('>', 100, true, false)]
|
||||
[TestCase('>', 150, true, true)]
|
||||
public void GuardsAgainstHighlyNestedNodes(char c, int count, bool parseOnly, bool shouldThrow)
|
||||
{
|
||||
var markdown = new string(c, count);
|
||||
TestDelegate test = parseOnly ? () => Markdown.Parse(markdown) : () => Markdown.ToHtml(markdown);
|
||||
|
||||
if (shouldThrow)
|
||||
{
|
||||
string markdown = $"[{linkText}](/uri)";
|
||||
string expected = $@"<p><a href=""/uri"">{linkText}</a></p>";
|
||||
|
||||
TestParser.TestSpec(markdown, expected);
|
||||
|
||||
// Make the link text unbalanced
|
||||
foreach (var bracketIndex in linkText
|
||||
.Select((c, i) => new Tuple<char, int>(c, i))
|
||||
.Where(t => t.Item1 == '[' || t.Item1 == ']')
|
||||
.Select(t => t.Item2))
|
||||
{
|
||||
string brokenLinkText = linkText.Remove(bracketIndex, 1);
|
||||
|
||||
markdown = $"[{brokenLinkText}](/uri)";
|
||||
expected = $@"<p><a href=""/uri"">{brokenLinkText}</a></p>";
|
||||
|
||||
string actual = Markdown.ToHtml(markdown);
|
||||
Assert.AreNotEqual(expected, actual);
|
||||
}
|
||||
Exception e = Assert.Throws<ArgumentException>(test);
|
||||
Assert.True(e.Message.Contains("depth limit"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[TestCase('[', 9 * 1024, true, false)]
|
||||
[TestCase('[', 11 * 1024, true, true)]
|
||||
[TestCase('[', 100, false, false)]
|
||||
[TestCase('[', 150, false, true)]
|
||||
[TestCase('>', 100, true, false)]
|
||||
[TestCase('>', 150, true, true)]
|
||||
public void GuardsAgainstHighlyNestedNodes(char c, int count, bool parseOnly, bool shouldThrow)
|
||||
else
|
||||
{
|
||||
var markdown = new string(c, count);
|
||||
TestDelegate test = parseOnly ? () => Markdown.Parse(markdown) : () => Markdown.ToHtml(markdown);
|
||||
|
||||
if (shouldThrow)
|
||||
{
|
||||
Exception e = Assert.Throws<ArgumentException>(test);
|
||||
Assert.True(e.Message.Contains("depth limit"));
|
||||
}
|
||||
else
|
||||
{
|
||||
test();
|
||||
}
|
||||
test();
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsIssue356Corrected()
|
||||
[Test]
|
||||
public void IsIssue356Corrected()
|
||||
{
|
||||
string input = @"https://foo.bar/path/\#m4mv5W0GYKZpGvfA.97";
|
||||
string expected = @"<p><a href=""https://foo.bar/path/%5C#m4mv5W0GYKZpGvfA.97"">https://foo.bar/path/\#m4mv5W0GYKZpGvfA.97</a></p>";
|
||||
|
||||
TestParser.TestSpec($"<{input}>", expected);
|
||||
TestParser.TestSpec(input, expected, "autolinks|advanced");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsIssue365Corrected()
|
||||
{
|
||||
// The scheme must be escaped too...
|
||||
string input = "";
|
||||
string expected = "<p><img src=\"%22onclick=%22alert&#40;%27click%27&#41;%22://\" alt=\"image\" /></p>";
|
||||
|
||||
TestParser.TestSpec(input, expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAltTextIsCorrectlyEscaped()
|
||||
{
|
||||
TestParser.TestSpec(
|
||||
@"",
|
||||
@"<p><img src=""girl.png"" alt=""This is image alt text with quotation ' and double quotation "hello" world"" /></p>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangelogPRLinksMatchDescription()
|
||||
{
|
||||
string solutionFolder = Path.GetFullPath(Path.Combine(TestParser.TestsDirectory, "../.."));
|
||||
string changelogPath = Path.Combine(solutionFolder, "changelog.md");
|
||||
string changelog = File.ReadAllText(changelogPath);
|
||||
var matches = Regex.Matches(changelog, @"\(\[\(PR #(\d+)\)\]\(.*?pull\/(\d+)\)\)");
|
||||
Assert.Greater(matches.Count, 0);
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
string input = @"https://foo.bar/path/\#m4mv5W0GYKZpGvfA.97";
|
||||
string expected = @"<p><a href=""https://foo.bar/path/%5C#m4mv5W0GYKZpGvfA.97"">https://foo.bar/path/\#m4mv5W0GYKZpGvfA.97</a></p>";
|
||||
|
||||
TestParser.TestSpec($"<{input}>", expected);
|
||||
TestParser.TestSpec(input, expected, "autolinks|advanced");
|
||||
Assert.True(int.TryParse(match.Groups[1].Value, out int textNr));
|
||||
Assert.True(int.TryParse(match.Groups[2].Value, out int linkNr));
|
||||
Assert.AreEqual(textNr, linkNr);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsIssue365Corrected()
|
||||
{
|
||||
// The scheme must be escaped too...
|
||||
string input = "";
|
||||
string expected = "<p><img src=\"%22onclick=%22alert&#40;%27click%27&#41;%22://\" alt=\"image\" /></p>";
|
||||
[Test]
|
||||
public void TestFixHang()
|
||||
{
|
||||
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "hang.md"));
|
||||
_ = Markdown.ToHtml(input);
|
||||
}
|
||||
|
||||
TestParser.TestSpec(input, expected);
|
||||
}
|
||||
[Test]
|
||||
public void TestInvalidHtmlEntity()
|
||||
{
|
||||
var input = "9&ddr;&*&ddr;&de<64><65>__";
|
||||
TestParser.TestSpec(input, "<p>9&ddr;&*&ddr;&de<64><65>__</p>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAltTextIsCorrectlyEscaped()
|
||||
{
|
||||
TestParser.TestSpec(
|
||||
@"",
|
||||
@"<p><img src=""girl.png"" alt=""This is image alt text with quotation ' and double quotation "hello" world"" /></p>");
|
||||
}
|
||||
[Test]
|
||||
public void TestInvalidCharacterHandling()
|
||||
{
|
||||
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "ArgumentOutOfRangeException.md"));
|
||||
_ = Markdown.ToHtml(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangelogPRLinksMatchDescription()
|
||||
{
|
||||
string solutionFolder = Path.GetFullPath(Path.Combine(TestParser.TestsDirectory, "../.."));
|
||||
string changelogPath = Path.Combine(solutionFolder, "changelog.md");
|
||||
string changelog = File.ReadAllText(changelogPath);
|
||||
var matches = Regex.Matches(changelog, @"\(\[\(PR #(\d+)\)\]\(.*?pull\/(\d+)\)\)");
|
||||
Assert.Greater(matches.Count, 0);
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
Assert.True(int.TryParse(match.Groups[1].Value, out int textNr));
|
||||
Assert.True(int.TryParse(match.Groups[2].Value, out int linkNr));
|
||||
Assert.AreEqual(textNr, linkNr);
|
||||
}
|
||||
}
|
||||
[Test]
|
||||
public void TestInvalidCodeEscape()
|
||||
{
|
||||
var input = "```**Header** ";
|
||||
_ = Markdown.ToHtml(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFixHang()
|
||||
{
|
||||
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "hang.md"));
|
||||
_ = Markdown.ToHtml(input);
|
||||
}
|
||||
[Test]
|
||||
public void TestEmphasisAndHtmlEntity()
|
||||
{
|
||||
var markdownText = "*Unlimited-Fun®*®";
|
||||
TestParser.TestSpec(markdownText, "<p><em>Unlimited-Fun®</em>®</p>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidHtmlEntity()
|
||||
{
|
||||
var input = "9&ddr;&*&ddr;&de<64><65>__";
|
||||
TestParser.TestSpec(input, "<p>9&ddr;&*&ddr;&de<64><65>__</p>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidCharacterHandling()
|
||||
{
|
||||
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "ArgumentOutOfRangeException.md"));
|
||||
_ = Markdown.ToHtml(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidCodeEscape()
|
||||
{
|
||||
var input = "```**Header** ";
|
||||
_ = Markdown.ToHtml(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmphasisAndHtmlEntity()
|
||||
{
|
||||
var markdownText = "*Unlimited-Fun®*®";
|
||||
TestParser.TestSpec(markdownText, "<p><em>Unlimited-Fun®</em>®</p>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestThematicInsideCodeBlockInsideList()
|
||||
{
|
||||
var input = @"1. In the :
|
||||
[Test]
|
||||
public void TestThematicInsideCodeBlockInsideList()
|
||||
{
|
||||
var input = @"1. In the :
|
||||
|
||||
```
|
||||
Id DisplayName Description
|
||||
-- ----------- -----------
|
||||
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
|
||||
```";
|
||||
TestParser.TestSpec(input, @"<ol>
|
||||
TestParser.TestSpec(input, @"<ol>
|
||||
<li><p>In the :</p>
|
||||
<pre><code>Id DisplayName Description
|
||||
-- ----------- -----------
|
||||
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
|
||||
</code></pre></li>
|
||||
</ol>");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VisualizeMathExpressions()
|
||||
{
|
||||
string math = @"Math expressions
|
||||
[Test]
|
||||
public void VisualizeMathExpressions()
|
||||
{
|
||||
string math = @"Math expressions
|
||||
|
||||
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
|
||||
|
||||
@@ -199,98 +199,191 @@ $$
|
||||
<div class=""math"">
|
||||
\begin{align}
|
||||
\sqrt{37} & = \sqrt{\frac{73^2-1}{12^2}} \\
|
||||
& = \sqrt{\frac{73^2}{12^2}\cdot\frac{73^2-1}{73^2}} \\
|
||||
& = \sqrt{\frac{73^2}{12^2}\cdot\frac{73^2-1}{73^2}} \\
|
||||
& = \sqrt{\frac{73^2}{12^2}}\sqrt{\frac{73^2-1}{73^2}} \\
|
||||
& = \frac{73}{12}\sqrt{1 - \frac{1}{73^2}} \\
|
||||
& = \frac{73}{12}\sqrt{1 - \frac{1}{73^2}} \\
|
||||
& \approx \frac{73}{12}\left(1 - \frac{1}{2\cdot73^2}\right)
|
||||
\end{align}
|
||||
</div>
|
||||
";
|
||||
//Console.WriteLine("Math Expressions:\n");
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
//Console.WriteLine(html);
|
||||
}
|
||||
//Console.WriteLine("Math Expressions:\n");
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
//Console.WriteLine(html);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InlineMathExpression()
|
||||
{
|
||||
string math = @"Math expressions
|
||||
[Test]
|
||||
public void InlineMathExpression()
|
||||
{
|
||||
string math = @"Math expressions
|
||||
|
||||
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
|
||||
";
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
|
||||
var test1 = html.Contains("<p><span class=\"math\">\\(");
|
||||
var test2 = html.Contains("\\)</span></p>");
|
||||
if (!test1 || !test2)
|
||||
{
|
||||
Console.WriteLine(html);
|
||||
}
|
||||
|
||||
Assert.IsTrue(test1, "Leading bracket missing");
|
||||
Assert.IsTrue(test2, "Trailing bracket missing");
|
||||
var test1 = html.Contains("<p><span class=\"math\">\\(");
|
||||
var test2 = html.Contains("\\)</span></p>");
|
||||
if (!test1 || !test2)
|
||||
{
|
||||
Console.WriteLine(html);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BlockMathExpression()
|
||||
{
|
||||
string math = @"Math expressions
|
||||
Assert.IsTrue(test1, "Leading bracket missing");
|
||||
Assert.IsTrue(test2, "Trailing bracket missing");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BlockMathExpression()
|
||||
{
|
||||
string math = @"Math expressions
|
||||
|
||||
$$
|
||||
\frac{n!}{k!(n-k)!} = \binom{n}{k}
|
||||
$$
|
||||
";
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
var test1 = html.Contains("<div class=\"math\">\n\\[");
|
||||
var test2 = html.Contains("\\]</div>");
|
||||
if (!test1 || !test2)
|
||||
{
|
||||
Console.WriteLine(html);
|
||||
}
|
||||
|
||||
Assert.IsTrue(test1, "Leading bracket missing");
|
||||
Assert.IsTrue(test2, "Trailing bracket missing");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanDisableParsingHeadings()
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
var test1 = html.Contains("<div class=\"math\">\n\\[");
|
||||
var test2 = html.Contains("\\]</div>");
|
||||
if (!test1 || !test2)
|
||||
{
|
||||
var noHeadingsPipeline = new MarkdownPipelineBuilder().DisableHeadings().Build();
|
||||
|
||||
TestParser.TestSpec("Foo\n===", "<h1>Foo</h1>");
|
||||
TestParser.TestSpec("Foo\n===", "<p>Foo\n===</p>", noHeadingsPipeline);
|
||||
|
||||
TestParser.TestSpec("# Heading 1", "<h1>Heading 1</h1>");
|
||||
TestParser.TestSpec("# Heading 1", "<p># Heading 1</p>", noHeadingsPipeline);
|
||||
|
||||
// Does not also disable link reference definitions
|
||||
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>");
|
||||
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>", noHeadingsPipeline);
|
||||
Console.WriteLine(html);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanOpenAutoLinksInNewWindow()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
|
||||
var newWindowPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { OpenInNewWindow = true }).Build();
|
||||
Assert.IsTrue(test1, "Leading bracket missing");
|
||||
Assert.IsTrue(test2, "Trailing bracket missing");
|
||||
}
|
||||
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\" target=\"_blank\">www.foo.bar</a></p>", newWindowPipeline);
|
||||
}
|
||||
[Test]
|
||||
public void CanDisableParsingHeadings()
|
||||
{
|
||||
var noHeadingsPipeline = new MarkdownPipelineBuilder().DisableHeadings().Build();
|
||||
|
||||
[Test]
|
||||
public void CanUseHttpsPrefixForWWWAutoLinks()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
|
||||
var httpsPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { UseHttpsForWWWLinks = true }).Build();
|
||||
TestParser.TestSpec("Foo\n===", "<h1>Foo</h1>");
|
||||
TestParser.TestSpec("Foo\n===", "<p>Foo\n===</p>", noHeadingsPipeline);
|
||||
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"https://www.foo.bar\">www.foo.bar</a></p>", httpsPipeline);
|
||||
}
|
||||
TestParser.TestSpec("# Heading 1", "<h1>Heading 1</h1>");
|
||||
TestParser.TestSpec("# Heading 1", "<p># Heading 1</p>", noHeadingsPipeline);
|
||||
|
||||
// Does not also disable link reference definitions
|
||||
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>");
|
||||
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>", noHeadingsPipeline);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanOpenAutoLinksInNewWindow()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
|
||||
var newWindowPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { OpenInNewWindow = true }).Build();
|
||||
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\" target=\"_blank\">www.foo.bar</a></p>", newWindowPipeline);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanUseHttpsPrefixForWWWAutoLinks()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
|
||||
var httpsPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { UseHttpsForWWWLinks = true }).Build();
|
||||
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"https://www.foo.bar\">www.foo.bar</a></p>", httpsPipeline);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RootInlineHasCorrectSourceSpan()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build();
|
||||
pipeline.TrackTrivia = true;
|
||||
|
||||
var document = Markdown.Parse("0123456789\n", pipeline);
|
||||
|
||||
var expectedSourceSpan = new SourceSpan(0, 10);
|
||||
Assert.That(((LeafBlock)document.LastChild).Inline.Span == expectedSourceSpan);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RootInlineInTableCellHasCorrectSourceSpan()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UsePreciseSourceLocation().UseAdvancedExtensions().Build();
|
||||
pipeline.TrackTrivia = true;
|
||||
|
||||
var document = Markdown.Parse("| a | b |\n| --- | --- |\n| <span id=\"dest\"></span><span id=\"DEST\"></span>*dest*<br/> | \\[in\\] The address of the result of the operation.<br/> |", pipeline);
|
||||
|
||||
var paragraph = (ParagraphBlock)((TableCell)((TableRow)((Table)document.LastChild).LastChild).First()).LastChild;
|
||||
Assert.That(paragraph.Inline.Span.Start == paragraph.Inline.FirstChild.Span.Start);
|
||||
Assert.That(paragraph.Inline.Span.End == paragraph.Inline.LastChild.Span.End);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGridTableShortLine()
|
||||
{
|
||||
var input = @"
|
||||
+--+
|
||||
| |
|
||||
+-";
|
||||
|
||||
var expected = @"<table>
|
||||
<col style=""width:100%"" />
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
";
|
||||
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseGridTables().Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDefinitionListInListItemWithBlankLine()
|
||||
{
|
||||
var input = @"
|
||||
-
|
||||
|
||||
term
|
||||
: definition
|
||||
";
|
||||
|
||||
var expected = @"<ul>
|
||||
<li>
|
||||
<dl>
|
||||
<dt>term</dt>
|
||||
<dd>definition</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
";
|
||||
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseDefinitionLists().Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAlertWithinAlertOrNestedBlock()
|
||||
{
|
||||
var input = @"
|
||||
>[!NOTE]
|
||||
[!NOTE]
|
||||
The second one is not a note.
|
||||
|
||||
>>[!NOTE]
|
||||
Also not a note.
|
||||
";
|
||||
|
||||
var expected = @"<div class=""markdown-alert markdown-alert-note"">
|
||||
<p class=""markdown-alert-title""><svg viewBox=""0 0 16 16"" version=""1.1"" width=""16"" height=""16"" aria-hidden=""true""><path d=""M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z""></path></svg>Note</p>
|
||||
<p>[!NOTE]
|
||||
The second one is not a note.</p>
|
||||
</div>
|
||||
<blockquote>
|
||||
<blockquote>
|
||||
<p>[!NOTE]
|
||||
Also not a note.</p>
|
||||
</blockquote>
|
||||
</blockquote>
|
||||
";
|
||||
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseAlertBlocks().Build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,15 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
namespace Markdig.Tests;
|
||||
|
||||
namespace Markdig.Tests
|
||||
class Program
|
||||
{
|
||||
class Program
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Run NUnit tests runner with this");
|
||||
// Uncomment the following line to debug a specific tests more easily
|
||||
//var tests = new Specs.CommonMarkV_0_29.TestLeafBlocksSetextHeadings();
|
||||
//tests.LeafBlocksSetextHeadings_Example063();
|
||||
}
|
||||
Console.WriteLine("Run NUnit tests runner with this");
|
||||
// Uncomment the following line to debug a specific tests more easily
|
||||
//var tests = new Specs.CommonMarkV_0_29.TestLeafBlocksSetextHeadings();
|
||||
//tests.LeafBlocksSetextHeadings_Example063();
|
||||
}
|
||||
}
|
||||
@@ -1,55 +1,53 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestAtxHeading
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestAtxHeading
|
||||
[TestCase("# h")]
|
||||
[TestCase("# h ")]
|
||||
[TestCase("# h\n#h")]
|
||||
[TestCase("# h\n #h")]
|
||||
[TestCase("# h\n # h")]
|
||||
[TestCase("# h\n # h ")]
|
||||
[TestCase(" # h \n # h ")]
|
||||
public void Test(string value)
|
||||
{
|
||||
[TestCase("# h")]
|
||||
[TestCase("# h ")]
|
||||
[TestCase("# h\n#h")]
|
||||
[TestCase("# h\n #h")]
|
||||
[TestCase("# h\n # h")]
|
||||
[TestCase("# h\n # h ")]
|
||||
[TestCase(" # h \n # h ")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("\n# h\n\np")]
|
||||
[TestCase("\n# h\n\np\n")]
|
||||
[TestCase("\n# h\n\np\n\n")]
|
||||
[TestCase("\n\n# h\n\np\n\n")]
|
||||
[TestCase("\n\n# h\np\n\n")]
|
||||
[TestCase("\n\n# h\np\n\n")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("\n# h\n\np")]
|
||||
[TestCase("\n# h\n\np\n")]
|
||||
[TestCase("\n# h\n\np\n\n")]
|
||||
[TestCase("\n\n# h\n\np\n\n")]
|
||||
[TestCase("\n\n# h\np\n\n")]
|
||||
[TestCase("\n\n# h\np\n\n")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("\n# h")]
|
||||
[TestCase("\n# h\n")]
|
||||
[TestCase("\n# h\r")]
|
||||
[TestCase("\n# h\r\n")]
|
||||
[TestCase("\n# h")]
|
||||
[TestCase("\n# h\n")]
|
||||
[TestCase("\n# h\r")]
|
||||
[TestCase("\n# h\r\n")]
|
||||
|
||||
[TestCase("\r# h")]
|
||||
[TestCase("\r# h\n")]
|
||||
[TestCase("\r# h\r")]
|
||||
[TestCase("\r# h\r\n")]
|
||||
[TestCase("\r# h")]
|
||||
[TestCase("\r# h\n")]
|
||||
[TestCase("\r# h\r")]
|
||||
[TestCase("\r# h\r\n")]
|
||||
|
||||
[TestCase("\r\n# h")]
|
||||
[TestCase("\r\n# h\n")]
|
||||
[TestCase("\r\n# h\r")]
|
||||
[TestCase("\r\n# h\r\n")]
|
||||
[TestCase("\r\n# h")]
|
||||
[TestCase("\r\n# h\n")]
|
||||
[TestCase("\r\n# h\r")]
|
||||
[TestCase("\r\n# h\r\n")]
|
||||
|
||||
[TestCase("# h\n\n ")]
|
||||
[TestCase("# h\n\n ")]
|
||||
[TestCase("# h\n\n ")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("# h\n\n ")]
|
||||
[TestCase("# h\n\n ")]
|
||||
[TestCase("# h\n\n ")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,107 +1,105 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestFencedCodeBlock
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestFencedCodeBlock
|
||||
[TestCase("```\nc\n```")]
|
||||
[TestCase("```\nc\n```\n")]
|
||||
[TestCase("\n```\nc\n```")]
|
||||
[TestCase("\n\n```\nc\n```")]
|
||||
[TestCase("```\nc\n```\n")]
|
||||
[TestCase("```\nc\n```\n\n")]
|
||||
[TestCase("\n```\nc\n```\n")]
|
||||
[TestCase("\n```\nc\n```\n\n")]
|
||||
[TestCase("\n\n```\nc\n```\n")]
|
||||
[TestCase("\n\n```\nc\n```\n\n")]
|
||||
|
||||
[TestCase(" ```\nc\n````")]
|
||||
[TestCase("```\nc\n````")]
|
||||
[TestCase("p\n\n```\nc\n```")]
|
||||
|
||||
[TestCase("```\n c\n```")]
|
||||
[TestCase("```\nc \n```")]
|
||||
[TestCase("```\n c \n```")]
|
||||
|
||||
[TestCase(" ``` \n c \n ``` ")]
|
||||
[TestCase("\t```\t\n\tc\t\n\t```\t")]
|
||||
[TestCase("\v```\v\n\vc\v\n\v```\v")]
|
||||
[TestCase("\f```\f\n\fc\f\n\f```\f")]
|
||||
public void Test(string value)
|
||||
{
|
||||
[TestCase("```\nc\n```")]
|
||||
[TestCase("```\nc\n```\n")]
|
||||
[TestCase("\n```\nc\n```")]
|
||||
[TestCase("\n\n```\nc\n```")]
|
||||
[TestCase("```\nc\n```\n")]
|
||||
[TestCase("```\nc\n```\n\n")]
|
||||
[TestCase("\n```\nc\n```\n")]
|
||||
[TestCase("\n```\nc\n```\n\n")]
|
||||
[TestCase("\n\n```\nc\n```\n")]
|
||||
[TestCase("\n\n```\nc\n```\n\n")]
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(" ```\nc\n````")]
|
||||
[TestCase("```\nc\n````")]
|
||||
[TestCase("p\n\n```\nc\n```")]
|
||||
[TestCase("~~~ aa ``` ~~~\nfoo\n~~~")]
|
||||
[TestCase("~~~ aa ``` ~~~\nfoo\n~~~ ")]
|
||||
public void TestTilde(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("```\n c\n```")]
|
||||
[TestCase("```\nc \n```")]
|
||||
[TestCase("```\n c \n```")]
|
||||
[TestCase("```\n c \n```")]
|
||||
[TestCase("```\n c \r```")]
|
||||
[TestCase("```\n c \r\n```")]
|
||||
[TestCase("```\r c \n```")]
|
||||
[TestCase("```\r c \r```")]
|
||||
[TestCase("```\r c \r\n```")]
|
||||
[TestCase("```\r\n c \n```")]
|
||||
[TestCase("```\r\n c \r```")]
|
||||
[TestCase("```\r\n c \r\n```")]
|
||||
|
||||
[TestCase(" ``` \n c \n ``` ")]
|
||||
[TestCase("\t```\t\n\tc\t\n\t```\t")]
|
||||
[TestCase("\v```\v\n\vc\v\n\v```\v")]
|
||||
[TestCase("\f```\f\n\fc\f\n\f```\f")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("```\n c \n```\n")]
|
||||
[TestCase("```\n c \r```\n")]
|
||||
[TestCase("```\n c \r\n```\n")]
|
||||
[TestCase("```\r c \n```\n")]
|
||||
[TestCase("```\r c \r```\n")]
|
||||
[TestCase("```\r c \r\n```\n")]
|
||||
[TestCase("```\r\n c \n```\n")]
|
||||
[TestCase("```\r\n c \r```\n")]
|
||||
[TestCase("```\r\n c \r\n```\n")]
|
||||
|
||||
[TestCase("~~~ aa ``` ~~~\nfoo\n~~~")]
|
||||
[TestCase("~~~ aa ``` ~~~\nfoo\n~~~ ")]
|
||||
public void TestTilde(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("```\n c \n```\r")]
|
||||
[TestCase("```\n c \r```\r")]
|
||||
[TestCase("```\n c \r\n```\r")]
|
||||
[TestCase("```\r c \n```\r")]
|
||||
[TestCase("```\r c \r```\r")]
|
||||
[TestCase("```\r c \r\n```\r")]
|
||||
[TestCase("```\r\n c \n```\r")]
|
||||
[TestCase("```\r\n c \r```\r")]
|
||||
[TestCase("```\r\n c \r\n```\r")]
|
||||
|
||||
[TestCase("```\n c \n```")]
|
||||
[TestCase("```\n c \r```")]
|
||||
[TestCase("```\n c \r\n```")]
|
||||
[TestCase("```\r c \n```")]
|
||||
[TestCase("```\r c \r```")]
|
||||
[TestCase("```\r c \r\n```")]
|
||||
[TestCase("```\r\n c \n```")]
|
||||
[TestCase("```\r\n c \r```")]
|
||||
[TestCase("```\r\n c \r\n```")]
|
||||
[TestCase("```\n c \n```\r\n")]
|
||||
[TestCase("```\n c \r```\r\n")]
|
||||
[TestCase("```\n c \r\n```\r\n")]
|
||||
[TestCase("```\r c \n```\r\n")]
|
||||
[TestCase("```\r c \r```\r\n")]
|
||||
[TestCase("```\r c \r\n```\r\n")]
|
||||
[TestCase("```\r\n c \n```\r\n")]
|
||||
[TestCase("```\r\n c \r```\r\n")]
|
||||
[TestCase("```\r\n c \r\n```\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("```\n c \n```\n")]
|
||||
[TestCase("```\n c \r```\n")]
|
||||
[TestCase("```\n c \r\n```\n")]
|
||||
[TestCase("```\r c \n```\n")]
|
||||
[TestCase("```\r c \r```\n")]
|
||||
[TestCase("```\r c \r\n```\n")]
|
||||
[TestCase("```\r\n c \n```\n")]
|
||||
[TestCase("```\r\n c \r```\n")]
|
||||
[TestCase("```\r\n c \r\n```\n")]
|
||||
[TestCase("```i a\n```")]
|
||||
[TestCase("```i a a2\n```")]
|
||||
[TestCase("```i a a2 a3\n```")]
|
||||
[TestCase("```i a a2 a3 a4\n```")]
|
||||
|
||||
[TestCase("```\n c \n```\r")]
|
||||
[TestCase("```\n c \r```\r")]
|
||||
[TestCase("```\n c \r\n```\r")]
|
||||
[TestCase("```\r c \n```\r")]
|
||||
[TestCase("```\r c \r```\r")]
|
||||
[TestCase("```\r c \r\n```\r")]
|
||||
[TestCase("```\r\n c \n```\r")]
|
||||
[TestCase("```\r\n c \r```\r")]
|
||||
[TestCase("```\r\n c \r\n```\r")]
|
||||
[TestCase("```i\ta\n```")]
|
||||
[TestCase("```i\ta a2\n```")]
|
||||
[TestCase("```i\ta a2 a3\n```")]
|
||||
[TestCase("```i\ta a2 a3 a4\n```")]
|
||||
|
||||
[TestCase("```\n c \n```\r\n")]
|
||||
[TestCase("```\n c \r```\r\n")]
|
||||
[TestCase("```\n c \r\n```\r\n")]
|
||||
[TestCase("```\r c \n```\r\n")]
|
||||
[TestCase("```\r c \r```\r\n")]
|
||||
[TestCase("```\r c \r\n```\r\n")]
|
||||
[TestCase("```\r\n c \n```\r\n")]
|
||||
[TestCase("```\r\n c \r```\r\n")]
|
||||
[TestCase("```\r\n c \r\n```\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("```i a\n```")]
|
||||
[TestCase("```i a a2\n```")]
|
||||
[TestCase("```i a a2 a3\n```")]
|
||||
[TestCase("```i a a2 a3 a4\n```")]
|
||||
|
||||
[TestCase("```i\ta\n```")]
|
||||
[TestCase("```i\ta a2\n```")]
|
||||
[TestCase("```i\ta a2 a3\n```")]
|
||||
[TestCase("```i\ta a2 a3 a4\n```")]
|
||||
|
||||
[TestCase("```i\ta \n```")]
|
||||
[TestCase("```i\ta a2 \n```")]
|
||||
[TestCase("```i\ta a2 a3 \n```")]
|
||||
[TestCase("```i\ta a2 a3 a4 \n```")]
|
||||
public void TestInfoArguments(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("```i\ta \n```")]
|
||||
[TestCase("```i\ta a2 \n```")]
|
||||
[TestCase("```i\ta a2 a3 \n```")]
|
||||
[TestCase("```i\ta a2 a3 a4 \n```")]
|
||||
public void TestInfoArguments(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestHtmlBlock
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestHtmlBlock
|
||||
[TestCase("<br>")]
|
||||
[TestCase("<br>\n")]
|
||||
[TestCase("<br>\n\n")]
|
||||
[TestCase("<div></div>\n\n# h")]
|
||||
[TestCase("p\n\n<div></div>\n")]
|
||||
[TestCase("<div></div>\n\n# h")]
|
||||
public void Test(string value)
|
||||
{
|
||||
[TestCase("<br>")]
|
||||
[TestCase("<br>\n")]
|
||||
[TestCase("<br>\n\n")]
|
||||
[TestCase("<div></div>\n\n# h")]
|
||||
[TestCase("p\n\n<div></div>\n")]
|
||||
[TestCase("<div></div>\n\n# h")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,86 +1,84 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestIndentedCodeBlock
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestIndentedCodeBlock
|
||||
// A codeblock is indented with 4 spaces. After the 4th space, whitespace is interpreted as content.
|
||||
// l = line
|
||||
[TestCase(" l")]
|
||||
[TestCase(" l")]
|
||||
[TestCase("\tl")]
|
||||
[TestCase("\t\tl")]
|
||||
[TestCase("\tl1\n l1")]
|
||||
|
||||
[TestCase("\n l")]
|
||||
[TestCase("\n\n l")]
|
||||
[TestCase("\n l\n")]
|
||||
[TestCase("\n l\n\n")]
|
||||
[TestCase("\n\n l\n")]
|
||||
[TestCase("\n\n l\n\n")]
|
||||
|
||||
[TestCase(" l\n l")]
|
||||
[TestCase(" l\n l\n l")]
|
||||
|
||||
|
||||
// two newlines are needed for indented codeblock start after paragraph
|
||||
[TestCase("p\n\n l")]
|
||||
[TestCase("p\n\n l\n")]
|
||||
[TestCase("p\n\n l\n\n")]
|
||||
|
||||
[TestCase("p\n\n l\n l")]
|
||||
[TestCase("p\n\n l\n l")]
|
||||
|
||||
[TestCase(" l\n\np\n\n l")]
|
||||
[TestCase(" l l\n\np\n\n l l")]
|
||||
public void Test(string value)
|
||||
{
|
||||
// A codeblock is indented with 4 spaces. After the 4th space, whitespace is interpreted as content.
|
||||
// l = line
|
||||
[TestCase(" l")]
|
||||
[TestCase(" l")]
|
||||
[TestCase("\tl")]
|
||||
[TestCase("\t\tl")]
|
||||
[TestCase("\tl1\n l1")]
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("\n l")]
|
||||
[TestCase("\n\n l")]
|
||||
[TestCase("\n l\n")]
|
||||
[TestCase("\n l\n\n")]
|
||||
[TestCase("\n\n l\n")]
|
||||
[TestCase("\n\n l\n\n")]
|
||||
[TestCase(" l\n")]
|
||||
[TestCase(" l\r")]
|
||||
[TestCase(" l\r\n")]
|
||||
|
||||
[TestCase(" l\n l")]
|
||||
[TestCase(" l\n l\n l")]
|
||||
[TestCase(" l\n l")]
|
||||
[TestCase(" l\n l\n")]
|
||||
[TestCase(" l\n l\r")]
|
||||
[TestCase(" l\n l\r\n")]
|
||||
|
||||
[TestCase(" l\r l")]
|
||||
[TestCase(" l\r l\n")]
|
||||
[TestCase(" l\r l\r")]
|
||||
[TestCase(" l\r l\r\n")]
|
||||
|
||||
// two newlines are needed for indented codeblock start after paragraph
|
||||
[TestCase("p\n\n l")]
|
||||
[TestCase("p\n\n l\n")]
|
||||
[TestCase("p\n\n l\n\n")]
|
||||
[TestCase(" l\r\n l")]
|
||||
[TestCase(" l\r\n l\n")]
|
||||
[TestCase(" l\r\n l\r")]
|
||||
[TestCase(" l\r\n l\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("p\n\n l\n l")]
|
||||
[TestCase("p\n\n l\n l")]
|
||||
[TestCase(" l\n\n l\n")]
|
||||
[TestCase(" l\n\n\n l\n")]
|
||||
public void TestNewlinesInBetweenResultInOneCodeBlock(string value)
|
||||
{
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
pipelineBuilder.EnableTrackTrivia();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
var markdownDocument = Markdown.Parse(value, pipeline);
|
||||
|
||||
[TestCase(" l\n\np\n\n l")]
|
||||
[TestCase(" l l\n\np\n\n l l")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
Assert.AreEqual(1, markdownDocument.Count);
|
||||
}
|
||||
|
||||
[TestCase(" l\n")]
|
||||
[TestCase(" l\r")]
|
||||
[TestCase(" l\r\n")]
|
||||
|
||||
[TestCase(" l\n l")]
|
||||
[TestCase(" l\n l\n")]
|
||||
[TestCase(" l\n l\r")]
|
||||
[TestCase(" l\n l\r\n")]
|
||||
|
||||
[TestCase(" l\r l")]
|
||||
[TestCase(" l\r l\n")]
|
||||
[TestCase(" l\r l\r")]
|
||||
[TestCase(" l\r l\r\n")]
|
||||
|
||||
[TestCase(" l\r\n l")]
|
||||
[TestCase(" l\r\n l\n")]
|
||||
[TestCase(" l\r\n l\r")]
|
||||
[TestCase(" l\r\n l\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(" l\n\n l\n")]
|
||||
[TestCase(" l\n\n\n l\n")]
|
||||
public void TestNewlinesInBetweenResultInOneCodeBlock(string value)
|
||||
{
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
pipelineBuilder.EnableTrackTrivia();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
var markdownDocument = Markdown.Parse(value, pipeline);
|
||||
|
||||
Assert.AreEqual(1, markdownDocument.Count);
|
||||
}
|
||||
|
||||
[TestCase(" l\n\np")]
|
||||
[TestCase(" l\n\n\np")]
|
||||
[TestCase(" l\n\n\n\np")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase(" l\n\np")]
|
||||
[TestCase(" l\n\n\np")]
|
||||
[TestCase(" l\n\n\n\np")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,214 +1,212 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestLinkReferenceDefinition
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestLinkReferenceDefinition
|
||||
[TestCase(@"[a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
|
||||
[TestCase(@"[a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
|
||||
[TestCase(@"[a]: /r ")]
|
||||
[TestCase(@" [a]: /r ")]
|
||||
[TestCase(@" [a]: /r ")]
|
||||
[TestCase(@" [a]: /r ")]
|
||||
|
||||
[TestCase(@"[a]: /r ""l""")]
|
||||
[TestCase(@"[a]: /r ""l""")]
|
||||
[TestCase(@"[a]: /r ""l""")]
|
||||
[TestCase(@"[a]: /r ""l"" ")]
|
||||
[TestCase(@"[a]: /r ""l""")]
|
||||
[TestCase(@"[a]: /r ""l"" ")]
|
||||
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
|
||||
[TestCase("[a]:\t/r")]
|
||||
[TestCase("[a]:\t/r\t")]
|
||||
[TestCase("[a]:\t/r\t\"l\"")]
|
||||
[TestCase("[a]:\t/r\t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t/r")]
|
||||
[TestCase("[a]: \t/r\t")]
|
||||
[TestCase("[a]: \t/r\t\"l\"")]
|
||||
[TestCase("[a]: \t/r\t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t /r")]
|
||||
[TestCase("[a]:\t /r\t")]
|
||||
[TestCase("[a]:\t /r\t\"l\"")]
|
||||
[TestCase("[a]:\t /r\t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t /r")]
|
||||
[TestCase("[a]: \t /r\t")]
|
||||
[TestCase("[a]: \t /r\t\"l\"")]
|
||||
[TestCase("[a]: \t /r\t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t/r \t")]
|
||||
[TestCase("[a]:\t/r \t\"l\"")]
|
||||
[TestCase("[a]:\t/r \t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t/r")]
|
||||
[TestCase("[a]: \t/r \t")]
|
||||
[TestCase("[a]: \t/r \t\"l\"")]
|
||||
[TestCase("[a]: \t/r \t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t /r")]
|
||||
[TestCase("[a]:\t /r \t")]
|
||||
[TestCase("[a]:\t /r \t\"l\"")]
|
||||
[TestCase("[a]:\t /r \t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t /r")]
|
||||
[TestCase("[a]: \t /r \t")]
|
||||
[TestCase("[a]: \t /r \t\"l\"")]
|
||||
[TestCase("[a]: \t /r \t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t/r\t ")]
|
||||
[TestCase("[a]:\t/r\t \"l\"")]
|
||||
[TestCase("[a]:\t/r\t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t/r")]
|
||||
[TestCase("[a]: \t/r\t ")]
|
||||
[TestCase("[a]: \t/r\t \"l\"")]
|
||||
[TestCase("[a]: \t/r\t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t /r")]
|
||||
[TestCase("[a]:\t /r\t ")]
|
||||
[TestCase("[a]:\t /r\t \"l\"")]
|
||||
[TestCase("[a]:\t /r\t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t /r")]
|
||||
[TestCase("[a]: \t /r\t ")]
|
||||
[TestCase("[a]: \t /r\t \"l\"")]
|
||||
[TestCase("[a]: \t /r\t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t/r \t ")]
|
||||
[TestCase("[a]:\t/r \t \"l\"")]
|
||||
[TestCase("[a]:\t/r \t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t/r")]
|
||||
[TestCase("[a]: \t/r \t ")]
|
||||
[TestCase("[a]: \t/r \t \"l\"")]
|
||||
[TestCase("[a]: \t/r \t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t /r")]
|
||||
[TestCase("[a]:\t /r \t ")]
|
||||
[TestCase("[a]:\t /r \t \"l\"")]
|
||||
[TestCase("[a]:\t /r \t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t /r")]
|
||||
[TestCase("[a]: \t /r \t ")]
|
||||
[TestCase("[a]: \t /r \t \"l\"")]
|
||||
[TestCase("[a]: \t /r \t \"l\"\t")]
|
||||
public void Test(string value)
|
||||
{
|
||||
[TestCase(@"[a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(@"[a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase(@" [a]: /r")]
|
||||
[TestCase("[a]: /r\n[b]: /r\n")]
|
||||
[TestCase("[a]: /r\n[b]: /r\n[c] /r\n")]
|
||||
public void TestMultiple(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(@"[a]: /r ")]
|
||||
[TestCase(@" [a]: /r ")]
|
||||
[TestCase(@" [a]: /r ")]
|
||||
[TestCase(@" [a]: /r ")]
|
||||
[TestCase("[a]:\f/r\f\"l\"")]
|
||||
[TestCase("[a]:\v/r\v\"l\"")]
|
||||
public void TestUncommonWhitespace(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(@"[a]: /r ""l""")]
|
||||
[TestCase(@"[a]: /r ""l""")]
|
||||
[TestCase(@"[a]: /r ""l""")]
|
||||
[TestCase(@"[a]: /r ""l"" ")]
|
||||
[TestCase(@"[a]: /r ""l""")]
|
||||
[TestCase(@"[a]: /r ""l"" ")]
|
||||
[TestCase("[a]:\n/r\n\"t\"")]
|
||||
[TestCase("[a]:\n/r\r\"t\"")]
|
||||
[TestCase("[a]:\n/r\r\n\"t\"")]
|
||||
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
[TestCase("[a]:\r/r\n\"t\"")]
|
||||
[TestCase("[a]:\r/r\r\"t\"")]
|
||||
[TestCase("[a]:\r/r\r\n\"t\"")]
|
||||
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
[TestCase("[a]:\r\n/r\n\"t\"")]
|
||||
[TestCase("[a]:\r\n/r\r\"t\"")]
|
||||
[TestCase("[a]:\r\n/r\r\n\"t\"")]
|
||||
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
[TestCase(@" [a]: /r ""l""")]
|
||||
[TestCase(@" [a]: /r ""l"" ")]
|
||||
[TestCase("[a]:\n/r\n\"t\nt\"")]
|
||||
[TestCase("[a]:\n/r\n\"t\rt\"")]
|
||||
[TestCase("[a]:\n/r\n\"t\r\nt\"")]
|
||||
|
||||
[TestCase("[a]:\t/r")]
|
||||
[TestCase("[a]:\t/r\t")]
|
||||
[TestCase("[a]:\t/r\t\"l\"")]
|
||||
[TestCase("[a]:\t/r\t\"l\"\t")]
|
||||
[TestCase("[a]:\r\n /r\t \n \t \"t\r\nt\" ")]
|
||||
[TestCase("[a]:\n/r\n\n[a],")]
|
||||
[TestCase("[a]: /r\n[b]: /r\n\n[a],")]
|
||||
public void TestNewlines(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]: \t/r")]
|
||||
[TestCase("[a]: \t/r\t")]
|
||||
[TestCase("[a]: \t/r\t\"l\"")]
|
||||
[TestCase("[a]: \t/r\t\"l\"\t")]
|
||||
[TestCase("[ a]: /r")]
|
||||
[TestCase("[a ]: /r")]
|
||||
[TestCase("[ a ]: /r")]
|
||||
[TestCase("[ a]: /r")]
|
||||
[TestCase("[ a ]: /r")]
|
||||
[TestCase("[a ]: /r")]
|
||||
[TestCase("[ a ]: /r")]
|
||||
[TestCase("[ a ]: /r")]
|
||||
[TestCase("[a a]: /r")]
|
||||
[TestCase("[a\va]: /r")]
|
||||
[TestCase("[a\fa]: /r")]
|
||||
[TestCase("[a\ta]: /r")]
|
||||
[TestCase("[\va]: /r")]
|
||||
[TestCase("[\fa]: /r")]
|
||||
[TestCase("[\ta]: /r")]
|
||||
[TestCase(@"[\]]: /r")]
|
||||
public void TestLabel(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]:\t /r")]
|
||||
[TestCase("[a]:\t /r\t")]
|
||||
[TestCase("[a]:\t /r\t\"l\"")]
|
||||
[TestCase("[a]:\t /r\t\"l\"\t")]
|
||||
[TestCase("[a]: /r ()")]
|
||||
[TestCase("[a]: /r (t)")]
|
||||
[TestCase("[a]: /r ( t)")]
|
||||
[TestCase("[a]: /r (t )")]
|
||||
[TestCase("[a]: /r ( t )")]
|
||||
|
||||
[TestCase("[a]: \t /r")]
|
||||
[TestCase("[a]: \t /r\t")]
|
||||
[TestCase("[a]: \t /r\t\"l\"")]
|
||||
[TestCase("[a]: \t /r\t\"l\"\t")]
|
||||
[TestCase("[a]: /r ''")]
|
||||
[TestCase("[a]: /r 't'")]
|
||||
[TestCase("[a]: /r ' t'")]
|
||||
[TestCase("[a]: /r 't '")]
|
||||
[TestCase("[a]: /r ' t '")]
|
||||
public void Test_Title(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]:\t/r \t")]
|
||||
[TestCase("[a]:\t/r \t\"l\"")]
|
||||
[TestCase("[a]:\t/r \t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t/r")]
|
||||
[TestCase("[a]: \t/r \t")]
|
||||
[TestCase("[a]: \t/r \t\"l\"")]
|
||||
[TestCase("[a]: \t/r \t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t /r")]
|
||||
[TestCase("[a]:\t /r \t")]
|
||||
[TestCase("[a]:\t /r \t\"l\"")]
|
||||
[TestCase("[a]:\t /r \t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t /r")]
|
||||
[TestCase("[a]: \t /r \t")]
|
||||
[TestCase("[a]: \t /r \t\"l\"")]
|
||||
[TestCase("[a]: \t /r \t\"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t/r\t ")]
|
||||
[TestCase("[a]:\t/r\t \"l\"")]
|
||||
[TestCase("[a]:\t/r\t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t/r")]
|
||||
[TestCase("[a]: \t/r\t ")]
|
||||
[TestCase("[a]: \t/r\t \"l\"")]
|
||||
[TestCase("[a]: \t/r\t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t /r")]
|
||||
[TestCase("[a]:\t /r\t ")]
|
||||
[TestCase("[a]:\t /r\t \"l\"")]
|
||||
[TestCase("[a]:\t /r\t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t /r")]
|
||||
[TestCase("[a]: \t /r\t ")]
|
||||
[TestCase("[a]: \t /r\t \"l\"")]
|
||||
[TestCase("[a]: \t /r\t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t/r \t ")]
|
||||
[TestCase("[a]:\t/r \t \"l\"")]
|
||||
[TestCase("[a]:\t/r \t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t/r")]
|
||||
[TestCase("[a]: \t/r \t ")]
|
||||
[TestCase("[a]: \t/r \t \"l\"")]
|
||||
[TestCase("[a]: \t/r \t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]:\t /r")]
|
||||
[TestCase("[a]:\t /r \t ")]
|
||||
[TestCase("[a]:\t /r \t \"l\"")]
|
||||
[TestCase("[a]:\t /r \t \"l\"\t")]
|
||||
|
||||
[TestCase("[a]: \t /r")]
|
||||
[TestCase("[a]: \t /r \t ")]
|
||||
[TestCase("[a]: \t /r \t \"l\"")]
|
||||
[TestCase("[a]: \t /r \t \"l\"\t")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]: /r\n[b]: /r\n")]
|
||||
[TestCase("[a]: /r\n[b]: /r\n[c] /r\n")]
|
||||
public void TestMultiple(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]:\f/r\f\"l\"")]
|
||||
[TestCase("[a]:\v/r\v\"l\"")]
|
||||
public void TestUncommonWhitespace(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]:\n/r\n\"t\"")]
|
||||
[TestCase("[a]:\n/r\r\"t\"")]
|
||||
[TestCase("[a]:\n/r\r\n\"t\"")]
|
||||
|
||||
[TestCase("[a]:\r/r\n\"t\"")]
|
||||
[TestCase("[a]:\r/r\r\"t\"")]
|
||||
[TestCase("[a]:\r/r\r\n\"t\"")]
|
||||
|
||||
[TestCase("[a]:\r\n/r\n\"t\"")]
|
||||
[TestCase("[a]:\r\n/r\r\"t\"")]
|
||||
[TestCase("[a]:\r\n/r\r\n\"t\"")]
|
||||
|
||||
[TestCase("[a]:\n/r\n\"t\nt\"")]
|
||||
[TestCase("[a]:\n/r\n\"t\rt\"")]
|
||||
[TestCase("[a]:\n/r\n\"t\r\nt\"")]
|
||||
|
||||
[TestCase("[a]:\r\n /r\t \n \t \"t\r\nt\" ")]
|
||||
[TestCase("[a]:\n/r\n\n[a],")]
|
||||
[TestCase("[a]: /r\n[b]: /r\n\n[a],")]
|
||||
public void TestNewlines(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[ a]: /r")]
|
||||
[TestCase("[a ]: /r")]
|
||||
[TestCase("[ a ]: /r")]
|
||||
[TestCase("[ a]: /r")]
|
||||
[TestCase("[ a ]: /r")]
|
||||
[TestCase("[a ]: /r")]
|
||||
[TestCase("[ a ]: /r")]
|
||||
[TestCase("[ a ]: /r")]
|
||||
[TestCase("[a a]: /r")]
|
||||
[TestCase("[a\va]: /r")]
|
||||
[TestCase("[a\fa]: /r")]
|
||||
[TestCase("[a\ta]: /r")]
|
||||
[TestCase("[\va]: /r")]
|
||||
[TestCase("[\fa]: /r")]
|
||||
[TestCase("[\ta]: /r")]
|
||||
[TestCase(@"[\]]: /r")]
|
||||
public void TestLabel(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]: /r ()")]
|
||||
[TestCase("[a]: /r (t)")]
|
||||
[TestCase("[a]: /r ( t)")]
|
||||
[TestCase("[a]: /r (t )")]
|
||||
[TestCase("[a]: /r ( t )")]
|
||||
|
||||
[TestCase("[a]: /r ''")]
|
||||
[TestCase("[a]: /r 't'")]
|
||||
[TestCase("[a]: /r ' t'")]
|
||||
[TestCase("[a]: /r 't '")]
|
||||
[TestCase("[a]: /r ' t '")]
|
||||
public void Test_Title(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("[a]: /r\n===\n[a]")]
|
||||
public void TestSetextHeader(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("[a]: /r\n===\n[a]")]
|
||||
public void TestSetextHeader(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestNoBlocksFoundBlock
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestNoBlocksFoundBlock
|
||||
[TestCase("\r")]
|
||||
[TestCase("\n")]
|
||||
[TestCase("\r\n")]
|
||||
[TestCase("\t")]
|
||||
[TestCase("\v")]
|
||||
[TestCase("\f")]
|
||||
[TestCase(" ")]
|
||||
[TestCase(" ")]
|
||||
[TestCase(" ")]
|
||||
public void Test(string value)
|
||||
{
|
||||
[TestCase("\r")]
|
||||
[TestCase("\n")]
|
||||
[TestCase("\r\n")]
|
||||
[TestCase("\t")]
|
||||
[TestCase("\v")]
|
||||
[TestCase("\f")]
|
||||
[TestCase(" ")]
|
||||
[TestCase(" ")]
|
||||
[TestCase(" ")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,191 +1,189 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestOrderedList
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestOrderedList
|
||||
[TestCase("1. i")]
|
||||
[TestCase("1. i")]
|
||||
[TestCase("1. i ")]
|
||||
[TestCase("1. i ")]
|
||||
[TestCase("1. i ")]
|
||||
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
|
||||
[TestCase("1. i\n")]
|
||||
[TestCase("1. i\n")]
|
||||
[TestCase("1. i \n")]
|
||||
[TestCase("1. i \n")]
|
||||
[TestCase("1. i \n")]
|
||||
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
|
||||
[TestCase("1. i\n2. j")]
|
||||
[TestCase("1. i\n2. j")]
|
||||
[TestCase("1. i \n2. j")]
|
||||
[TestCase("1. i \n2. j")]
|
||||
[TestCase("1. i \n2. j")]
|
||||
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
|
||||
[TestCase("1. i\n2. j\n")]
|
||||
[TestCase("1. i\n2. j\n")]
|
||||
[TestCase("1. i \n2. j\n")]
|
||||
[TestCase("1. i \n2. j\n")]
|
||||
[TestCase("1. i \n2. j\n")]
|
||||
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
|
||||
[TestCase("1. i\n2. j\n3. k")]
|
||||
[TestCase("1. i\n2. j\n3. k\n")]
|
||||
public void Test(string value)
|
||||
{
|
||||
[TestCase("1. i")]
|
||||
[TestCase("1. i")]
|
||||
[TestCase("1. i ")]
|
||||
[TestCase("1. i ")]
|
||||
[TestCase("1. i ")]
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
[TestCase(" 1. i ")]
|
||||
|
||||
[TestCase("1. i\n")]
|
||||
[TestCase("1. i\n")]
|
||||
[TestCase("1. i \n")]
|
||||
[TestCase("1. i \n")]
|
||||
[TestCase("1. i \n")]
|
||||
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i\n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
[TestCase(" 1. i \n")]
|
||||
|
||||
[TestCase("1. i\n2. j")]
|
||||
[TestCase("1. i\n2. j")]
|
||||
[TestCase("1. i \n2. j")]
|
||||
[TestCase("1. i \n2. j")]
|
||||
[TestCase("1. i \n2. j")]
|
||||
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i\n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
[TestCase(" 1. i \n2. j")]
|
||||
|
||||
[TestCase("1. i\n2. j\n")]
|
||||
[TestCase("1. i\n2. j\n")]
|
||||
[TestCase("1. i \n2. j\n")]
|
||||
[TestCase("1. i \n2. j\n")]
|
||||
[TestCase("1. i \n2. j\n")]
|
||||
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i\n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
[TestCase(" 1. i \n2. j\n")]
|
||||
|
||||
[TestCase("1. i\n2. j\n3. k")]
|
||||
[TestCase("1. i\n2. j\n3. k\n")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("10. i")]
|
||||
[TestCase("11. i")]
|
||||
[TestCase("10. i\n12. i")]
|
||||
[TestCase("2. i\n3. i")]
|
||||
public void Test_MoreThenOneStart(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("10. i")]
|
||||
[TestCase("11. i")]
|
||||
[TestCase("10. i\n12. i")]
|
||||
[TestCase("2. i\n3. i")]
|
||||
public void Test_MoreThenOneStart(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
|
||||
[TestCase("\n1. i")]
|
||||
[TestCase("\r1. i")]
|
||||
[TestCase("\r\n1. i")]
|
||||
[TestCase("\n1. i")]
|
||||
[TestCase("\r1. i")]
|
||||
[TestCase("\r\n1. i")]
|
||||
|
||||
[TestCase("\n1. i\n")]
|
||||
[TestCase("\r1. i\n")]
|
||||
[TestCase("\r\n1. i\n")]
|
||||
[TestCase("\n1. i\n")]
|
||||
[TestCase("\r1. i\n")]
|
||||
[TestCase("\r\n1. i\n")]
|
||||
|
||||
[TestCase("\n1. i\r")]
|
||||
[TestCase("\r1. i\r")]
|
||||
[TestCase("\r\n1. i\r")]
|
||||
[TestCase("\n1. i\r")]
|
||||
[TestCase("\r1. i\r")]
|
||||
[TestCase("\r\n1. i\r")]
|
||||
|
||||
[TestCase("\n1. i\r\n")]
|
||||
[TestCase("\r1. i\r\n")]
|
||||
[TestCase("\r\n1. i\r\n")]
|
||||
[TestCase("\n1. i\r\n")]
|
||||
[TestCase("\r1. i\r\n")]
|
||||
[TestCase("\r\n1. i\r\n")]
|
||||
|
||||
[TestCase("1. i\n2. i")]
|
||||
[TestCase("\n1. i\n2. i")]
|
||||
[TestCase("\r1. i\n2. i")]
|
||||
[TestCase("\r\n1. i\n2. i")]
|
||||
[TestCase("1. i\n2. i")]
|
||||
[TestCase("\n1. i\n2. i")]
|
||||
[TestCase("\r1. i\n2. i")]
|
||||
[TestCase("\r\n1. i\n2. i")]
|
||||
|
||||
[TestCase("1. i\r2. i")]
|
||||
[TestCase("\n1. i\r2. i")]
|
||||
[TestCase("\r1. i\r2. i")]
|
||||
[TestCase("\r\n1. i\r2. i")]
|
||||
[TestCase("1. i\r2. i")]
|
||||
[TestCase("\n1. i\r2. i")]
|
||||
[TestCase("\r1. i\r2. i")]
|
||||
[TestCase("\r\n1. i\r2. i")]
|
||||
|
||||
[TestCase("1. i\r\n2. i")]
|
||||
[TestCase("\n1. i\r\n2. i")]
|
||||
[TestCase("\r1. i\r\n2. i")]
|
||||
[TestCase("\r\n1. i\r\n2. i")]
|
||||
[TestCase("1. i\r\n2. i")]
|
||||
[TestCase("\n1. i\r\n2. i")]
|
||||
[TestCase("\r1. i\r\n2. i")]
|
||||
[TestCase("\r\n1. i\r\n2. i")]
|
||||
|
||||
[TestCase("1. i\n2. i\n")]
|
||||
[TestCase("\n1. i\n2. i\n")]
|
||||
[TestCase("\r1. i\n2. i\n")]
|
||||
[TestCase("\r\n1. i\n2. i\n")]
|
||||
[TestCase("1. i\n2. i\n")]
|
||||
[TestCase("\n1. i\n2. i\n")]
|
||||
[TestCase("\r1. i\n2. i\n")]
|
||||
[TestCase("\r\n1. i\n2. i\n")]
|
||||
|
||||
[TestCase("1. i\r2. i\r")]
|
||||
[TestCase("\n1. i\r2. i\r")]
|
||||
[TestCase("\r1. i\r2. i\r")]
|
||||
[TestCase("\r\n1. i\r2. i\r")]
|
||||
[TestCase("1. i\r2. i\r")]
|
||||
[TestCase("\n1. i\r2. i\r")]
|
||||
[TestCase("\r1. i\r2. i\r")]
|
||||
[TestCase("\r\n1. i\r2. i\r")]
|
||||
|
||||
[TestCase("1. i\r\n2. i\r\n")]
|
||||
[TestCase("\n1. i\r\n2. i\r\n")]
|
||||
[TestCase("\r1. i\r\n2. i\r\n")]
|
||||
[TestCase("\r\n1. i\r\n2. i\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("1. i\r\n2. i\r\n")]
|
||||
[TestCase("\n1. i\r\n2. i\r\n")]
|
||||
[TestCase("\r1. i\r\n2. i\r\n")]
|
||||
[TestCase("\r\n1. i\r\n2. i\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("1. i\n 1. i")]
|
||||
[TestCase("1. i\n 1. i\n")]
|
||||
[TestCase("1. i\n 1. i\n 2. i")]
|
||||
[TestCase("1. i\n 2. i\n 3. i")]
|
||||
[TestCase("1. i\n 1. i")]
|
||||
[TestCase("1. i\n 1. i\n")]
|
||||
[TestCase("1. i\n 1. i\n 2. i")]
|
||||
[TestCase("1. i\n 2. i\n 3. i")]
|
||||
|
||||
[TestCase("1. i\n\t1. i")]
|
||||
[TestCase("1. i\n\t1. i\n2. i")]
|
||||
public void TestMultipleLevels(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("1. i\n\t1. i")]
|
||||
[TestCase("1. i\n\t1. i\n2. i")]
|
||||
public void TestMultipleLevels(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("1. c")]
|
||||
[TestCase("1. c")]
|
||||
public void Test_IndentedCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("1. c")]
|
||||
[TestCase("1. c")]
|
||||
public void Test_IndentedCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,247 +1,245 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestParagraph
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestParagraph
|
||||
[TestCase("p")]
|
||||
[TestCase(" p")]
|
||||
[TestCase("p ")]
|
||||
[TestCase(" p ")]
|
||||
|
||||
[TestCase("p\np")]
|
||||
[TestCase(" p\np")]
|
||||
[TestCase("p \np")]
|
||||
[TestCase(" p \np")]
|
||||
|
||||
[TestCase("p\n p")]
|
||||
[TestCase(" p\n p")]
|
||||
[TestCase("p \n p")]
|
||||
[TestCase(" p \n p")]
|
||||
|
||||
[TestCase("p\np ")]
|
||||
[TestCase(" p\np ")]
|
||||
[TestCase("p \np ")]
|
||||
[TestCase(" p \np ")]
|
||||
|
||||
[TestCase("p\n\n p ")]
|
||||
[TestCase(" p\n\n p ")]
|
||||
[TestCase("p \n\n p ")]
|
||||
[TestCase(" p \n\n p ")]
|
||||
|
||||
[TestCase("p\n\np")]
|
||||
[TestCase(" p\n\np")]
|
||||
[TestCase("p \n\np")]
|
||||
[TestCase(" p \n\np")]
|
||||
|
||||
[TestCase("p\n\n p")]
|
||||
[TestCase(" p\n\n p")]
|
||||
[TestCase("p \n\n p")]
|
||||
[TestCase(" p \n\n p")]
|
||||
|
||||
[TestCase("p\n\np ")]
|
||||
[TestCase(" p\n\np ")]
|
||||
[TestCase("p \n\np ")]
|
||||
[TestCase(" p \n\np ")]
|
||||
|
||||
[TestCase("p\n\n p ")]
|
||||
[TestCase(" p\n\n p ")]
|
||||
[TestCase("p \n\n p ")]
|
||||
[TestCase(" p \n\n p ")]
|
||||
|
||||
[TestCase("\np")]
|
||||
[TestCase("\n p")]
|
||||
[TestCase("\np ")]
|
||||
[TestCase("\n p ")]
|
||||
|
||||
[TestCase("\np\np")]
|
||||
[TestCase("\n p\np")]
|
||||
[TestCase("\np \np")]
|
||||
[TestCase("\n p \np")]
|
||||
|
||||
[TestCase("\np\n p")]
|
||||
[TestCase("\n p\n p")]
|
||||
[TestCase("\np \n p")]
|
||||
[TestCase("\n p \n p")]
|
||||
|
||||
[TestCase("\np\np ")]
|
||||
[TestCase("\n p\np ")]
|
||||
[TestCase("\np \np ")]
|
||||
[TestCase("\n p \np ")]
|
||||
|
||||
[TestCase("\np\n\n p ")]
|
||||
[TestCase("\n p\n\n p ")]
|
||||
[TestCase("\np \n\n p ")]
|
||||
[TestCase("\n p \n\n p ")]
|
||||
|
||||
[TestCase("\np\n\np")]
|
||||
[TestCase("\n p\n\np")]
|
||||
[TestCase("\np \n\np")]
|
||||
[TestCase("\n p \n\np")]
|
||||
|
||||
[TestCase("\np\n\n p")]
|
||||
[TestCase("\n p\n\n p")]
|
||||
[TestCase("\np \n\n p")]
|
||||
[TestCase("\n p \n\n p")]
|
||||
|
||||
[TestCase("\np\n\np ")]
|
||||
[TestCase("\n p\n\np ")]
|
||||
[TestCase("\np \n\np ")]
|
||||
[TestCase("\n p \n\np ")]
|
||||
|
||||
[TestCase("\np\n\n p ")]
|
||||
[TestCase("\n p\n\n p ")]
|
||||
[TestCase("\np \n\n p ")]
|
||||
[TestCase("\n p \n\n p ")]
|
||||
|
||||
[TestCase("p p")]
|
||||
[TestCase("p\tp")]
|
||||
[TestCase("p \tp")]
|
||||
[TestCase("p \t p")]
|
||||
[TestCase("p \tp")]
|
||||
|
||||
// special cases
|
||||
[TestCase(" p \n\n\n\n p \n\n")]
|
||||
[TestCase("\n\np")]
|
||||
[TestCase("p\n")]
|
||||
[TestCase("p\n\n")]
|
||||
[TestCase("p\np\n p")]
|
||||
[TestCase("p\np\n p\n p")]
|
||||
public void Test(string value)
|
||||
{
|
||||
[TestCase("p")]
|
||||
[TestCase(" p")]
|
||||
[TestCase("p ")]
|
||||
[TestCase(" p ")]
|
||||
|
||||
[TestCase("p\np")]
|
||||
[TestCase(" p\np")]
|
||||
[TestCase("p \np")]
|
||||
[TestCase(" p \np")]
|
||||
|
||||
[TestCase("p\n p")]
|
||||
[TestCase(" p\n p")]
|
||||
[TestCase("p \n p")]
|
||||
[TestCase(" p \n p")]
|
||||
|
||||
[TestCase("p\np ")]
|
||||
[TestCase(" p\np ")]
|
||||
[TestCase("p \np ")]
|
||||
[TestCase(" p \np ")]
|
||||
|
||||
[TestCase("p\n\n p ")]
|
||||
[TestCase(" p\n\n p ")]
|
||||
[TestCase("p \n\n p ")]
|
||||
[TestCase(" p \n\n p ")]
|
||||
|
||||
[TestCase("p\n\np")]
|
||||
[TestCase(" p\n\np")]
|
||||
[TestCase("p \n\np")]
|
||||
[TestCase(" p \n\np")]
|
||||
|
||||
[TestCase("p\n\n p")]
|
||||
[TestCase(" p\n\n p")]
|
||||
[TestCase("p \n\n p")]
|
||||
[TestCase(" p \n\n p")]
|
||||
|
||||
[TestCase("p\n\np ")]
|
||||
[TestCase(" p\n\np ")]
|
||||
[TestCase("p \n\np ")]
|
||||
[TestCase(" p \n\np ")]
|
||||
|
||||
[TestCase("p\n\n p ")]
|
||||
[TestCase(" p\n\n p ")]
|
||||
[TestCase("p \n\n p ")]
|
||||
[TestCase(" p \n\n p ")]
|
||||
|
||||
[TestCase("\np")]
|
||||
[TestCase("\n p")]
|
||||
[TestCase("\np ")]
|
||||
[TestCase("\n p ")]
|
||||
|
||||
[TestCase("\np\np")]
|
||||
[TestCase("\n p\np")]
|
||||
[TestCase("\np \np")]
|
||||
[TestCase("\n p \np")]
|
||||
|
||||
[TestCase("\np\n p")]
|
||||
[TestCase("\n p\n p")]
|
||||
[TestCase("\np \n p")]
|
||||
[TestCase("\n p \n p")]
|
||||
|
||||
[TestCase("\np\np ")]
|
||||
[TestCase("\n p\np ")]
|
||||
[TestCase("\np \np ")]
|
||||
[TestCase("\n p \np ")]
|
||||
|
||||
[TestCase("\np\n\n p ")]
|
||||
[TestCase("\n p\n\n p ")]
|
||||
[TestCase("\np \n\n p ")]
|
||||
[TestCase("\n p \n\n p ")]
|
||||
|
||||
[TestCase("\np\n\np")]
|
||||
[TestCase("\n p\n\np")]
|
||||
[TestCase("\np \n\np")]
|
||||
[TestCase("\n p \n\np")]
|
||||
|
||||
[TestCase("\np\n\n p")]
|
||||
[TestCase("\n p\n\n p")]
|
||||
[TestCase("\np \n\n p")]
|
||||
[TestCase("\n p \n\n p")]
|
||||
|
||||
[TestCase("\np\n\np ")]
|
||||
[TestCase("\n p\n\np ")]
|
||||
[TestCase("\np \n\np ")]
|
||||
[TestCase("\n p \n\np ")]
|
||||
|
||||
[TestCase("\np\n\n p ")]
|
||||
[TestCase("\n p\n\n p ")]
|
||||
[TestCase("\np \n\n p ")]
|
||||
[TestCase("\n p \n\n p ")]
|
||||
|
||||
[TestCase("p p")]
|
||||
[TestCase("p\tp")]
|
||||
[TestCase("p \tp")]
|
||||
[TestCase("p \t p")]
|
||||
[TestCase("p \tp")]
|
||||
|
||||
// special cases
|
||||
[TestCase(" p \n\n\n\n p \n\n")]
|
||||
[TestCase("\n\np")]
|
||||
[TestCase("p\n")]
|
||||
[TestCase("p\n\n")]
|
||||
[TestCase("p\np\n p")]
|
||||
[TestCase("p\np\n p\n p")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
|
||||
[TestCase("\n")]
|
||||
[TestCase("\r\n")]
|
||||
[TestCase("\r")]
|
||||
[TestCase("\n")]
|
||||
[TestCase("\r\n")]
|
||||
[TestCase("\r")]
|
||||
|
||||
[TestCase("p\n")]
|
||||
[TestCase("p\r")]
|
||||
[TestCase("p\r\n")]
|
||||
[TestCase("p\n")]
|
||||
[TestCase("p\r")]
|
||||
[TestCase("p\r\n")]
|
||||
|
||||
[TestCase("p\np")]
|
||||
[TestCase("p\rp")]
|
||||
[TestCase("p\r\np")]
|
||||
[TestCase("p\np")]
|
||||
[TestCase("p\rp")]
|
||||
[TestCase("p\r\np")]
|
||||
|
||||
[TestCase("p\np\n")]
|
||||
[TestCase("p\rp\n")]
|
||||
[TestCase("p\r\np\n")]
|
||||
[TestCase("p\np\n")]
|
||||
[TestCase("p\rp\n")]
|
||||
[TestCase("p\r\np\n")]
|
||||
|
||||
[TestCase("p\np\r")]
|
||||
[TestCase("p\rp\r")]
|
||||
[TestCase("p\r\np\r")]
|
||||
[TestCase("p\np\r")]
|
||||
[TestCase("p\rp\r")]
|
||||
[TestCase("p\r\np\r")]
|
||||
|
||||
[TestCase("p\np\r\n")]
|
||||
[TestCase("p\rp\r\n")]
|
||||
[TestCase("p\r\np\r\n")]
|
||||
[TestCase("p\np\r\n")]
|
||||
[TestCase("p\rp\r\n")]
|
||||
[TestCase("p\r\np\r\n")]
|
||||
|
||||
[TestCase("\np\n")]
|
||||
[TestCase("\np\r")]
|
||||
[TestCase("\np\r\n")]
|
||||
[TestCase("\np\n")]
|
||||
[TestCase("\np\r")]
|
||||
[TestCase("\np\r\n")]
|
||||
|
||||
[TestCase("\np\np")]
|
||||
[TestCase("\np\rp")]
|
||||
[TestCase("\np\r\np")]
|
||||
[TestCase("\np\np")]
|
||||
[TestCase("\np\rp")]
|
||||
[TestCase("\np\r\np")]
|
||||
|
||||
[TestCase("\np\np\n")]
|
||||
[TestCase("\np\rp\n")]
|
||||
[TestCase("\np\r\np\n")]
|
||||
[TestCase("\np\np\n")]
|
||||
[TestCase("\np\rp\n")]
|
||||
[TestCase("\np\r\np\n")]
|
||||
|
||||
[TestCase("\np\np\r")]
|
||||
[TestCase("\np\rp\r")]
|
||||
[TestCase("\np\r\np\r")]
|
||||
[TestCase("\np\np\r")]
|
||||
[TestCase("\np\rp\r")]
|
||||
[TestCase("\np\r\np\r")]
|
||||
|
||||
[TestCase("\np\np\r\n")]
|
||||
[TestCase("\np\rp\r\n")]
|
||||
[TestCase("\np\r\np\r\n")]
|
||||
[TestCase("\np\np\r\n")]
|
||||
[TestCase("\np\rp\r\n")]
|
||||
[TestCase("\np\r\np\r\n")]
|
||||
|
||||
[TestCase("\rp\n")]
|
||||
[TestCase("\rp\r")]
|
||||
[TestCase("\rp\r\n")]
|
||||
[TestCase("\rp\n")]
|
||||
[TestCase("\rp\r")]
|
||||
[TestCase("\rp\r\n")]
|
||||
|
||||
[TestCase("\rp\np")]
|
||||
[TestCase("\rp\rp")]
|
||||
[TestCase("\rp\r\np")]
|
||||
[TestCase("\rp\np")]
|
||||
[TestCase("\rp\rp")]
|
||||
[TestCase("\rp\r\np")]
|
||||
|
||||
[TestCase("\rp\np\n")]
|
||||
[TestCase("\rp\rp\n")]
|
||||
[TestCase("\rp\r\np\n")]
|
||||
[TestCase("\rp\np\n")]
|
||||
[TestCase("\rp\rp\n")]
|
||||
[TestCase("\rp\r\np\n")]
|
||||
|
||||
[TestCase("\rp\np\r")]
|
||||
[TestCase("\rp\rp\r")]
|
||||
[TestCase("\rp\r\np\r")]
|
||||
[TestCase("\rp\np\r")]
|
||||
[TestCase("\rp\rp\r")]
|
||||
[TestCase("\rp\r\np\r")]
|
||||
|
||||
[TestCase("\rp\np\r\n")]
|
||||
[TestCase("\rp\rp\r\n")]
|
||||
[TestCase("\rp\r\np\r\n")]
|
||||
[TestCase("\rp\np\r\n")]
|
||||
[TestCase("\rp\rp\r\n")]
|
||||
[TestCase("\rp\r\np\r\n")]
|
||||
|
||||
[TestCase("\r\np\n")]
|
||||
[TestCase("\r\np\r")]
|
||||
[TestCase("\r\np\r\n")]
|
||||
[TestCase("\r\np\n")]
|
||||
[TestCase("\r\np\r")]
|
||||
[TestCase("\r\np\r\n")]
|
||||
|
||||
[TestCase("\r\np\np")]
|
||||
[TestCase("\r\np\rp")]
|
||||
[TestCase("\r\np\r\np")]
|
||||
[TestCase("\r\np\np")]
|
||||
[TestCase("\r\np\rp")]
|
||||
[TestCase("\r\np\r\np")]
|
||||
|
||||
[TestCase("\r\np\np\n")]
|
||||
[TestCase("\r\np\rp\n")]
|
||||
[TestCase("\r\np\r\np\n")]
|
||||
[TestCase("\r\np\np\n")]
|
||||
[TestCase("\r\np\rp\n")]
|
||||
[TestCase("\r\np\r\np\n")]
|
||||
|
||||
[TestCase("\r\np\np\r")]
|
||||
[TestCase("\r\np\rp\r")]
|
||||
[TestCase("\r\np\r\np\r")]
|
||||
[TestCase("\r\np\np\r")]
|
||||
[TestCase("\r\np\rp\r")]
|
||||
[TestCase("\r\np\r\np\r")]
|
||||
|
||||
[TestCase("\r\np\np\r\n")]
|
||||
[TestCase("\r\np\rp\r\n")]
|
||||
[TestCase("\r\np\r\np\r\n")]
|
||||
[TestCase("\r\np\np\r\n")]
|
||||
[TestCase("\r\np\rp\r\n")]
|
||||
[TestCase("\r\np\r\np\r\n")]
|
||||
|
||||
[TestCase("p\n")]
|
||||
[TestCase("p\n\n")]
|
||||
[TestCase("p\n\n\n")]
|
||||
[TestCase("p\n\n\n\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("p\n")]
|
||||
[TestCase("p\n\n")]
|
||||
[TestCase("p\n\n\n")]
|
||||
[TestCase("p\n\n\n\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(" \n")]
|
||||
[TestCase(" \r")]
|
||||
[TestCase(" \r\n")]
|
||||
[TestCase(" \n")]
|
||||
[TestCase(" \r")]
|
||||
[TestCase(" \r\n")]
|
||||
|
||||
[TestCase(" \np")]
|
||||
[TestCase(" \rp")]
|
||||
[TestCase(" \r\np")]
|
||||
[TestCase(" \np")]
|
||||
[TestCase(" \rp")]
|
||||
[TestCase(" \r\np")]
|
||||
|
||||
[TestCase(" \np")]
|
||||
[TestCase(" \rp")]
|
||||
[TestCase(" \r\np")]
|
||||
[TestCase(" \np")]
|
||||
[TestCase(" \rp")]
|
||||
[TestCase(" \r\np")]
|
||||
|
||||
[TestCase(" \np")]
|
||||
[TestCase(" \rp")]
|
||||
[TestCase(" \r\np")]
|
||||
[TestCase(" \np")]
|
||||
[TestCase(" \rp")]
|
||||
[TestCase(" \r\np")]
|
||||
|
||||
[TestCase(" \n ")]
|
||||
[TestCase(" \r ")]
|
||||
[TestCase(" \r\n ")]
|
||||
[TestCase(" \n ")]
|
||||
[TestCase(" \r ")]
|
||||
[TestCase(" \r\n ")]
|
||||
|
||||
[TestCase(" \np ")]
|
||||
[TestCase(" \rp ")]
|
||||
[TestCase(" \r\np ")]
|
||||
[TestCase(" \np ")]
|
||||
[TestCase(" \rp ")]
|
||||
[TestCase(" \r\np ")]
|
||||
|
||||
[TestCase(" \np ")]
|
||||
[TestCase(" \rp ")]
|
||||
[TestCase(" \r\np ")]
|
||||
[TestCase(" \np ")]
|
||||
[TestCase(" \rp ")]
|
||||
[TestCase(" \r\np ")]
|
||||
|
||||
[TestCase(" \np ")]
|
||||
[TestCase(" \rp ")]
|
||||
[TestCase(" \r\np ")]
|
||||
public void Test_WhitespaceWithNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase(" \np ")]
|
||||
[TestCase(" \rp ")]
|
||||
[TestCase(" \r\np ")]
|
||||
public void Test_WhitespaceWithNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,286 +1,284 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestQuoteBlock
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestQuoteBlock
|
||||
[TestCase(">q")]
|
||||
[TestCase(" >q")]
|
||||
[TestCase(" >q")]
|
||||
[TestCase(" >q")]
|
||||
[TestCase("> q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase("> q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase(" > q")]
|
||||
|
||||
[TestCase(">q\n>q")]
|
||||
[TestCase(">q\n >q")]
|
||||
[TestCase(">q\n >q")]
|
||||
[TestCase(">q\n >q")]
|
||||
[TestCase(">q\n> q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n> q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n > q")]
|
||||
|
||||
[TestCase(" >q\n>q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n> q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n> q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
|
||||
[TestCase(" >q\n>q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n> q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n> q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
|
||||
[TestCase("> q\n>q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n> q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n> q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
|
||||
[TestCase("> q\n>q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n> q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n> q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
|
||||
[TestCase(">q\n>q\n>q")]
|
||||
[TestCase(">q\n>\n>q")]
|
||||
[TestCase(">q\np\n>q")]
|
||||
[TestCase(">q\n>\n>\n>q")]
|
||||
[TestCase(">q\n>\n>\n>\n>q")]
|
||||
[TestCase(">q\n>\n>q\n>\n>q")]
|
||||
[TestCase("p\n\n> **q**\n>p\n")]
|
||||
|
||||
[TestCase("> q\np\n> q")] // lazy
|
||||
[TestCase("> q\n> q\np")] // lazy
|
||||
|
||||
[TestCase(">>q")]
|
||||
[TestCase(" > > q")]
|
||||
|
||||
[TestCase("> **q**\n>p\n")]
|
||||
[TestCase("> **q**")]
|
||||
public void Test(string value)
|
||||
{
|
||||
[TestCase(">q")]
|
||||
[TestCase(" >q")]
|
||||
[TestCase(" >q")]
|
||||
[TestCase(" >q")]
|
||||
[TestCase("> q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase("> q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase(" > q")]
|
||||
[TestCase(" > q")]
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(">q\n>q")]
|
||||
[TestCase(">q\n >q")]
|
||||
[TestCase(">q\n >q")]
|
||||
[TestCase(">q\n >q")]
|
||||
[TestCase(">q\n> q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n> q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase(">q\n > q")]
|
||||
[TestCase("> q")] // 5
|
||||
[TestCase("> q")] // 6
|
||||
[TestCase(" > q")] //5
|
||||
[TestCase(" > q")] //6
|
||||
[TestCase(" > \tq")]
|
||||
[TestCase("> q\n> q")] // 5, 5
|
||||
[TestCase("> q\n> q")] // 5, 6
|
||||
[TestCase("> q\n> q")] // 6, 5
|
||||
[TestCase("> q\n> q")] // 6, 6
|
||||
[TestCase("> q\n\n> 5")] // 5, 5
|
||||
public void TestIndentedCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(" >q\n>q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n> q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n> q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase("\n> q")]
|
||||
[TestCase("\n> q\n")]
|
||||
[TestCase("\n> q\n\n")]
|
||||
[TestCase("> q\n\np")]
|
||||
[TestCase("p\n\n> q\n\n# h")]
|
||||
|
||||
[TestCase(" >q\n>q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n >q")]
|
||||
[TestCase(" >q\n> q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n> q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
[TestCase(" >q\n > q")]
|
||||
//https://github.com/lunet-io/markdig/issues/480
|
||||
//[TestCase(">\np")]
|
||||
//[TestCase(">**b**\n>\n>p\n>\np\n")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("> q\n>q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n> q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n> q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n\n# h\n")]
|
||||
public void TestAtxHeader(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(">- i")]
|
||||
[TestCase("> - i")]
|
||||
[TestCase(">- i\n>- i")]
|
||||
[TestCase(">- >p")]
|
||||
[TestCase("> - >p")]
|
||||
[TestCase(">- i1\n>- i2\n")]
|
||||
[TestCase("> **p** p\n>- i1\n>- i2\n")]
|
||||
public void TestUnorderedList(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase("> *q*\n>p\n")]
|
||||
[TestCase("> *q*")]
|
||||
public void TestEmphasis(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase("> **q**\n>p\n")]
|
||||
[TestCase("> **q**")]
|
||||
public void TestStrongEmphasis(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("> q\n>q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n >q")]
|
||||
[TestCase("> q\n> q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n> q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase("> q\n > q")]
|
||||
[TestCase(">p\n")]
|
||||
[TestCase(">p\r")]
|
||||
[TestCase(">p\r\n")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(">p\n>p")]
|
||||
[TestCase(">p\r>p")]
|
||||
[TestCase(">p\r\n>p")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(">p\n>p\n")]
|
||||
[TestCase(">p\r>p\n")]
|
||||
[TestCase(">p\r\n>p\n")]
|
||||
|
||||
[TestCase(" > q\n>q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n >q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n> q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(" > q\n > q")]
|
||||
[TestCase(">p\n>p\r")]
|
||||
[TestCase(">p\r>p\r")]
|
||||
[TestCase(">p\r\n>p\r")]
|
||||
|
||||
[TestCase(">q\n>q\n>q")]
|
||||
[TestCase(">q\n>\n>q")]
|
||||
[TestCase(">q\np\n>q")]
|
||||
[TestCase(">q\n>\n>\n>q")]
|
||||
[TestCase(">q\n>\n>\n>\n>q")]
|
||||
[TestCase(">q\n>\n>q\n>\n>q")]
|
||||
[TestCase("p\n\n> **q**\n>p\n")]
|
||||
[TestCase(">p\n>p\r\n")]
|
||||
[TestCase(">p\r>p\r\n")]
|
||||
[TestCase(">p\r\n>p\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("> q\np\n> q")] // lazy
|
||||
[TestCase("> q\n> q\np")] // lazy
|
||||
|
||||
[TestCase(">>q")]
|
||||
[TestCase(" > > q")]
|
||||
|
||||
[TestCase("> **q**\n>p\n")]
|
||||
[TestCase("> **q**")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("> q")] // 5
|
||||
[TestCase("> q")] // 6
|
||||
[TestCase(" > q")] //5
|
||||
[TestCase(" > q")] //6
|
||||
[TestCase(" > \tq")]
|
||||
[TestCase("> q\n> q")] // 5, 5
|
||||
[TestCase("> q\n> q")] // 5, 6
|
||||
[TestCase("> q\n> q")] // 6, 5
|
||||
[TestCase("> q\n> q")] // 6, 6
|
||||
[TestCase("> q\n\n> 5")] // 5, 5
|
||||
public void TestIndentedCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("\n> q")]
|
||||
[TestCase("\n> q\n")]
|
||||
[TestCase("\n> q\n\n")]
|
||||
[TestCase("> q\n\np")]
|
||||
[TestCase("p\n\n> q\n\n# h")]
|
||||
|
||||
//https://github.com/lunet-io/markdig/issues/480
|
||||
//[TestCase(">\np")]
|
||||
//[TestCase(">**b**\n>\n>p\n>\np\n")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("> q\n\n# h\n")]
|
||||
public void TestAtxHeader(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(">- i")]
|
||||
[TestCase("> - i")]
|
||||
[TestCase(">- i\n>- i")]
|
||||
[TestCase(">- >p")]
|
||||
[TestCase("> - >p")]
|
||||
[TestCase(">- i1\n>- i2\n")]
|
||||
[TestCase("> **p** p\n>- i1\n>- i2\n")]
|
||||
public void TestUnorderedList(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("> *q*\n>p\n")]
|
||||
[TestCase("> *q*")]
|
||||
public void TestEmphasis(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("> **q**\n>p\n")]
|
||||
[TestCase("> **q**")]
|
||||
public void TestStrongEmphasis(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(">p\n")]
|
||||
[TestCase(">p\r")]
|
||||
[TestCase(">p\r\n")]
|
||||
|
||||
[TestCase(">p\n>p")]
|
||||
[TestCase(">p\r>p")]
|
||||
[TestCase(">p\r\n>p")]
|
||||
|
||||
[TestCase(">p\n>p\n")]
|
||||
[TestCase(">p\r>p\n")]
|
||||
[TestCase(">p\r\n>p\n")]
|
||||
|
||||
[TestCase(">p\n>p\r")]
|
||||
[TestCase(">p\r>p\r")]
|
||||
[TestCase(">p\r\n>p\r")]
|
||||
|
||||
[TestCase(">p\n>p\r\n")]
|
||||
[TestCase(">p\r>p\r\n")]
|
||||
[TestCase(">p\r\n>p\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase(">\n>q")]
|
||||
[TestCase(">\n>\n>q")]
|
||||
[TestCase(">q\n>\n>q")]
|
||||
[TestCase(">q\n>\n>\n>q")]
|
||||
[TestCase(">q\n> \n>q")]
|
||||
[TestCase(">q\n> \n>q")]
|
||||
[TestCase(">q\n> \n>q")]
|
||||
[TestCase(">q\n>\t\n>q")]
|
||||
[TestCase(">q\n>\v\n>q")]
|
||||
[TestCase(">q\n>\f\n>q")]
|
||||
public void TestEmptyLines(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase(">\n>q")]
|
||||
[TestCase(">\n>\n>q")]
|
||||
[TestCase(">q\n>\n>q")]
|
||||
[TestCase(">q\n>\n>\n>q")]
|
||||
[TestCase(">q\n> \n>q")]
|
||||
[TestCase(">q\n> \n>q")]
|
||||
[TestCase(">q\n> \n>q")]
|
||||
[TestCase(">q\n>\t\n>q")]
|
||||
[TestCase(">q\n>\v\n>q")]
|
||||
[TestCase(">q\n>\f\n>q")]
|
||||
public void TestEmptyLines(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestSetextHeading
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestSetextHeading
|
||||
[TestCase("h1\n===")] //3
|
||||
[TestCase("h1\n ===")] //3
|
||||
[TestCase("h1\n ===")] //3
|
||||
[TestCase("h1\n ===")] //3
|
||||
[TestCase("h1\n=== ")] //3
|
||||
[TestCase("h1 \n===")] //3
|
||||
[TestCase("h1\\\n===")] //3
|
||||
[TestCase("h1\n === ")] //3
|
||||
[TestCase("h1\nh1 l2\n===")] //3
|
||||
[TestCase("h1\n====")] // 4
|
||||
[TestCase("h1\n ====")] // 4
|
||||
[TestCase("h1\n==== ")] // 4
|
||||
[TestCase("h1\n ==== ")] // 4
|
||||
[TestCase("h1\n===\nh1\n===")] //3
|
||||
[TestCase("\\>h1\n===")] //3
|
||||
public void Test(string value)
|
||||
{
|
||||
[TestCase("h1\n===")] //3
|
||||
[TestCase("h1\n ===")] //3
|
||||
[TestCase("h1\n ===")] //3
|
||||
[TestCase("h1\n ===")] //3
|
||||
[TestCase("h1\n=== ")] //3
|
||||
[TestCase("h1 \n===")] //3
|
||||
[TestCase("h1\\\n===")] //3
|
||||
[TestCase("h1\n === ")] //3
|
||||
[TestCase("h1\nh1 l2\n===")] //3
|
||||
[TestCase("h1\n====")] // 4
|
||||
[TestCase("h1\n ====")] // 4
|
||||
[TestCase("h1\n==== ")] // 4
|
||||
[TestCase("h1\n ==== ")] // 4
|
||||
[TestCase("h1\n===\nh1\n===")] //3
|
||||
[TestCase("\\>h1\n===")] //3
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("h1\r===")]
|
||||
[TestCase("h1\n===")]
|
||||
[TestCase("h1\r\n===")]
|
||||
[TestCase("h1\r===")]
|
||||
[TestCase("h1\n===")]
|
||||
[TestCase("h1\r\n===")]
|
||||
|
||||
[TestCase("h1\r===\r")]
|
||||
[TestCase("h1\n===\r")]
|
||||
[TestCase("h1\r\n===\r")]
|
||||
[TestCase("h1\r===\r")]
|
||||
[TestCase("h1\n===\r")]
|
||||
[TestCase("h1\r\n===\r")]
|
||||
|
||||
[TestCase("h1\r===\n")]
|
||||
[TestCase("h1\n===\n")]
|
||||
[TestCase("h1\r\n===\n")]
|
||||
[TestCase("h1\r===\n")]
|
||||
[TestCase("h1\n===\n")]
|
||||
[TestCase("h1\r\n===\n")]
|
||||
|
||||
[TestCase("h1\r===\r\n")]
|
||||
[TestCase("h1\n===\r\n")]
|
||||
[TestCase("h1\r\n===\r\n")]
|
||||
[TestCase("h1\r===\r\n")]
|
||||
[TestCase("h1\n===\r\n")]
|
||||
[TestCase("h1\r\n===\r\n")]
|
||||
|
||||
[TestCase("h1\n===\n\n\nh2---\n\n")]
|
||||
[TestCase("h1\r===\r\r\rh2---\r\r")]
|
||||
[TestCase("h1\r\n===\r\n\r\n\r\nh2---\r\n\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("h1\n===\n\n\nh2---\n\n")]
|
||||
[TestCase("h1\r===\r\r\rh2---\r\r")]
|
||||
[TestCase("h1\r\n===\r\n\r\n\r\nh2---\r\n\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestThematicBreak
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestThematicBreak
|
||||
[TestCase("---")]
|
||||
[TestCase(" ---")]
|
||||
[TestCase(" ---")]
|
||||
[TestCase(" ---")]
|
||||
[TestCase("--- ")]
|
||||
[TestCase(" --- ")]
|
||||
[TestCase(" --- ")]
|
||||
[TestCase(" --- ")]
|
||||
[TestCase("- - -")]
|
||||
[TestCase(" - - -")]
|
||||
[TestCase(" - - - ")]
|
||||
[TestCase("-- -")]
|
||||
[TestCase("---\n")]
|
||||
[TestCase("---\n\n")]
|
||||
[TestCase("---\np")]
|
||||
[TestCase("---\n\np")]
|
||||
[TestCase("---\n# h")]
|
||||
[TestCase("p\n\n---")]
|
||||
// Note: "p\n---" is parsed as setext heading
|
||||
public void Test(string value)
|
||||
{
|
||||
[TestCase("---")]
|
||||
[TestCase(" ---")]
|
||||
[TestCase(" ---")]
|
||||
[TestCase(" ---")]
|
||||
[TestCase("--- ")]
|
||||
[TestCase(" --- ")]
|
||||
[TestCase(" --- ")]
|
||||
[TestCase(" --- ")]
|
||||
[TestCase("- - -")]
|
||||
[TestCase(" - - -")]
|
||||
[TestCase(" - - - ")]
|
||||
[TestCase("-- -")]
|
||||
[TestCase("---\n")]
|
||||
[TestCase("---\n\n")]
|
||||
[TestCase("---\np")]
|
||||
[TestCase("---\n\np")]
|
||||
[TestCase("---\n# h")]
|
||||
[TestCase("p\n\n---")]
|
||||
// Note: "p\n---" is parsed as setext heading
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("\n---")]
|
||||
[TestCase("\r---")]
|
||||
[TestCase("\r\n---")]
|
||||
[TestCase("\n---")]
|
||||
[TestCase("\r---")]
|
||||
[TestCase("\r\n---")]
|
||||
|
||||
[TestCase("\n---\n")]
|
||||
[TestCase("\r---\n")]
|
||||
[TestCase("\r\n---\n")]
|
||||
[TestCase("\n---\n")]
|
||||
[TestCase("\r---\n")]
|
||||
[TestCase("\r\n---\n")]
|
||||
|
||||
[TestCase("\n---\r")]
|
||||
[TestCase("\r---\r")]
|
||||
[TestCase("\r\n---\r")]
|
||||
[TestCase("\n---\r")]
|
||||
[TestCase("\r---\r")]
|
||||
[TestCase("\r\n---\r")]
|
||||
|
||||
[TestCase("\n---\r\n")]
|
||||
[TestCase("\r---\r\n")]
|
||||
[TestCase("\r\n---\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("\n---\r\n")]
|
||||
[TestCase("\r---\r\n")]
|
||||
[TestCase("\r\n---\r\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,183 +1,181 @@
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestUnorderedList
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestUnorderedList
|
||||
// i = item
|
||||
[TestCase("- i1")]
|
||||
[TestCase("- i1 ")]
|
||||
[TestCase("- i1\n")]
|
||||
[TestCase("- i1\n\n")]
|
||||
[TestCase("- i1\n- i2")]
|
||||
[TestCase("- i1\n - i2")]
|
||||
[TestCase("- i1\n - i1.1\n - i1.2")]
|
||||
[TestCase("- i1 \n- i2 \n")]
|
||||
[TestCase("- i1 \n- i2 \n")]
|
||||
[TestCase(" - i1")]
|
||||
[TestCase(" - i1")]
|
||||
[TestCase(" - i1")]
|
||||
[TestCase("- i1\n\n- i1")]
|
||||
[TestCase("- i1\n\n\n- i1")]
|
||||
[TestCase("- i1\n - i1.1\n - i1.1.1\n")]
|
||||
|
||||
[TestCase("-\ti1")]
|
||||
[TestCase("-\ti1\n-\ti2")]
|
||||
[TestCase("-\ti1\n- i2\n-\ti3")]
|
||||
public void Test(string value)
|
||||
{
|
||||
// i = item
|
||||
[TestCase("- i1")]
|
||||
[TestCase("- i1 ")]
|
||||
[TestCase("- i1\n")]
|
||||
[TestCase("- i1\n\n")]
|
||||
[TestCase("- i1\n- i2")]
|
||||
[TestCase("- i1\n - i2")]
|
||||
[TestCase("- i1\n - i1.1\n - i1.2")]
|
||||
[TestCase("- i1 \n- i2 \n")]
|
||||
[TestCase("- i1 \n- i2 \n")]
|
||||
[TestCase(" - i1")]
|
||||
[TestCase(" - i1")]
|
||||
[TestCase(" - i1")]
|
||||
[TestCase("- i1\n\n- i1")]
|
||||
[TestCase("- i1\n\n\n- i1")]
|
||||
[TestCase("- i1\n - i1.1\n - i1.1.1\n")]
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("-\ti1")]
|
||||
[TestCase("-\ti1\n-\ti2")]
|
||||
[TestCase("-\ti1\n- i2\n-\ti3")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q1\n - > q2")]
|
||||
public void TestBlockQuote(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase("- > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q")]
|
||||
[TestCase(" - > q1\n - > q2")]
|
||||
public void TestBlockQuote(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("- i1\n\np\n")] // TODO: listblock should render newline, apparently last paragraph of last listitem dont have newline
|
||||
[TestCase("- i1\n\n\np\n")]
|
||||
[TestCase("- i1\n\np")]
|
||||
[TestCase("- i1\n\np\n")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("- i1\n\np\n")] // TODO: listblock should render newline, apparently last paragraph of last listitem dont have newline
|
||||
[TestCase("- i1\n\n\np\n")]
|
||||
[TestCase("- i1\n\np")]
|
||||
[TestCase("- i1\n\np\n")]
|
||||
public void TestParagraph(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("- i1\n\n---\n")]
|
||||
[TestCase("- i1\n\n\n---\n")]
|
||||
public void TestThematicBreak(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("- i1\n\n---\n")]
|
||||
[TestCase("- i1\n\n\n---\n")]
|
||||
public void TestThematicBreak(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("- c")] // 5
|
||||
[TestCase("- c\n c")] // 5, 6
|
||||
[TestCase(" - c\n c")] // 5, 6
|
||||
[TestCase(" - c\n c")] // 5, 7
|
||||
[TestCase("- c\n c")] // 6, 6
|
||||
[TestCase(" - c\n c")] // 6, 6
|
||||
[TestCase(" - c\n c")] // 6, 7
|
||||
public void TestIndentedCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("- c")] // 5
|
||||
[TestCase("- c\n c")] // 5, 6
|
||||
[TestCase(" - c\n c")] // 5, 6
|
||||
[TestCase(" - c\n c")] // 5, 7
|
||||
[TestCase("- c\n c")] // 6, 6
|
||||
[TestCase(" - c\n c")] // 6, 6
|
||||
[TestCase(" - c\n c")] // 6, 7
|
||||
public void TestIndentedCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("- ```a```")]
|
||||
[TestCase("- ```\n a\n```")]
|
||||
[TestCase("- i1\n - i1.1\n ```\n c\n ```")]
|
||||
[TestCase("- i1\n - i1.1\n ```\nc\n```")]
|
||||
[TestCase("- i1\n - i1.1\n ```\nc\n```\n")]
|
||||
public void TestFencedCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
|
||||
[TestCase("- ```a```")]
|
||||
[TestCase("- ```\n a\n```")]
|
||||
[TestCase("- i1\n - i1.1\n ```\n c\n ```")]
|
||||
[TestCase("- i1\n - i1.1\n ```\nc\n```")]
|
||||
[TestCase("- i1\n - i1.1\n ```\nc\n```\n")]
|
||||
public void TestFencedCodeBlock(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("\n- i")]
|
||||
[TestCase("\r- i")]
|
||||
[TestCase("\r\n- i")]
|
||||
|
||||
[TestCase("\n- i")]
|
||||
[TestCase("\r- i")]
|
||||
[TestCase("\r\n- i")]
|
||||
[TestCase("\n- i\n")]
|
||||
[TestCase("\r- i\n")]
|
||||
[TestCase("\r\n- i\n")]
|
||||
|
||||
[TestCase("\n- i\n")]
|
||||
[TestCase("\r- i\n")]
|
||||
[TestCase("\r\n- i\n")]
|
||||
[TestCase("\n- i\r")]
|
||||
[TestCase("\r- i\r")]
|
||||
[TestCase("\r\n- i\r")]
|
||||
|
||||
[TestCase("\n- i\r")]
|
||||
[TestCase("\r- i\r")]
|
||||
[TestCase("\r\n- i\r")]
|
||||
[TestCase("\n- i\r\n")]
|
||||
[TestCase("\r- i\r\n")]
|
||||
[TestCase("\r\n- i\r\n")]
|
||||
|
||||
[TestCase("\n- i\r\n")]
|
||||
[TestCase("\r- i\r\n")]
|
||||
[TestCase("\r\n- i\r\n")]
|
||||
[TestCase("- i\n- j")]
|
||||
[TestCase("- i\r- j")]
|
||||
[TestCase("- i\r\n- j")]
|
||||
|
||||
[TestCase("- i\n- j")]
|
||||
[TestCase("- i\r- j")]
|
||||
[TestCase("- i\r\n- j")]
|
||||
[TestCase("\n- i\n- j")]
|
||||
[TestCase("\n- i\r- j")]
|
||||
[TestCase("\n- i\r\n- j")]
|
||||
|
||||
[TestCase("\n- i\n- j")]
|
||||
[TestCase("\n- i\r- j")]
|
||||
[TestCase("\n- i\r\n- j")]
|
||||
[TestCase("\r- i\n- j")]
|
||||
[TestCase("\r- i\r- j")]
|
||||
[TestCase("\r- i\r\n- j")]
|
||||
|
||||
[TestCase("\r- i\n- j")]
|
||||
[TestCase("\r- i\r- j")]
|
||||
[TestCase("\r- i\r\n- j")]
|
||||
[TestCase("\r\n- i\n- j")]
|
||||
[TestCase("\r\n- i\r- j")]
|
||||
[TestCase("\r\n- i\r\n- j")]
|
||||
|
||||
[TestCase("\r\n- i\n- j")]
|
||||
[TestCase("\r\n- i\r- j")]
|
||||
[TestCase("\r\n- i\r\n- j")]
|
||||
[TestCase("- i\n- j\n")]
|
||||
[TestCase("- i\r- j\n")]
|
||||
[TestCase("- i\r\n- j\n")]
|
||||
|
||||
[TestCase("- i\n- j\n")]
|
||||
[TestCase("- i\r- j\n")]
|
||||
[TestCase("- i\r\n- j\n")]
|
||||
[TestCase("\n- i\n- j\n")]
|
||||
[TestCase("\n- i\r- j\n")]
|
||||
[TestCase("\n- i\r\n- j\n")]
|
||||
|
||||
[TestCase("\n- i\n- j\n")]
|
||||
[TestCase("\n- i\r- j\n")]
|
||||
[TestCase("\n- i\r\n- j\n")]
|
||||
[TestCase("\r- i\n- j\n")]
|
||||
[TestCase("\r- i\r- j\n")]
|
||||
[TestCase("\r- i\r\n- j\n")]
|
||||
|
||||
[TestCase("\r- i\n- j\n")]
|
||||
[TestCase("\r- i\r- j\n")]
|
||||
[TestCase("\r- i\r\n- j\n")]
|
||||
[TestCase("\r\n- i\n- j\n")]
|
||||
[TestCase("\r\n- i\r- j\n")]
|
||||
[TestCase("\r\n- i\r\n- j\n")]
|
||||
|
||||
[TestCase("\r\n- i\n- j\n")]
|
||||
[TestCase("\r\n- i\r- j\n")]
|
||||
[TestCase("\r\n- i\r\n- j\n")]
|
||||
[TestCase("- i\n- j\r")]
|
||||
[TestCase("- i\r- j\r")]
|
||||
[TestCase("- i\r\n- j\r")]
|
||||
|
||||
[TestCase("- i\n- j\r")]
|
||||
[TestCase("- i\r- j\r")]
|
||||
[TestCase("- i\r\n- j\r")]
|
||||
[TestCase("\n- i\n- j\r")]
|
||||
[TestCase("\n- i\r- j\r")]
|
||||
[TestCase("\n- i\r\n- j\r")]
|
||||
|
||||
[TestCase("\n- i\n- j\r")]
|
||||
[TestCase("\n- i\r- j\r")]
|
||||
[TestCase("\n- i\r\n- j\r")]
|
||||
[TestCase("\r- i\n- j\r")]
|
||||
[TestCase("\r- i\r- j\r")]
|
||||
[TestCase("\r- i\r\n- j\r")]
|
||||
|
||||
[TestCase("\r- i\n- j\r")]
|
||||
[TestCase("\r- i\r- j\r")]
|
||||
[TestCase("\r- i\r\n- j\r")]
|
||||
[TestCase("\r\n- i\n- j\r")]
|
||||
[TestCase("\r\n- i\r- j\r")]
|
||||
[TestCase("\r\n- i\r\n- j\r")]
|
||||
|
||||
[TestCase("\r\n- i\n- j\r")]
|
||||
[TestCase("\r\n- i\r- j\r")]
|
||||
[TestCase("\r\n- i\r\n- j\r")]
|
||||
[TestCase("- i\n- j\r\n")]
|
||||
[TestCase("- i\r- j\r\n")]
|
||||
[TestCase("- i\r\n- j\r\n")]
|
||||
|
||||
[TestCase("- i\n- j\r\n")]
|
||||
[TestCase("- i\r- j\r\n")]
|
||||
[TestCase("- i\r\n- j\r\n")]
|
||||
[TestCase("\n- i\n- j\r\n")]
|
||||
[TestCase("\n- i\r- j\r\n")]
|
||||
[TestCase("\n- i\r\n- j\r\n")]
|
||||
|
||||
[TestCase("\n- i\n- j\r\n")]
|
||||
[TestCase("\n- i\r- j\r\n")]
|
||||
[TestCase("\n- i\r\n- j\r\n")]
|
||||
[TestCase("\r- i\n- j\r\n")]
|
||||
[TestCase("\r- i\r- j\r\n")]
|
||||
[TestCase("\r- i\r\n- j\r\n")]
|
||||
|
||||
[TestCase("\r- i\n- j\r\n")]
|
||||
[TestCase("\r- i\r- j\r\n")]
|
||||
[TestCase("\r- i\r\n- j\r\n")]
|
||||
[TestCase("\r\n- i\n- j\r\n")]
|
||||
[TestCase("\r\n- i\r- j\r\n")]
|
||||
[TestCase("\r\n- i\r\n- j\r\n")]
|
||||
|
||||
[TestCase("\r\n- i\n- j\r\n")]
|
||||
[TestCase("\r\n- i\r- j\r\n")]
|
||||
[TestCase("\r\n- i\r\n- j\r\n")]
|
||||
|
||||
[TestCase("- i\n")]
|
||||
[TestCase("- i\n\n")]
|
||||
[TestCase("- i\n\n\n")]
|
||||
[TestCase("- i\n\n\n\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
[TestCase("- i\n")]
|
||||
[TestCase("- i\n\n")]
|
||||
[TestCase("- i\n\n\n")]
|
||||
[TestCase("- i\n\n\n\n")]
|
||||
public void TestNewline(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Markdig.Renderers.Roundtrip;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
namespace Markdig.Tests.RoundtripSpecs;
|
||||
|
||||
[TestFixture]
|
||||
public class TestYamlFrontMatterBlock
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestYamlFrontMatterBlock
|
||||
[TestCase("---\nkey1: value1\nkey2: value2\n---\n\nContent\n")]
|
||||
[TestCase("No front matter")]
|
||||
[TestCase("Looks like front matter but actually is not\n---\nkey1: value1\nkey2: value2\n---")]
|
||||
public void FrontMatterBlockIsPreserved(string value)
|
||||
{
|
||||
[TestCase("---\nkey1: value1\nkey2: value2\n---\n\nContent\n")]
|
||||
[TestCase("No front matter")]
|
||||
[TestCase("Looks like front matter but actually is not\n---\nkey1: value1\nkey2: value2\n---")]
|
||||
public void FrontMatterBlockIsPreserved(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
|
||||
179
src/Markdig.Tests/Specs/AlertBlockSpecs.generated.cs
Normal file
179
src/Markdig.Tests/Specs/AlertBlockSpecs.generated.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
|
||||
// --------------------------------
|
||||
// Alert Blocks
|
||||
// --------------------------------
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests.Specs.AlertBlocks
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestExtensionsAlertBlocks
|
||||
{
|
||||
// # Extensions
|
||||
//
|
||||
// This section describes the different extensions supported:
|
||||
//
|
||||
// ## Alert Blocks
|
||||
//
|
||||
// This is supporting the [GitHub Alert blocks](https://github.com/orgs/community/discussions/16925)
|
||||
[Test]
|
||||
public void ExtensionsAlertBlocks_Example001()
|
||||
{
|
||||
// Example 1
|
||||
// Section: Extensions / Alert Blocks
|
||||
//
|
||||
// The following Markdown:
|
||||
// > [!NOTE]
|
||||
// > Highlights information that users should take into account, even when skimming.
|
||||
//
|
||||
// > [!TIP]
|
||||
// > Optional information to help a user be more successful.
|
||||
//
|
||||
// > [!IMPORTANT]
|
||||
// > Crucial information necessary for users to succeed.
|
||||
//
|
||||
// > [!WARNING]
|
||||
// > Critical content demanding immediate user attention due to potential risks.
|
||||
//
|
||||
// > [!CAUTION]
|
||||
// > Negative potential consequences of an action.
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <div class="markdown-alert markdown-alert-note">
|
||||
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
|
||||
// <p>Highlights information that users should take into account, even when skimming.</p>
|
||||
// </div>
|
||||
// <div class="markdown-alert markdown-alert-tip">
|
||||
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"></path></svg>Tip</p>
|
||||
// <p>Optional information to help a user be more successful.</p>
|
||||
// </div>
|
||||
// <div class="markdown-alert markdown-alert-important">
|
||||
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Important</p>
|
||||
// <p>Crucial information necessary for users to succeed.</p>
|
||||
// </div>
|
||||
// <div class="markdown-alert markdown-alert-warning">
|
||||
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Warning</p>
|
||||
// <p>Critical content demanding immediate user attention due to potential risks.</p>
|
||||
// </div>
|
||||
// <div class="markdown-alert markdown-alert-caution">
|
||||
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Caution</p>
|
||||
// <p>Negative potential consequences of an action.</p>
|
||||
// </div>
|
||||
|
||||
TestParser.TestSpec("> [!NOTE] \n> Highlights information that users should take into account, even when skimming.\n\n> [!TIP]\n> Optional information to help a user be more successful.\n\n> [!IMPORTANT] \n> Crucial information necessary for users to succeed.\n\n> [!WARNING] \n> Critical content demanding immediate user attention due to potential risks.\n\n> [!CAUTION]\n> Negative potential consequences of an action.", "<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Note</p>\n<p>Highlights information that users should take into account, even when skimming.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-tip\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z\"></path></svg>Tip</p>\n<p>Optional information to help a user be more successful.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-important\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"></path></svg>Important</p>\n<p>Crucial information necessary for users to succeed.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-warning\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"></path></svg>Warning</p>\n<p>Critical content demanding immediate user attention due to potential risks.</p>\n</div>\n<div class=\"markdown-alert markdown-alert-caution\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Caution</p>\n<p>Negative potential consequences of an action.</p>\n</div>", "advanced", context: "Example 1\nSection Extensions / Alert Blocks\n");
|
||||
}
|
||||
|
||||
// Example with code blocks and mix formatting:
|
||||
[Test]
|
||||
public void ExtensionsAlertBlocks_Example002()
|
||||
{
|
||||
// Example 2
|
||||
// Section: Extensions / Alert Blocks
|
||||
//
|
||||
// The following Markdown:
|
||||
// > [!NOTE]
|
||||
// > Highlights information that users should take into account, even when skimming.
|
||||
// > Testing rendering for multiple lines
|
||||
// > ```csharp
|
||||
// > var test = "I can also add code to panels
|
||||
// > ```
|
||||
// > `Inline code testing`
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <div class="markdown-alert markdown-alert-note">
|
||||
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
|
||||
// <p>Highlights information that users should take into account, even when skimming.
|
||||
// Testing rendering for multiple lines</p>
|
||||
// <pre><code class="language-csharp">var test = "I can also add code to panels
|
||||
// </code></pre>
|
||||
// <p><code>Inline code testing</code></p>
|
||||
// </div>
|
||||
|
||||
TestParser.TestSpec("> [!NOTE]\n> Highlights information that users should take into account, even when skimming.\n> Testing rendering for multiple lines\n> ```csharp\n> var test = \"I can also add code to panels\n> ```\n> `Inline code testing`", "<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Note</p>\n<p>Highlights information that users should take into account, even when skimming.\nTesting rendering for multiple lines</p>\n<pre><code class=\"language-csharp\">var test = "I can also add code to panels\n</code></pre>\n<p><code>Inline code testing</code></p>\n</div>", "advanced", context: "Example 2\nSection Extensions / Alert Blocks\n");
|
||||
}
|
||||
|
||||
// Multiline:
|
||||
[Test]
|
||||
public void ExtensionsAlertBlocks_Example003()
|
||||
{
|
||||
// Example 3
|
||||
// Section: Extensions / Alert Blocks
|
||||
//
|
||||
// The following Markdown:
|
||||
// > [!NOTE]
|
||||
// > Highlights information that users should take into account, even when skimming.
|
||||
// >
|
||||
// > Testing rendering for multiple lines
|
||||
// >
|
||||
// > `Inline code testing`
|
||||
// >
|
||||
// > Other line
|
||||
// >
|
||||
// > > Nested quote
|
||||
// > >
|
||||
// > > Final nested quote line
|
||||
// >
|
||||
// > Final line of alert
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <div class="markdown-alert markdown-alert-note">
|
||||
// <p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
|
||||
// <p>Highlights information that users should take into account, even when skimming.</p>
|
||||
// <p>Testing rendering for multiple lines</p>
|
||||
// <p><code>Inline code testing</code></p>
|
||||
// <p>Other line</p>
|
||||
// <blockquote>
|
||||
// <p>Nested quote</p>
|
||||
// <p>Final nested quote line</p>
|
||||
// </blockquote>
|
||||
// <p>Final line of alert</p>
|
||||
// </div>
|
||||
|
||||
TestParser.TestSpec("> [!NOTE]\n> Highlights information that users should take into account, even when skimming.\n> \n> Testing rendering for multiple lines\n> \n> `Inline code testing`\n> \n> Other line\n> \n> > Nested quote\n> >\n> > Final nested quote line\n> \n> Final line of alert", "<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Note</p>\n<p>Highlights information that users should take into account, even when skimming.</p>\n<p>Testing rendering for multiple lines</p>\n<p><code>Inline code testing</code></p>\n<p>Other line</p>\n<blockquote>\n<p>Nested quote</p>\n<p>Final nested quote line</p>\n</blockquote>\n<p>Final line of alert</p>\n</div>", "advanced", context: "Example 3\nSection Extensions / Alert Blocks\n");
|
||||
}
|
||||
|
||||
// An alert inline (e.g `[!NOTE]`) must come first in a quote block, and must be followed by optional spaces with a new line. If no new lines are found, it will not be considered as an alert block.
|
||||
//
|
||||
// Followed by space and new line:
|
||||
[Test]
|
||||
public void ExtensionsAlertBlocks_Example004()
|
||||
{
|
||||
// Example 4
|
||||
// Section: Extensions / Alert Blocks
|
||||
//
|
||||
// The following Markdown:
|
||||
// > [!NOTE] This is invalid because no new line
|
||||
// > Highlights information that users should take into account, even when skimming.
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <blockquote>
|
||||
// <p>[!NOTE] This is invalid because no new line
|
||||
// Highlights information that users should take into account, even when skimming.</p>
|
||||
// </blockquote>
|
||||
|
||||
TestParser.TestSpec("> [!NOTE] This is invalid because no new line\n> Highlights information that users should take into account, even when skimming.", "<blockquote>\n<p>[!NOTE] This is invalid because no new line\nHighlights information that users should take into account, even when skimming.</p>\n</blockquote>", "advanced", context: "Example 4\nSection Extensions / Alert Blocks\n");
|
||||
}
|
||||
|
||||
// Must come first in a quote block:
|
||||
[Test]
|
||||
public void ExtensionsAlertBlocks_Example005()
|
||||
{
|
||||
// Example 5
|
||||
// Section: Extensions / Alert Blocks
|
||||
//
|
||||
// The following Markdown:
|
||||
// > This is not a [!NOTE]
|
||||
// > Highlights information that users should take into account, even when skimming.
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <blockquote>
|
||||
// <p>This is not a [!NOTE]
|
||||
// Highlights information that users should take into account, even when skimming.</p>
|
||||
// </blockquote>
|
||||
|
||||
TestParser.TestSpec("> This is not a [!NOTE]\n> Highlights information that users should take into account, even when skimming.", "<blockquote>\n<p>This is not a [!NOTE]\nHighlights information that users should take into account, even when skimming.</p>\n</blockquote>", "advanced", context: "Example 5\nSection Extensions / Alert Blocks\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
127
src/Markdig.Tests/Specs/AlertBlockSpecs.md
Normal file
127
src/Markdig.Tests/Specs/AlertBlockSpecs.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Extensions
|
||||
|
||||
This section describes the different extensions supported:
|
||||
|
||||
## Alert Blocks
|
||||
|
||||
This is supporting the [GitHub Alert blocks](https://github.com/orgs/community/discussions/16925)
|
||||
|
||||
```````````````````````````````` example
|
||||
> [!NOTE]
|
||||
> Highlights information that users should take into account, even when skimming.
|
||||
|
||||
> [!TIP]
|
||||
> Optional information to help a user be more successful.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Crucial information necessary for users to succeed.
|
||||
|
||||
> [!WARNING]
|
||||
> Critical content demanding immediate user attention due to potential risks.
|
||||
|
||||
> [!CAUTION]
|
||||
> Negative potential consequences of an action.
|
||||
.
|
||||
<div class="markdown-alert markdown-alert-note">
|
||||
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
|
||||
<p>Highlights information that users should take into account, even when skimming.</p>
|
||||
</div>
|
||||
<div class="markdown-alert markdown-alert-tip">
|
||||
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z"></path></svg>Tip</p>
|
||||
<p>Optional information to help a user be more successful.</p>
|
||||
</div>
|
||||
<div class="markdown-alert markdown-alert-important">
|
||||
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Important</p>
|
||||
<p>Crucial information necessary for users to succeed.</p>
|
||||
</div>
|
||||
<div class="markdown-alert markdown-alert-warning">
|
||||
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg>Warning</p>
|
||||
<p>Critical content demanding immediate user attention due to potential risks.</p>
|
||||
</div>
|
||||
<div class="markdown-alert markdown-alert-caution">
|
||||
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Caution</p>
|
||||
<p>Negative potential consequences of an action.</p>
|
||||
</div>
|
||||
````````````````````````````````
|
||||
|
||||
Example with code blocks and mix formatting:
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
> [!NOTE]
|
||||
> Highlights information that users should take into account, even when skimming.
|
||||
> Testing rendering for multiple lines
|
||||
> ```csharp
|
||||
> var test = "I can also add code to panels
|
||||
> ```
|
||||
> `Inline code testing`
|
||||
.
|
||||
<div class="markdown-alert markdown-alert-note">
|
||||
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
|
||||
<p>Highlights information that users should take into account, even when skimming.
|
||||
Testing rendering for multiple lines</p>
|
||||
<pre><code class="language-csharp">var test = "I can also add code to panels
|
||||
</code></pre>
|
||||
<p><code>Inline code testing</code></p>
|
||||
</div>
|
||||
````````````````````````````````
|
||||
|
||||
Multiline:
|
||||
|
||||
```````````````````````````````` example
|
||||
> [!NOTE]
|
||||
> Highlights information that users should take into account, even when skimming.
|
||||
>
|
||||
> Testing rendering for multiple lines
|
||||
>
|
||||
> `Inline code testing`
|
||||
>
|
||||
> Other line
|
||||
>
|
||||
> > Nested quote
|
||||
> >
|
||||
> > Final nested quote line
|
||||
>
|
||||
> Final line of alert
|
||||
.
|
||||
<div class="markdown-alert markdown-alert-note">
|
||||
<p class="markdown-alert-title"><svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
|
||||
<p>Highlights information that users should take into account, even when skimming.</p>
|
||||
<p>Testing rendering for multiple lines</p>
|
||||
<p><code>Inline code testing</code></p>
|
||||
<p>Other line</p>
|
||||
<blockquote>
|
||||
<p>Nested quote</p>
|
||||
<p>Final nested quote line</p>
|
||||
</blockquote>
|
||||
<p>Final line of alert</p>
|
||||
</div>
|
||||
````````````````````````````````
|
||||
|
||||
An alert inline (e.g `[!NOTE]`) must come first in a quote block, and must be followed by optional spaces with a new line. If no new lines are found, it will not be considered as an alert block.
|
||||
|
||||
Followed by space and new line:
|
||||
|
||||
```````````````````````````````` example
|
||||
> [!NOTE] This is invalid because no new line
|
||||
> Highlights information that users should take into account, even when skimming.
|
||||
.
|
||||
<blockquote>
|
||||
<p>[!NOTE] This is invalid because no new line
|
||||
Highlights information that users should take into account, even when skimming.</p>
|
||||
</blockquote>
|
||||
````````````````````````````````
|
||||
|
||||
Must come first in a quote block:
|
||||
|
||||
```````````````````````````````` example
|
||||
> This is not a [!NOTE]
|
||||
> Highlights information that users should take into account, even when skimming.
|
||||
.
|
||||
<blockquote>
|
||||
<p>This is not a [!NOTE]
|
||||
Highlights information that users should take into account, even when skimming.</p>
|
||||
</blockquote>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -533,5 +533,28 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
|
||||
TestParser.TestSpec("<http://foö.bar.`baz>`", "<p><a href=\"http://xn--fo-gka.bar.%60baz\">http://foö.bar.`baz</a>`</p>", "autolinks|advanced", context: "Example 25\nSection Extensions / AutoLinks / Unicode support\n");
|
||||
}
|
||||
|
||||
// Unicode punctuation characters are not allowed, but symbols are.
|
||||
// Note that this does _not_ exactly match CommonMark's "Unicode punctuation character" definition.
|
||||
[Test]
|
||||
public void ExtensionsAutoLinksUnicodeSupport_Example026()
|
||||
{
|
||||
// Example 26
|
||||
// Section: Extensions / AutoLinks / Unicode support
|
||||
//
|
||||
// The following Markdown:
|
||||
// http://☃.net?☃ // OtherSymbol
|
||||
//
|
||||
// http://🍉.net?🍉 // A UTF-16 surrogate pair, but code point is OtherSymbol
|
||||
//
|
||||
// http://‰.net?‰ // OtherPunctuation
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p><a href="http://xn--n3h.net?%E2%98%83">http://☃.net?☃</a> // OtherSymbol</p>
|
||||
// <p><a href="http://xn--ji8h.net?%F0%9F%8D%89">http://🍉.net?🍉</a> // A UTF-16 surrogate pair, but code point is OtherSymbol</p>
|
||||
// <p>http://‰.net?‰ // OtherPunctuation</p>
|
||||
|
||||
TestParser.TestSpec("http://☃.net?☃ // OtherSymbol\n\nhttp://🍉.net?🍉 // A UTF-16 surrogate pair, but code point is OtherSymbol\n\nhttp://‰.net?‰ // OtherPunctuation", "<p><a href=\"http://xn--n3h.net?%E2%98%83\">http://☃.net?☃</a> // OtherSymbol</p>\n<p><a href=\"http://xn--ji8h.net?%F0%9F%8D%89\">http://🍉.net?🍉</a> // A UTF-16 surrogate pair, but code point is OtherSymbol</p>\n<p>http://‰.net?‰ // OtherPunctuation</p>", "autolinks|advanced", context: "Example 26\nSection Extensions / AutoLinks / Unicode support\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,4 +303,19 @@ This will therefore be seen as an autolink and not as code inline.
|
||||
<http://foö.bar.`baz>`
|
||||
.
|
||||
<p><a href="http://xn--fo-gka.bar.%60baz">http://foö.bar.`baz</a>`</p>
|
||||
````````````````````````````````
|
||||
|
||||
Unicode punctuation characters are not allowed, but symbols are.
|
||||
Note that this does _not_ exactly match CommonMark's "Unicode punctuation character" definition.
|
||||
|
||||
```````````````````````````````` example
|
||||
http://☃.net?☃ // OtherSymbol
|
||||
|
||||
http://🍉.net?🍉 // A UTF-16 surrogate pair, but code point is OtherSymbol
|
||||
|
||||
http://‰.net?‰ // OtherPunctuation
|
||||
.
|
||||
<p><a href="http://xn--n3h.net?%E2%98%83">http://☃.net?☃</a> // OtherSymbol</p>
|
||||
<p><a href="http://xn--ji8h.net?%F0%9F%8D%89">http://🍉.net?🍉</a> // A UTF-16 surrogate pair, but code point is OtherSymbol</p>
|
||||
<p>http://‰.net?‰ // OtherPunctuation</p>
|
||||
````````````````````````````````
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: CommonMark Spec
|
||||
author: John MacFarlane
|
||||
version: '0.30'
|
||||
date: '2021-06-19'
|
||||
license: '[CC-BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)'
|
||||
version: '0.31.2'
|
||||
date: '2024-01-28'
|
||||
license: '[CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)'
|
||||
...
|
||||
|
||||
# Introduction
|
||||
@@ -14,7 +14,7 @@ Markdown is a plain text format for writing structured documents,
|
||||
based on conventions for indicating formatting in email
|
||||
and usenet posts. It was developed by John Gruber (with
|
||||
help from Aaron Swartz) and released in 2004 in the form of a
|
||||
[syntax description](http://daringfireball.net/projects/markdown/syntax)
|
||||
[syntax description](https://daringfireball.net/projects/markdown/syntax)
|
||||
and a Perl script (`Markdown.pl`) for converting Markdown to
|
||||
HTML. In the next decade, dozens of implementations were
|
||||
developed in many languages. Some extended the original
|
||||
@@ -34,10 +34,10 @@ As Gruber writes:
|
||||
> Markdown-formatted document should be publishable as-is, as
|
||||
> plain text, without looking like it's been marked up with tags
|
||||
> or formatting instructions.
|
||||
> (<http://daringfireball.net/projects/markdown/>)
|
||||
> (<https://daringfireball.net/projects/markdown/>)
|
||||
|
||||
The point can be illustrated by comparing a sample of
|
||||
[AsciiDoc](http://www.methods.co.nz/asciidoc/) with
|
||||
[AsciiDoc](https://asciidoc.org/) with
|
||||
an equivalent sample of Markdown. Here is a sample of
|
||||
AsciiDoc from the AsciiDoc manual:
|
||||
|
||||
@@ -103,7 +103,7 @@ source, not just in the processed document.
|
||||
## Why is a spec needed?
|
||||
|
||||
John Gruber's [canonical description of Markdown's
|
||||
syntax](http://daringfireball.net/projects/markdown/syntax)
|
||||
syntax](https://daringfireball.net/projects/markdown/syntax)
|
||||
does not specify the syntax unambiguously. Here are some examples of
|
||||
questions it does not answer:
|
||||
|
||||
@@ -316,9 +316,9 @@ A line containing no characters, or a line containing only spaces
|
||||
|
||||
The following definitions of character classes will be used in this spec:
|
||||
|
||||
A [Unicode whitespace character](@) is
|
||||
any code point in the Unicode `Zs` general category, or a tab (`U+0009`),
|
||||
line feed (`U+000A`), form feed (`U+000C`), or carriage return (`U+000D`).
|
||||
A [Unicode whitespace character](@) is a character in the Unicode `Zs` general
|
||||
category, or a tab (`U+0009`), line feed (`U+000A`), form feed (`U+000C`), or
|
||||
carriage return (`U+000D`).
|
||||
|
||||
[Unicode whitespace](@) is a sequence of one or more
|
||||
[Unicode whitespace characters].
|
||||
@@ -337,9 +337,8 @@ is `!`, `"`, `#`, `$`, `%`, `&`, `'`, `(`, `)`,
|
||||
`[`, `\`, `]`, `^`, `_`, `` ` `` (U+005B–0060),
|
||||
`{`, `|`, `}`, or `~` (U+007B–007E).
|
||||
|
||||
A [Unicode punctuation character](@) is an [ASCII
|
||||
punctuation character] or anything in
|
||||
the general Unicode categories `Pc`, `Pd`, `Pe`, `Pf`, `Pi`, `Po`, or `Ps`.
|
||||
A [Unicode punctuation character](@) is a character in the Unicode `P`
|
||||
(puncuation) or `S` (symbol) general categories.
|
||||
|
||||
## Tabs
|
||||
|
||||
@@ -579,9 +578,9 @@ raw HTML:
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
<http://example.com?find=\*>
|
||||
<https://example.com?find=\*>
|
||||
.
|
||||
<p><a href="http://example.com?find=%5C*">http://example.com?find=\*</a></p>
|
||||
<p><a href="https://example.com?find=%5C*">https://example.com?find=\*</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -1964,7 +1963,7 @@ has been found, the code block contains all of the lines after the
|
||||
opening code fence until the end of the containing block (or
|
||||
document). (An alternative spec would require backtracking in the
|
||||
event that a closing code fence is not found. But this makes parsing
|
||||
much less efficient, and there seems to be no real down side to the
|
||||
much less efficient, and there seems to be no real downside to the
|
||||
behavior described here.)
|
||||
|
||||
A fenced code block may interrupt a paragraph, and does not require
|
||||
@@ -2403,7 +2402,7 @@ followed by one of the strings (case-insensitive) `address`,
|
||||
`h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `head`, `header`, `hr`,
|
||||
`html`, `iframe`, `legend`, `li`, `link`, `main`, `menu`, `menuitem`,
|
||||
`nav`, `noframes`, `ol`, `optgroup`, `option`, `p`, `param`,
|
||||
`section`, `source`, `summary`, `table`, `tbody`, `td`,
|
||||
`search`, `section`, `summary`, `table`, `tbody`, `td`,
|
||||
`tfoot`, `th`, `thead`, `title`, `tr`, `track`, `ul`, followed
|
||||
by a space, a tab, the end of the line, the string `>`, or
|
||||
the string `/>`.\
|
||||
@@ -4115,7 +4114,7 @@ The following rules define [list items]:
|
||||
blocks *Bs* starting with a character other than a space or tab, and *M* is
|
||||
a list marker of width *W* followed by 1 ≤ *N* ≤ 4 spaces of indentation,
|
||||
then the result of prepending *M* and the following spaces to the first line
|
||||
of Ls*, and indenting subsequent lines of *Ls* by *W + N* spaces, is a
|
||||
of *Ls*, and indenting subsequent lines of *Ls* by *W + N* spaces, is a
|
||||
list item with *Bs* as its contents. The type of the list item
|
||||
(bullet or ordered) is determined by the type of its list marker.
|
||||
If the list item is ordered, then it is also assigned a start
|
||||
@@ -5350,11 +5349,11 @@ by itself should be a paragraph followed by a nested sublist.
|
||||
Since it is well established Markdown practice to allow lists to
|
||||
interrupt paragraphs inside list items, the [principle of
|
||||
uniformity] requires us to allow this outside list items as
|
||||
well. ([reStructuredText](http://docutils.sourceforge.net/rst.html)
|
||||
well. ([reStructuredText](https://docutils.sourceforge.net/rst.html)
|
||||
takes a different approach, requiring blank lines before lists
|
||||
even inside other list items.)
|
||||
|
||||
In order to solve of unwanted lists in paragraphs with
|
||||
In order to solve the problem of unwanted lists in paragraphs with
|
||||
hard-wrapped numerals, we allow only lists starting with `1` to
|
||||
interrupt paragraphs. Thus,
|
||||
|
||||
@@ -6055,18 +6054,18 @@ But this is an HTML tag:
|
||||
And this is code:
|
||||
|
||||
```````````````````````````````` example
|
||||
`<http://foo.bar.`baz>`
|
||||
`<https://foo.bar.`baz>`
|
||||
.
|
||||
<p><code><http://foo.bar.</code>baz>`</p>
|
||||
<p><code><https://foo.bar.</code>baz>`</p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
But this is an autolink:
|
||||
|
||||
```````````````````````````````` example
|
||||
<http://foo.bar.`baz>`
|
||||
<https://foo.bar.`baz>`
|
||||
.
|
||||
<p><a href="http://foo.bar.%60baz">http://foo.bar.`baz</a>`</p>
|
||||
<p><a href="https://foo.bar.%60baz">https://foo.bar.`baz</a>`</p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -6099,7 +6098,7 @@ closing backtick strings to be equal in length:
|
||||
## Emphasis and strong emphasis
|
||||
|
||||
John Gruber's original [Markdown syntax
|
||||
description](http://daringfireball.net/projects/markdown/syntax#em) says:
|
||||
description](https://daringfireball.net/projects/markdown/syntax#em) says:
|
||||
|
||||
> Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
|
||||
> emphasis. Text wrapped with one `*` or `_` will be wrapped with an HTML
|
||||
@@ -6201,7 +6200,7 @@ Here are some examples of delimiter runs.
|
||||
(The idea of distinguishing left-flanking and right-flanking
|
||||
delimiter runs based on the character before and the character
|
||||
after comes from Roopesh Chander's
|
||||
[vfmd](http://www.vfmd.org/vfmd-spec/specification/#procedure-for-identifying-emphasis-tags).
|
||||
[vfmd](https://web.archive.org/web/20220608143320/http://www.vfmd.org/vfmd-spec/specification/#procedure-for-identifying-emphasis-tags).
|
||||
vfmd uses the terminology "emphasis indicator string" instead of "delimiter
|
||||
run," and its rules for distinguishing left- and right-flanking runs
|
||||
are a bit more complex than the ones given here.)
|
||||
@@ -6343,6 +6342,21 @@ Unicode nonbreaking spaces count as whitespace, too:
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
Unicode symbols count as punctuation, too:
|
||||
|
||||
```````````````````````````````` example
|
||||
*$*alpha.
|
||||
|
||||
*£*bravo.
|
||||
|
||||
*€*charlie.
|
||||
.
|
||||
<p>*$*alpha.</p>
|
||||
<p>*£*bravo.</p>
|
||||
<p>*€*charlie.</p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
Intraword emphasis with `*` is permitted:
|
||||
|
||||
```````````````````````````````` example
|
||||
@@ -7428,16 +7442,16 @@ _a `_`_
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
**a<http://foo.bar/?q=**>
|
||||
**a<https://foo.bar/?q=**>
|
||||
.
|
||||
<p>**a<a href="http://foo.bar/?q=**">http://foo.bar/?q=**</a></p>
|
||||
<p>**a<a href="https://foo.bar/?q=**">https://foo.bar/?q=**</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
__a<http://foo.bar/?q=__>
|
||||
__a<https://foo.bar/?q=__>
|
||||
.
|
||||
<p>__a<a href="http://foo.bar/?q=__">http://foo.bar/?q=__</a></p>
|
||||
<p>__a<a href="https://foo.bar/?q=__">https://foo.bar/?q=__</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -7685,13 +7699,13 @@ A link can contain fragment identifiers and queries:
|
||||
```````````````````````````````` example
|
||||
[link](#fragment)
|
||||
|
||||
[link](http://example.com#fragment)
|
||||
[link](https://example.com#fragment)
|
||||
|
||||
[link](http://example.com?foo=3#frag)
|
||||
[link](https://example.com?foo=3#frag)
|
||||
.
|
||||
<p><a href="#fragment">link</a></p>
|
||||
<p><a href="http://example.com#fragment">link</a></p>
|
||||
<p><a href="http://example.com?foo=3#frag">link</a></p>
|
||||
<p><a href="https://example.com#fragment">link</a></p>
|
||||
<p><a href="https://example.com?foo=3#frag">link</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -7935,9 +7949,9 @@ and autolinks over link grouping:
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
[foo<http://example.com/?search=](uri)>
|
||||
[foo<https://example.com/?search=](uri)>
|
||||
.
|
||||
<p>[foo<a href="http://example.com/?search=%5D(uri)">http://example.com/?search=](uri)</a></p>
|
||||
<p>[foo<a href="https://example.com/?search=%5D(uri)">https://example.com/?search=](uri)</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -8091,11 +8105,11 @@ and autolinks over link grouping:
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
[foo<http://example.com/?search=][ref]>
|
||||
[foo<https://example.com/?search=][ref]>
|
||||
|
||||
[ref]: /uri
|
||||
.
|
||||
<p>[foo<a href="http://example.com/?search=%5D%5Bref%5D">http://example.com/?search=][ref]</a></p>
|
||||
<p>[foo<a href="https://example.com/?search=%5D%5Bref%5D">https://example.com/?search=][ref]</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -8295,7 +8309,7 @@ A [collapsed reference link](@)
|
||||
consists of a [link label] that [matches] a
|
||||
[link reference definition] elsewhere in the
|
||||
document, followed by the string `[]`.
|
||||
The contents of the first link label are parsed as inlines,
|
||||
The contents of the link label are parsed as inlines,
|
||||
which are used as the link's text. The link's URI and title are
|
||||
provided by the matching reference link definition. Thus,
|
||||
`[foo][]` is equivalent to `[foo][foo]`.
|
||||
@@ -8348,7 +8362,7 @@ A [shortcut reference link](@)
|
||||
consists of a [link label] that [matches] a
|
||||
[link reference definition] elsewhere in the
|
||||
document and is not followed by `[]` or a link label.
|
||||
The contents of the first link label are parsed as inlines,
|
||||
The contents of the link label are parsed as inlines,
|
||||
which are used as the link's text. The link's URI and title
|
||||
are provided by the matching link reference definition.
|
||||
Thus, `[foo]` is equivalent to `[foo][]`.
|
||||
@@ -8435,7 +8449,7 @@ following closing bracket:
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
Full and compact references take precedence over shortcut
|
||||
Full and collapsed references take precedence over shortcut
|
||||
references:
|
||||
|
||||
```````````````````````````````` example
|
||||
@@ -8771,9 +8785,9 @@ Here are some valid autolinks:
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
<http://foo.bar.baz/test?q=hello&id=22&boolean>
|
||||
<https://foo.bar.baz/test?q=hello&id=22&boolean>
|
||||
.
|
||||
<p><a href="http://foo.bar.baz/test?q=hello&id=22&boolean">http://foo.bar.baz/test?q=hello&id=22&boolean</a></p>
|
||||
<p><a href="https://foo.bar.baz/test?q=hello&id=22&boolean">https://foo.bar.baz/test?q=hello&id=22&boolean</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -8813,9 +8827,9 @@ with their syntax:
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
<http://../>
|
||||
<https://../>
|
||||
.
|
||||
<p><a href="http://../">http://../</a></p>
|
||||
<p><a href="https://../">https://../</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -8829,18 +8843,18 @@ with their syntax:
|
||||
Spaces are not allowed in autolinks:
|
||||
|
||||
```````````````````````````````` example
|
||||
<http://foo.bar/baz bim>
|
||||
<https://foo.bar/baz bim>
|
||||
.
|
||||
<p><http://foo.bar/baz bim></p>
|
||||
<p><https://foo.bar/baz bim></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
Backslash-escapes do not work inside autolinks:
|
||||
|
||||
```````````````````````````````` example
|
||||
<http://example.com/\[\>
|
||||
<https://example.com/\[\>
|
||||
.
|
||||
<p><a href="http://example.com/%5C%5B%5C">http://example.com/\[\</a></p>
|
||||
<p><a href="https://example.com/%5C%5B%5C">https://example.com/\[\</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -8892,9 +8906,9 @@ These are not autolinks:
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
< http://foo.bar >
|
||||
< https://foo.bar >
|
||||
.
|
||||
<p>< http://foo.bar ></p>
|
||||
<p>< https://foo.bar ></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -8913,9 +8927,9 @@ These are not autolinks:
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
http://example.com
|
||||
https://example.com
|
||||
.
|
||||
<p>http://example.com</p>
|
||||
<p>https://example.com</p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -8977,10 +8991,9 @@ A [closing tag](@) consists of the string `</`, a
|
||||
[tag name], optional spaces, tabs, and up to one line ending, and the character
|
||||
`>`.
|
||||
|
||||
An [HTML comment](@) consists of `<!--` + *text* + `-->`,
|
||||
where *text* does not start with `>` or `->`, does not end with `-`,
|
||||
and does not contain `--`. (See the
|
||||
[HTML5 spec](http://www.w3.org/TR/html5/syntax.html#comments).)
|
||||
An [HTML comment](@) consists of `<!-->`, `<!--->`, or `<!--`, a string of
|
||||
characters not including the string `-->`, and `-->` (see the
|
||||
[HTML spec](https://html.spec.whatwg.org/multipage/parsing.html#markup-declaration-open-state)).
|
||||
|
||||
A [processing instruction](@)
|
||||
consists of the string `<?`, a string
|
||||
@@ -9119,30 +9132,20 @@ Illegal attributes in closing tag:
|
||||
Comments:
|
||||
|
||||
```````````````````````````````` example
|
||||
foo <!-- this is a
|
||||
comment - with hyphen -->
|
||||
foo <!-- this is a --
|
||||
comment - with hyphens -->
|
||||
.
|
||||
<p>foo <!-- this is a
|
||||
comment - with hyphen --></p>
|
||||
<p>foo <!-- this is a --
|
||||
comment - with hyphens --></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
foo <!-- not a comment -- two hyphens -->
|
||||
.
|
||||
<p>foo <!-- not a comment -- two hyphens --></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
Not comments:
|
||||
|
||||
```````````````````````````````` example
|
||||
foo <!--> foo -->
|
||||
|
||||
foo <!-- foo--->
|
||||
foo <!---> foo -->
|
||||
.
|
||||
<p>foo <!--> foo --></p>
|
||||
<p>foo <!-- foo---></p>
|
||||
<p>foo <!--> foo --></p>
|
||||
<p>foo <!---> foo --></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -9671,7 +9674,7 @@ through the stack for an opening `[` or `![` delimiter.
|
||||
delimiter from the stack, and return a literal text node `]`.
|
||||
|
||||
- If we find one and it's active, then we parse ahead to see if
|
||||
we have an inline link/image, reference link/image, compact reference
|
||||
we have an inline link/image, reference link/image, collapsed reference
|
||||
link/image, or shortcut reference link/image.
|
||||
|
||||
+ If we don't, then we remove the opening delimiter from the
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Markdig.Tests.Specs.Diagrams
|
||||
//
|
||||
// ## Mermaid diagrams
|
||||
//
|
||||
// Using a fenced code block with the `mermaid` language info will output a `<div class='mermaid'>` instead of a `pre/code` block:
|
||||
// Using a fenced code block with the `mermaid` language info will output a `<pre class='mermaid'>` block (which is the default for other code block):
|
||||
[Test]
|
||||
public void ExtensionsMermaidDiagrams_Example001()
|
||||
{
|
||||
@@ -34,14 +34,14 @@ namespace Markdig.Tests.Specs.Diagrams
|
||||
// ```
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <div class="mermaid">graph TD;
|
||||
// <pre class="mermaid">graph TD;
|
||||
// A-->B;
|
||||
// A-->C;
|
||||
// B-->D;
|
||||
// C-->D;
|
||||
// </div>
|
||||
// </pre>
|
||||
|
||||
TestParser.TestSpec("```mermaid\ngraph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n```", "<div class=\"mermaid\">graph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n</div>", "diagrams|advanced", context: "Example 1\nSection Extensions / Mermaid diagrams\n");
|
||||
TestParser.TestSpec("```mermaid\ngraph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n```", "<pre class=\"mermaid\">graph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n</pre>", "diagrams|advanced", context: "Example 1\nSection Extensions / Mermaid diagrams\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Adds support for diagrams extension:
|
||||
|
||||
## Mermaid diagrams
|
||||
|
||||
Using a fenced code block with the `mermaid` language info will output a `<div class='mermaid'>` instead of a `pre/code` block:
|
||||
Using a fenced code block with the `mermaid` language info will output a `<pre class='mermaid'>` block (which is the default for other code block):
|
||||
|
||||
```````````````````````````````` example
|
||||
```mermaid
|
||||
@@ -15,12 +15,12 @@ graph TD;
|
||||
C-->D;
|
||||
```
|
||||
.
|
||||
<div class="mermaid">graph TD;
|
||||
<pre class="mermaid">graph TD;
|
||||
A-->B;
|
||||
A-->C;
|
||||
B-->D;
|
||||
C-->D;
|
||||
</div>
|
||||
</pre>
|
||||
````````````````````````````````
|
||||
|
||||
## nomnoml diagrams
|
||||
|
||||
@@ -123,6 +123,8 @@ namespace Markdig.Tests.Specs.EmphasisExtra
|
||||
public class TestExtensionsEmphasisOnHtmlEntities
|
||||
{
|
||||
// ## Emphasis on Html Entities
|
||||
//
|
||||
// Note that Unicode symbols are treated as punctuation, which are not allowed to open the emphasis unless they are preceded by a space.
|
||||
[Test]
|
||||
public void ExtensionsEmphasisOnHtmlEntities_Example006()
|
||||
{
|
||||
@@ -132,14 +134,14 @@ namespace Markdig.Tests.Specs.EmphasisExtra
|
||||
// The following Markdown:
|
||||
// This is text MyBrand ^®^ and MyTrademark ^™^
|
||||
// This is text MyBrand^®^ and MyTrademark^™^
|
||||
// This is text MyBrand~®~ and MyCopyright^©^
|
||||
// This is text MyBrand ~®~ and MyCopyright ^©^
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is text MyBrand <sup>®</sup> and MyTrademark <sup>TM</sup>
|
||||
// This is text MyBrand<sup>®</sup> and MyTrademark<sup>TM</sup>
|
||||
// This is text MyBrand<sub>®</sub> and MyCopyright<sup>©</sup></p>
|
||||
// This is text MyBrand^®^ and MyTrademark^TM^
|
||||
// This is text MyBrand <sub>®</sub> and MyCopyright <sup>©</sup></p>
|
||||
|
||||
TestParser.TestSpec("This is text MyBrand ^®^ and MyTrademark ^™^\nThis is text MyBrand^®^ and MyTrademark^™^\nThis is text MyBrand~®~ and MyCopyright^©^", "<p>This is text MyBrand <sup>®</sup> and MyTrademark <sup>TM</sup>\nThis is text MyBrand<sup>®</sup> and MyTrademark<sup>TM</sup>\nThis is text MyBrand<sub>®</sub> and MyCopyright<sup>©</sup></p>", "emphasisextras|advanced", context: "Example 6\nSection Extensions / Emphasis on Html Entities\n");
|
||||
TestParser.TestSpec("This is text MyBrand ^®^ and MyTrademark ^™^\nThis is text MyBrand^®^ and MyTrademark^™^\nThis is text MyBrand ~®~ and MyCopyright ^©^", "<p>This is text MyBrand <sup>®</sup> and MyTrademark <sup>TM</sup>\nThis is text MyBrand^®^ and MyTrademark^TM^\nThis is text MyBrand <sub>®</sub> and MyCopyright <sup>©</sup></p>", "emphasisextras|advanced", context: "Example 6\nSection Extensions / Emphasis on Html Entities\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,16 +52,17 @@ 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
|
||||
|
||||
Note that Unicode symbols are treated as punctuation, which are not allowed to open the emphasis unless they are preceded by a space.
|
||||
|
||||
```````````````````````````````` example
|
||||
This is text MyBrand ^®^ and MyTrademark ^™^
|
||||
This is text MyBrand^®^ and MyTrademark^™^
|
||||
This is text MyBrand~®~ and MyCopyright^©^
|
||||
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>
|
||||
This is text MyBrand^®^ and MyTrademark^TM^
|
||||
This is text MyBrand <sub>®</sub> and MyCopyright <sup>©</sup></p>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// ## Grid Table
|
||||
//
|
||||
// A grid table allows to have multiple lines per cells and allows to span cells over multiple columns. The following shows a simple grid table
|
||||
// A grid table allows having multiple lines per cells and allows spanning cells over multiple columns.
|
||||
// The following shows a simple grid table:
|
||||
//
|
||||
// ```
|
||||
// +---------+---------+
|
||||
@@ -38,17 +39,20 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// ```
|
||||
//
|
||||
// **Rule #1**
|
||||
// The first line of a grid table must a **row separator**. It must start with the column separator character `+` used to separate columns in a row separator. Each column separator is:
|
||||
// - starting by optional spaces
|
||||
// - followed by an optional `:` to specify left align, followed by optional spaces
|
||||
// - followed by a sequence of at least one `-` character, followed by optional spaces
|
||||
// - followed by an optional `:` to specify right align (or center align if left align is also defined)
|
||||
// - ending by optional spaces
|
||||
// The first line of a grid table must be a **row separator**.
|
||||
// It must start with the column separator character `+` used to separate columns in a row separator.
|
||||
//
|
||||
// The first row separator must be followed by a *regular row*. A regular row must start with the character `|` that is starting at the same position than the column separator `+` of the first row separator.
|
||||
// Each column separator:
|
||||
// - starts with optional spaces
|
||||
// - followed by an optional `:` to specify left align, followed by optional spaces
|
||||
// - followed by a sequence of one or more `-` characters, followed by optional spaces
|
||||
// - followed by an optional `:` to specify right align (or center align if left align is also defined)
|
||||
// - ends with optional spaces
|
||||
//
|
||||
// The first row separator must be followed by a *regular row*.
|
||||
// A regular row must start with a `|` character starting at the same position as the column separator `+` of the first row separator.
|
||||
//
|
||||
// The following is a valid row separator
|
||||
// The following is a valid row separator:
|
||||
[Test]
|
||||
public void ExtensionsGridTable_Example001()
|
||||
{
|
||||
@@ -74,7 +78,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table |", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced", context: "Example 1\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// The following is not a valid row separator
|
||||
// The following is not a valid row separator:
|
||||
[Test]
|
||||
public void ExtensionsGridTable_Example002()
|
||||
{
|
||||
@@ -93,7 +97,8 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
}
|
||||
|
||||
// **Rule #2**
|
||||
// A regular row can continue a previous regular row when column separator `|` are positioned at the same position than the previous line. If they are positioned at the same location, the column may span over multiple columns:
|
||||
// A regular row can continue a previous regular row when the column separators `|` are positioned at the same position as those of the previous line.
|
||||
// If they are positioned at the same location, the column may span over multiple columns:
|
||||
[Test]
|
||||
public void ExtensionsGridTable_Example003()
|
||||
{
|
||||
@@ -187,15 +192,13 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table with a longer text in the second column", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table with a longer text in the second column</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced", context: "Example 5\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// The respective width of the columns are calculated from the ratio between the total size of the first table row without counting the `+`: `+----+--------+----+` would be divided between:
|
||||
// The respective widths of the columns are calculated from the ratio between the total size of the first table row without counting the `+`: `+----+--------+----+` would be divided between:
|
||||
//
|
||||
// Total size is : 16
|
||||
// - `----` → 4 characters
|
||||
// - `--------` → 8 characters
|
||||
// - `----` → 4 characters
|
||||
//
|
||||
// - `----` -> 4
|
||||
// - `--------` -> 8
|
||||
// - `----` -> 4
|
||||
//
|
||||
// So the width would be 4/16 = 25%, 8/16 = 50%, 4/16 = 25%
|
||||
// The total size is 16 characters, so the widths would be 4/16 = 25%, 8/16 = 50%, and 4/16 = 25%.
|
||||
[Test]
|
||||
public void ExtensionsGridTable_Example006()
|
||||
{
|
||||
@@ -296,7 +299,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+---+---+ B +\n| D | E | B |\n+ D +---+---+\n| D | CCCCC |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td colspan=\"2\">AAAAA</td>\n<td rowspan=\"2\">B\nB\nB</td>\n</tr>\n<tr>\n<td rowspan=\"2\">D\nD\nD</td>\n<td>E</td>\n</tr>\n<tr>\n<td colspan=\"2\">CCCCC</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced", context: "Example 8\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// A grid table may have cells with both colspan and rowspan:
|
||||
// A grid table may have cells with both `colspan` and `rowspan`:
|
||||
[Test]
|
||||
public void ExtensionsGridTable_Example009()
|
||||
{
|
||||
|
||||
@@ -4,7 +4,8 @@ This section describes the different extensions supported:
|
||||
|
||||
## Grid Table
|
||||
|
||||
A grid table allows to have multiple lines per cells and allows to span cells over multiple columns. The following shows a simple grid table
|
||||
A grid table allows having multiple lines per cells and allows spanning cells over multiple columns.
|
||||
The following shows a simple grid table:
|
||||
|
||||
```
|
||||
+---------+---------+
|
||||
@@ -25,17 +26,20 @@ A grid table allows to have multiple lines per cells and allows to span cells ov
|
||||
```
|
||||
|
||||
**Rule #1**
|
||||
The first line of a grid table must a **row separator**. It must start with the column separator character `+` used to separate columns in a row separator. Each column separator is:
|
||||
- starting by optional spaces
|
||||
- followed by an optional `:` to specify left align, followed by optional spaces
|
||||
- followed by a sequence of at least one `-` character, followed by optional spaces
|
||||
- followed by an optional `:` to specify right align (or center align if left align is also defined)
|
||||
- ending by optional spaces
|
||||
The first line of a grid table must be a **row separator**.
|
||||
It must start with the column separator character `+` used to separate columns in a row separator.
|
||||
|
||||
The first row separator must be followed by a *regular row*. A regular row must start with the character `|` that is starting at the same position than the column separator `+` of the first row separator.
|
||||
Each column separator:
|
||||
- starts with optional spaces
|
||||
- followed by an optional `:` to specify left align, followed by optional spaces
|
||||
- followed by a sequence of one or more `-` characters, followed by optional spaces
|
||||
- followed by an optional `:` to specify right align (or center align if left align is also defined)
|
||||
- ends with optional spaces
|
||||
|
||||
The first row separator must be followed by a *regular row*.
|
||||
A regular row must start with a `|` character starting at the same position as the column separator `+` of the first row separator.
|
||||
|
||||
The following is a valid row separator
|
||||
The following is a valid row separator:
|
||||
|
||||
```````````````````````````````` example
|
||||
+---------+---------+
|
||||
@@ -53,8 +57,8 @@ The following is a valid row separator
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
The following is not a valid row separator:
|
||||
|
||||
The following is not a valid row separator
|
||||
```````````````````````````````` example
|
||||
|-----xxx----+---------+
|
||||
| This is | not a table
|
||||
@@ -64,7 +68,8 @@ The following is not a valid row separator
|
||||
````````````````````````````````
|
||||
|
||||
**Rule #2**
|
||||
A regular row can continue a previous regular row when column separator `|` are positioned at the same position than the previous line. If they are positioned at the same location, the column may span over multiple columns:
|
||||
A regular row can continue a previous regular row when the column separators `|` are positioned at the same position as those of the previous line.
|
||||
If they are positioned at the same location, the column may span over multiple columns:
|
||||
|
||||
```````````````````````````````` example
|
||||
+---------+---------+---------+
|
||||
@@ -134,15 +139,13 @@ The last column separator `|` may be omitted:
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
The respective width of the columns are calculated from the ratio between the total size of the first table row without counting the `+`: `+----+--------+----+` would be divided between:
|
||||
The respective widths of the columns are calculated from the ratio between the total size of the first table row without counting the `+`: `+----+--------+----+` would be divided between:
|
||||
|
||||
Total size is : 16
|
||||
- `----` → 4 characters
|
||||
- `--------` → 8 characters
|
||||
- `----` → 4 characters
|
||||
|
||||
- `----` -> 4
|
||||
- `--------` -> 8
|
||||
- `----` -> 4
|
||||
|
||||
So the width would be 4/16 = 25%, 8/16 = 50%, 4/16 = 25%
|
||||
The total size is 16 characters, so the widths would be 4/16 = 25%, 8/16 = 50%, and 4/16 = 25%.
|
||||
|
||||
```````````````````````````````` example
|
||||
+----+--------+----+
|
||||
@@ -165,7 +168,6 @@ So the width would be 4/16 = 25%, 8/16 = 50%, 4/16 = 25%
|
||||
|
||||
Alignment might be specified on the first row using the character `:`:
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
+-----+:---:+-----+
|
||||
| A | B | C |
|
||||
@@ -220,7 +222,7 @@ D</td>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
A grid table may have cells with both colspan and rowspan:
|
||||
A grid table may have cells with both `colspan` and `rowspan`:
|
||||
|
||||
```````````````````````````````` example
|
||||
+---+---+---+
|
||||
@@ -276,7 +278,6 @@ A grid table may not have irregularly shaped cells:
|
||||
|
||||
An empty `+` on a line should result in a simple empty list output:
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
+
|
||||
.
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
//
|
||||
// ## Math Inline
|
||||
//
|
||||
// Allows to define a mathematic block embraced by `$...$`
|
||||
// Allows to define a mathematic inline block embraced by `$...$`
|
||||
[Test]
|
||||
public void ExtensionsMathInline_Example001()
|
||||
{
|
||||
@@ -25,12 +25,12 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Section: Extensions / Math Inline
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a $math block$
|
||||
// This is a $math inline$
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math block\)</span></p>
|
||||
// <p>This is a <span class="math">\(math inline\)</span></p>
|
||||
|
||||
TestParser.TestSpec("This is a $math block$", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced", context: "Example 1\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $math inline$", "<p>This is a <span class=\"math\">\\(math inline\\)</span></p>", "mathematics|advanced", context: "Example 1\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// Or by `$$...$$` embracing it by:
|
||||
@@ -41,12 +41,12 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Section: Extensions / Math Inline
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a $$math block$$
|
||||
// This is a $$math inline$$
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math block\)</span></p>
|
||||
// <p>This is a <span class="math">\(math inline\)</span></p>
|
||||
|
||||
TestParser.TestSpec("This is a $$math block$$", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced", context: "Example 2\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $$math inline$$", "<p>This is a <span class=\"math\">\\(math inline\\)</span></p>", "mathematics|advanced", context: "Example 2\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// Newlines inside an inline math are not allowed:
|
||||
@@ -58,13 +58,13 @@ namespace Markdig.Tests.Specs.Math
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is not a $$math
|
||||
// block$$ and? this is a $$math block$$
|
||||
// inline$$ and? this is a $$math inline$$
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is not a $$math
|
||||
// block$$ and? this is a <span class="math">\(math block\)</span></p>
|
||||
// inline$$ and? this is a <span class="math">\(math inline\)</span></p>
|
||||
|
||||
TestParser.TestSpec("This is not a $$math \nblock$$ and? this is a $$math block$$", "<p>This is not a $$math\nblock$$ and? this is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced", context: "Example 3\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is not a $$math \ninline$$ and? this is a $$math inline$$", "<p>This is not a $$math\ninline$$ and? this is a <span class=\"math\">\\(math inline\\)</span></p>", "mathematics|advanced", context: "Example 3\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -75,13 +75,13 @@ namespace Markdig.Tests.Specs.Math
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is not a $math
|
||||
// block$ and? this is a $math block$
|
||||
// inline$ and? this is a $math inline$
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is not a $math
|
||||
// block$ and? this is a <span class="math">\(math block\)</span></p>
|
||||
// inline$ and? this is a <span class="math">\(math inline\)</span></p>
|
||||
|
||||
TestParser.TestSpec("This is not a $math \nblock$ and? this is a $math block$", "<p>This is not a $math\nblock$ and? this is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced", context: "Example 4\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is not a $math \ninline$ and? this is a $math inline$", "<p>This is not a $math\ninline$ and? this is a <span class=\"math\">\\(math inline\\)</span></p>", "mathematics|advanced", context: "Example 4\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// An opening `$` can be followed by a space if the closing is also preceded by a space `$`:
|
||||
@@ -92,12 +92,12 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Section: Extensions / Math Inline
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a $ math block $
|
||||
// This is a $ math inline $
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math block\)</span></p>
|
||||
// <p>This is a <span class="math">\(math inline\)</span></p>
|
||||
|
||||
TestParser.TestSpec("This is a $ math block $", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced", context: "Example 5\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $ math inline $", "<p>This is a <span class=\"math\">\\(math inline\\)</span></p>", "mathematics|advanced", context: "Example 5\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -107,12 +107,12 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Section: Extensions / Math Inline
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a $ math block $ after
|
||||
// This is a $ math inline $ after
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math block\)</span> after</p>
|
||||
// <p>This is a <span class="math">\(math inline\)</span> after</p>
|
||||
|
||||
TestParser.TestSpec("This is a $ math block $ after", "<p>This is a <span class=\"math\">\\(math block\\)</span> after</p>", "mathematics|advanced", context: "Example 6\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $ math inline $ after", "<p>This is a <span class=\"math\">\\(math inline\\)</span> after</p>", "mathematics|advanced", context: "Example 6\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -122,12 +122,12 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Section: Extensions / Math Inline
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a $$ math block $$ after
|
||||
// This is a $$ math inline $$ after
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math block\)</span> after</p>
|
||||
// <p>This is a <span class="math">\(math inline\)</span> after</p>
|
||||
|
||||
TestParser.TestSpec("This is a $$ math block $$ after", "<p>This is a <span class=\"math\">\\(math block\\)</span> after</p>", "mathematics|advanced", context: "Example 7\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $$ math inline $$ after", "<p>This is a <span class=\"math\">\\(math inline\\)</span> after</p>", "mathematics|advanced", context: "Example 7\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -137,12 +137,12 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Section: Extensions / Math Inline
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a not $ math block$ because there is not a whitespace before the closing
|
||||
// This is a not $ math inline$ because there is not a whitespace before the closing
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a not $ math block$ because there is not a whitespace before the closing</p>
|
||||
// <p>This is a not $ math inline$ because there is not a whitespace before the closing</p>
|
||||
|
||||
TestParser.TestSpec("This is a not $ math block$ because there is not a whitespace before the closing", "<p>This is a not $ math block$ because there is not a whitespace before the closing</p>", "mathematics|advanced", context: "Example 8\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a not $ math inline$ because there is not a whitespace before the closing", "<p>This is a not $ math inline$ because there is not a whitespace before the closing</p>", "mathematics|advanced", context: "Example 8\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// For the opening `$` it requires a space or a punctuation before (but cannot be used within a word):
|
||||
@@ -153,12 +153,12 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Section: Extensions / Math Inline
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is not a m$ath block$
|
||||
// This is not a m$ath inline$
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is not a m$ath block$</p>
|
||||
// <p>This is not a m$ath inline$</p>
|
||||
|
||||
TestParser.TestSpec("This is not a m$ath block$", "<p>This is not a m$ath block$</p>", "mathematics|advanced", context: "Example 9\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is not a m$ath inline$", "<p>This is not a m$ath inline$</p>", "mathematics|advanced", context: "Example 9\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// For the closing `$` it requires a space after or a punctuation (but cannot be preceded by a space and cannot be used within a word):
|
||||
@@ -169,12 +169,12 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Section: Extensions / Math Inline
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is not a $math bloc$k
|
||||
// This is not a $math inlin$e
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is not a $math bloc$k</p>
|
||||
// <p>This is not a $math inlin$e</p>
|
||||
|
||||
TestParser.TestSpec("This is not a $math bloc$k", "<p>This is not a $math bloc$k</p>", "mathematics|advanced", context: "Example 10\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is not a $math inlin$e", "<p>This is not a $math inlin$e</p>", "mathematics|advanced", context: "Example 10\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// For the closing `$` it requires a space after or a punctuation (but cannot be preceded by a space and cannot be used within a word):
|
||||
@@ -201,12 +201,12 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Section: Extensions / Math Inline
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a $math \$ block$
|
||||
// This is a $math \$ inline$
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math \$ block\)</span></p>
|
||||
// <p>This is a <span class="math">\(math \$ inline\)</span></p>
|
||||
|
||||
TestParser.TestSpec("This is a $math \\$ block$", "<p>This is a <span class=\"math\">\\(math \\$ block\\)</span></p>", "mathematics|advanced", context: "Example 12\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $math \\$ inline$", "<p>This is a <span class=\"math\">\\(math \\$ inline\\)</span></p>", "mathematics|advanced", context: "Example 12\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// At most, two `$` will be matched for the opening and closing:
|
||||
@@ -217,12 +217,12 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Section: Extensions / Math Inline
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a $$$math block$$$
|
||||
// This is a $$$math inline$$$
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\($math block$\)</span></p>
|
||||
// <p>This is a <span class="math">\($math inline$\)</span></p>
|
||||
|
||||
TestParser.TestSpec("This is a $$$math block$$$", "<p>This is a <span class=\"math\">\\($math block$\\)</span></p>", "mathematics|advanced", context: "Example 13\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $$$math inline$$$", "<p>This is a <span class=\"math\">\\($math inline$\\)</span></p>", "mathematics|advanced", context: "Example 13\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// Regular text can come both before and after the math inline
|
||||
@@ -233,15 +233,15 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Section: Extensions / Math Inline
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a $math block$ with text on both sides.
|
||||
// This is a $math inline$ with text on both sides.
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math block\)</span> with text on both sides.</p>
|
||||
// <p>This is a <span class="math">\(math inline\)</span> with text on both sides.</p>
|
||||
|
||||
TestParser.TestSpec("This is a $math block$ with text on both sides.", "<p>This is a <span class=\"math\">\\(math block\\)</span> with text on both sides.</p>", "mathematics|advanced", context: "Example 14\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $math inline$ with text on both sides.", "<p>This is a <span class=\"math\">\\(math inline\\)</span> with text on both sides.</p>", "mathematics|advanced", context: "Example 14\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// A mathematic block takes precedence over standard emphasis `*` `_`:
|
||||
// A mathematic inline block takes precedence over standard emphasis `*` `_`:
|
||||
[Test]
|
||||
public void ExtensionsMathInline_Example015()
|
||||
{
|
||||
@@ -249,15 +249,15 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Section: Extensions / Math Inline
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is *a $math* block$
|
||||
// This is *a $math* inline$
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is *a <span class="math">\(math* block\)</span></p>
|
||||
// <p>This is *a <span class="math">\(math* inline\)</span></p>
|
||||
|
||||
TestParser.TestSpec("This is *a $math* block$", "<p>This is *a <span class=\"math\">\\(math* block\\)</span></p>", "mathematics|advanced", context: "Example 15\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is *a $math* inline$", "<p>This is *a <span class=\"math\">\\(math* inline\\)</span></p>", "mathematics|advanced", context: "Example 15\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// An opening $$ at the beginning of a line should not be interpreted as a Math block:
|
||||
// An opening $$ at the beginning of a line should not be interpreted as a Math inline:
|
||||
[Test]
|
||||
public void ExtensionsMathInline_Example016()
|
||||
{
|
||||
@@ -279,7 +279,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
{
|
||||
// ## Math Block
|
||||
//
|
||||
// The match block can spawn on multiple lines by having a $$ starting on a line.
|
||||
// The math block can spawn on multiple lines by having a $$ starting on a line.
|
||||
// It is working as a fenced code block.
|
||||
[Test]
|
||||
public void ExtensionsMathBlock_Example017()
|
||||
|
||||
@@ -4,79 +4,79 @@ Adds support for mathematics spans:
|
||||
|
||||
## Math Inline
|
||||
|
||||
Allows to define a mathematic block embraced by `$...$`
|
||||
Allows to define a mathematic inline block embraced by `$...$`
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a $math block$
|
||||
This is a $math inline$
|
||||
.
|
||||
<p>This is a <span class="math">\(math block\)</span></p>
|
||||
<p>This is a <span class="math">\(math inline\)</span></p>
|
||||
````````````````````````````````
|
||||
|
||||
Or by `$$...$$` embracing it by:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a $$math block$$
|
||||
This is a $$math inline$$
|
||||
.
|
||||
<p>This is a <span class="math">\(math block\)</span></p>
|
||||
<p>This is a <span class="math">\(math inline\)</span></p>
|
||||
````````````````````````````````
|
||||
|
||||
Newlines inside an inline math are not allowed:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not a $$math
|
||||
block$$ and? this is a $$math block$$
|
||||
inline$$ and? this is a $$math inline$$
|
||||
.
|
||||
<p>This is not a $$math
|
||||
block$$ and? this is a <span class="math">\(math block\)</span></p>
|
||||
inline$$ and? this is a <span class="math">\(math inline\)</span></p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not a $math
|
||||
block$ and? this is a $math block$
|
||||
inline$ and? this is a $math inline$
|
||||
.
|
||||
<p>This is not a $math
|
||||
block$ and? this is a <span class="math">\(math block\)</span></p>
|
||||
inline$ and? this is a <span class="math">\(math inline\)</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 $
|
||||
This is a $ math inline $
|
||||
.
|
||||
<p>This is a <span class="math">\(math block\)</span></p>
|
||||
<p>This is a <span class="math">\(math inline\)</span></p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a $ math block $ after
|
||||
This is a $ math inline $ after
|
||||
.
|
||||
<p>This is a <span class="math">\(math block\)</span> after</p>
|
||||
<p>This is a <span class="math">\(math inline\)</span> after</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a $$ math block $$ after
|
||||
This is a $$ math inline $$ after
|
||||
.
|
||||
<p>This is a <span class="math">\(math block\)</span> after</p>
|
||||
<p>This is a <span class="math">\(math inline\)</span> after</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a not $ math block$ because there is not a whitespace before the closing
|
||||
This is a not $ math inline$ 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>
|
||||
<p>This is a not $ math inline$ 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):
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not a m$ath block$
|
||||
This is not a m$ath inline$
|
||||
.
|
||||
<p>This is not a m$ath block$</p>
|
||||
<p>This is not a m$ath inline$</p>
|
||||
````````````````````````````````
|
||||
|
||||
For the closing `$` it requires a space after or a punctuation (but cannot be preceded by a space and cannot be used within a word):
|
||||
|
||||
```````````````````````````````` example
|
||||
This is not a $math bloc$k
|
||||
This is not a $math inlin$e
|
||||
.
|
||||
<p>This is not a $math bloc$k</p>
|
||||
<p>This is not a $math inlin$e</p>
|
||||
````````````````````````````````
|
||||
|
||||
For the closing `$` it requires a space after or a punctuation (but cannot be preceded by a space and cannot be used within a word):
|
||||
@@ -90,34 +90,34 @@ This is should not match a 16$ or a $15
|
||||
A `$` can be escaped between a math inline block by using the escape `\\`
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a $math \$ block$
|
||||
This is a $math \$ inline$
|
||||
.
|
||||
<p>This is a <span class="math">\(math \$ block\)</span></p>
|
||||
<p>This is a <span class="math">\(math \$ inline\)</span></p>
|
||||
````````````````````````````````
|
||||
|
||||
At most, two `$` will be matched for the opening and closing:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a $$$math block$$$
|
||||
This is a $$$math inline$$$
|
||||
.
|
||||
<p>This is a <span class="math">\($math block$\)</span></p>
|
||||
<p>This is a <span class="math">\($math inline$\)</span></p>
|
||||
````````````````````````````````
|
||||
|
||||
Regular text can come both before and after the math inline
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a $math block$ with text on both sides.
|
||||
This is a $math inline$ with text on both sides.
|
||||
.
|
||||
<p>This is a <span class="math">\(math block\)</span> with text on both sides.</p>
|
||||
<p>This is a <span class="math">\(math inline\)</span> with text on both sides.</p>
|
||||
````````````````````````````````
|
||||
A mathematic block takes precedence over standard emphasis `*` `_`:
|
||||
A mathematic inline block takes precedence over standard emphasis `*` `_`:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is *a $math* block$
|
||||
This is *a $math* inline$
|
||||
.
|
||||
<p>This is *a <span class="math">\(math* block\)</span></p>
|
||||
<p>This is *a <span class="math">\(math* inline\)</span></p>
|
||||
````````````````````````````````
|
||||
An opening $$ at the beginning of a line should not be interpreted as a Math block:
|
||||
An opening $$ at the beginning of a line should not be interpreted as a Math inline:
|
||||
|
||||
```````````````````````````````` example
|
||||
$$ math $$ starting at a line
|
||||
@@ -127,7 +127,7 @@ $$ math $$ starting at a line
|
||||
|
||||
## Math Block
|
||||
|
||||
The match block can spawn on multiple lines by having a $$ starting on a line.
|
||||
The math block can spawn on multiple lines by having a $$ starting on a line.
|
||||
It is working as a fenced code block.
|
||||
|
||||
```````````````````````````````` example
|
||||
|
||||
24
src/Markdig.Tests/TestAutoLinks.cs
Normal file
24
src/Markdig.Tests/TestAutoLinks.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Markdig.Extensions.AutoLinks;
|
||||
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestAutoLinks
|
||||
{
|
||||
[Test]
|
||||
[TestCase("https://localhost", "<p><a href=\"https://localhost\">https://localhost</a></p>")]
|
||||
[TestCase("http://localhost", "<p><a href=\"http://localhost\">http://localhost</a></p>")]
|
||||
[TestCase("https://l", "<p><a href=\"https://l\">https://l</a></p>")]
|
||||
[TestCase("www.l", "<p><a href=\"http://www.l\">www.l</a></p>")]
|
||||
[TestCase("https://localhost:5000", "<p><a href=\"https://localhost:5000\">https://localhost:5000</a></p>")]
|
||||
[TestCase("www.l:5000", "<p><a href=\"http://www.l:5000\">www.l:5000</a></p>")]
|
||||
public void TestLinksWithAllowDomainWithoutPeriod(string markdown, string expected)
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAutoLinks(new AutoLinkOptions { AllowDomainWithoutPeriod = true })
|
||||
.Build();
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
Assert.That(html, Is.EqualTo(expected).IgnoreWhiteSpace);
|
||||
}
|
||||
}
|
||||
262
src/Markdig.Tests/TestCharHelper.cs
Normal file
262
src/Markdig.Tests/TestCharHelper.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
using System.Globalization;
|
||||
|
||||
using Markdig.Helpers;
|
||||
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestCharHelper
|
||||
{
|
||||
// An ASCII punctuation character is
|
||||
// !, ", #, $, %, &, ', (, ), *, +, ,, -, ., / (U+0021–2F),
|
||||
// :, ;, <, =, >, ?, @ (U+003A–0040),
|
||||
// [, \, ], ^, _, ` (U+005B–0060),
|
||||
// {, |, }, or ~ (U+007B–007E).
|
||||
private static readonly HashSet<char> s_asciiPunctuation = new()
|
||||
{
|
||||
'!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
|
||||
':', ';', '<', '=', '>', '?', '@',
|
||||
'[', '\\', ']', '^', '_', '`',
|
||||
'{', '|', '}', '~'
|
||||
};
|
||||
|
||||
// A Unicode punctuation character is a character in the Unicode P (punctuation) or S (symbol) general categories.
|
||||
private static readonly HashSet<UnicodeCategory> s_punctuationCategories =
|
||||
[
|
||||
UnicodeCategory.ConnectorPunctuation,
|
||||
UnicodeCategory.DashPunctuation,
|
||||
UnicodeCategory.OpenPunctuation,
|
||||
UnicodeCategory.ClosePunctuation,
|
||||
UnicodeCategory.InitialQuotePunctuation,
|
||||
UnicodeCategory.FinalQuotePunctuation,
|
||||
UnicodeCategory.OtherPunctuation,
|
||||
UnicodeCategory.MathSymbol,
|
||||
UnicodeCategory.CurrencySymbol,
|
||||
UnicodeCategory.ModifierSymbol,
|
||||
UnicodeCategory.OtherSymbol,
|
||||
];
|
||||
|
||||
private static readonly HashSet<UnicodeCategory> s_punctuationWithoutSymbolsCategories =
|
||||
[
|
||||
UnicodeCategory.ConnectorPunctuation,
|
||||
UnicodeCategory.DashPunctuation,
|
||||
UnicodeCategory.OpenPunctuation,
|
||||
UnicodeCategory.ClosePunctuation,
|
||||
UnicodeCategory.InitialQuotePunctuation,
|
||||
UnicodeCategory.FinalQuotePunctuation,
|
||||
UnicodeCategory.OtherPunctuation,
|
||||
];
|
||||
|
||||
private static bool ExpectedIsPunctuation(char c)
|
||||
{
|
||||
return c <= 127
|
||||
? s_asciiPunctuation.Contains(c)
|
||||
: s_punctuationCategories.Contains(CharUnicodeInfo.GetUnicodeCategory(c));
|
||||
}
|
||||
|
||||
private static bool ExpectedIsPunctuationWithoutSymbols(char c)
|
||||
{
|
||||
return c <= 127
|
||||
? s_asciiPunctuation.Contains(c)
|
||||
: s_punctuationWithoutSymbolsCategories.Contains(CharUnicodeInfo.GetUnicodeCategory(c));
|
||||
}
|
||||
|
||||
private static bool ExpectedIsWhitespace(char c)
|
||||
{
|
||||
// A Unicode whitespace character is any code point in the Unicode Zs general category,
|
||||
// or a tab (U+0009), line feed (U+000A), form feed (U+000C), or carriage return (U+000D).
|
||||
return c == '\t' || c == '\n' || c == '\f' || c == '\r' ||
|
||||
CharUnicodeInfo.GetUnicodeCategory(c) == UnicodeCategory.SpaceSeparator;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsAcrossTab()
|
||||
{
|
||||
Assert.False(CharHelper.IsAcrossTab(0));
|
||||
Assert.True(CharHelper.IsAcrossTab(1));
|
||||
Assert.True(CharHelper.IsAcrossTab(2));
|
||||
Assert.True(CharHelper.IsAcrossTab(3));
|
||||
Assert.False(CharHelper.IsAcrossTab(4));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddTab()
|
||||
{
|
||||
Assert.AreEqual(4, CharHelper.AddTab(0));
|
||||
Assert.AreEqual(4, CharHelper.AddTab(1));
|
||||
Assert.AreEqual(4, CharHelper.AddTab(2));
|
||||
Assert.AreEqual(4, CharHelper.AddTab(3));
|
||||
Assert.AreEqual(8, CharHelper.AddTab(4));
|
||||
Assert.AreEqual(8, CharHelper.AddTab(5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsWhitespace()
|
||||
{
|
||||
Test(
|
||||
ExpectedIsWhitespace,
|
||||
CharHelper.IsWhitespace);
|
||||
|
||||
Test(
|
||||
ExpectedIsWhitespace,
|
||||
CharHelper.WhitespaceChars.Contains);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsWhiteSpaceOrZero()
|
||||
{
|
||||
Test(
|
||||
c => ExpectedIsWhitespace(c) || c == 0,
|
||||
CharHelper.IsWhiteSpaceOrZero);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsAsciiPunctuation()
|
||||
{
|
||||
Test(
|
||||
c => char.IsAscii(c) && ExpectedIsPunctuation(c),
|
||||
CharHelper.IsAsciiPunctuation);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsAsciiPunctuationOrZero()
|
||||
{
|
||||
Test(
|
||||
c => char.IsAscii(c) && (ExpectedIsPunctuation(c) || c == 0),
|
||||
CharHelper.IsAsciiPunctuationOrZero);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsSpaceOrPunctuationForGFMAutoLink()
|
||||
{
|
||||
Test(
|
||||
c => c == 0 || ExpectedIsWhitespace(c) || ExpectedIsPunctuationWithoutSymbols(c),
|
||||
CharHelper.IsSpaceOrPunctuationForGFMAutoLink);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InvalidAutoLinkCharacters()
|
||||
{
|
||||
// 6.5 Autolinks - https://spec.commonmark.org/0.31.2/#autolinks
|
||||
// An absolute URI, for these purposes, consists of a scheme followed by a colon (:) followed by
|
||||
// zero or more characters other than ASCII control characters, space, <, and >.
|
||||
//
|
||||
// 2.1 Characters and lines
|
||||
// An ASCII control character is a character between U+0000–1F (both including) or U+007F.
|
||||
Test(
|
||||
c => c != 0 && c is < (char)0x20 or ' ' or '<' or '>' or '\u007F',
|
||||
CharHelper.InvalidAutoLinkCharacters.Contains);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CheckUnicodeCategory()
|
||||
{
|
||||
for (int i = char.MinValue; i <= char.MaxValue; i++)
|
||||
{
|
||||
char c = (char)i;
|
||||
|
||||
bool expectedSpace = c == 0 || ExpectedIsWhitespace(c);
|
||||
bool expectedPunctuation = c == 0 || ExpectedIsPunctuation(c);
|
||||
|
||||
CharHelper.CheckUnicodeCategory(c, out bool spaceActual, out bool punctuationActual);
|
||||
|
||||
Assert.AreEqual(expectedSpace, spaceActual);
|
||||
Assert.AreEqual(expectedPunctuation, punctuationActual);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsControl()
|
||||
{
|
||||
Test(
|
||||
char.IsControl,
|
||||
CharHelper.IsControl);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsAlpha()
|
||||
{
|
||||
Test(
|
||||
c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'),
|
||||
CharHelper.IsAlpha);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsAlphaUpper()
|
||||
{
|
||||
Test(
|
||||
c => c >= 'A' && c <= 'Z',
|
||||
CharHelper.IsAlphaUpper);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsAlphaNumeric()
|
||||
{
|
||||
Test(
|
||||
c => (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'),
|
||||
CharHelper.IsAlphaNumeric);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsDigit()
|
||||
{
|
||||
Test(
|
||||
c => c >= '0' && c <= '9',
|
||||
CharHelper.IsDigit);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsNewLineOrLineFeed()
|
||||
{
|
||||
Test(
|
||||
c => c is '\r' or '\n',
|
||||
CharHelper.IsNewLineOrLineFeed);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsSpaceOrTab()
|
||||
{
|
||||
Test(
|
||||
c => c is ' ' or '\t',
|
||||
CharHelper.IsSpaceOrTab);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEscapableSymbol()
|
||||
{
|
||||
Test(
|
||||
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~•".Contains,
|
||||
CharHelper.IsEscapableSymbol);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEmailUsernameSpecialChar()
|
||||
{
|
||||
Test(
|
||||
".!#$%&'*+/=?^_`{|}~-+.~".Contains,
|
||||
CharHelper.IsEmailUsernameSpecialChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsEmailUsernameSpecialCharOrDigit()
|
||||
{
|
||||
Test(
|
||||
c => CharHelper.IsDigit(c) || ".!#$%&'*+/=?^_`{|}~-+.~".Contains(c),
|
||||
CharHelper.IsEmailUsernameSpecialCharOrDigit);
|
||||
}
|
||||
|
||||
private static void Test(Func<char, bool> expected, Func<char, bool> actual)
|
||||
{
|
||||
for (int i = char.MinValue; i <= char.MaxValue; i++)
|
||||
{
|
||||
char c = (char)i;
|
||||
|
||||
bool expectedResult = expected(c);
|
||||
bool actualResult = actual(c);
|
||||
|
||||
if (expectedResult != actualResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, actualResult, $"Char: '{c}' ({i})");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,39 @@
|
||||
using NUnit.Framework;
|
||||
namespace Markdig.Tests;
|
||||
|
||||
namespace Markdig.Tests
|
||||
[TestFixture]
|
||||
public class TestConfigureNewLine
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestConfigureNewLine
|
||||
[Test]
|
||||
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "<p><em>1</em>\n<em>2</em></p>\n")]
|
||||
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "<p><em>1</em>\n<em>2</em></p>\n")]
|
||||
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "<p><em>1</em>\r\n<em>2</em></p>\r\n")]
|
||||
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "<p><em>1</em>\r\n<em>2</em></p>\r\n")]
|
||||
[TestCase(/* newLineForWriting: */ "!!!" , /* markdownText: */ "*1*\n*2*\n", /* expected: */ "<p><em>1</em>!!!<em>2</em></p>!!!")]
|
||||
[TestCase(/* newLineForWriting: */ "!!!" , /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "<p><em>1</em>!!!<em>2</em></p>!!!")]
|
||||
public void TestHtmlOutputWhenConfiguringNewLine(string newLineForWriting, string markdownText, string expected)
|
||||
{
|
||||
[Test]
|
||||
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "<p><em>1</em>\n<em>2</em></p>\n")]
|
||||
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "<p><em>1</em>\n<em>2</em></p>\n")]
|
||||
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "<p><em>1</em>\r\n<em>2</em></p>\r\n")]
|
||||
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "<p><em>1</em>\r\n<em>2</em></p>\r\n")]
|
||||
[TestCase(/* newLineForWriting: */ "!!!" , /* markdownText: */ "*1*\n*2*\n", /* expected: */ "<p><em>1</em>!!!<em>2</em></p>!!!")]
|
||||
[TestCase(/* newLineForWriting: */ "!!!" , /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "<p><em>1</em>!!!<em>2</em></p>!!!")]
|
||||
public void TestHtmlOutputWhenConfiguringNewLine(string newLineForWriting, string markdownText, string expected)
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.ConfigureNewLine(newLineForWriting)
|
||||
.Build();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.ConfigureNewLine(newLineForWriting)
|
||||
.Build();
|
||||
|
||||
var actual = Markdown.ToHtml(markdownText, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
var actual = Markdown.ToHtml(markdownText, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "1\n2\n")]
|
||||
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "1\n2\n")]
|
||||
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "1\r\n2\r\n")]
|
||||
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "1\r\n2\r\n")]
|
||||
[TestCase(/* newLineForWriting: */ "!!!", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "1!!!2!!!")]
|
||||
[TestCase(/* newLineForWriting: */ "!!!", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "1!!!2!!!")]
|
||||
public void TestPlainOutputWhenConfiguringNewLine(string newLineForWriting, string markdownText, string expected)
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.ConfigureNewLine(newLineForWriting)
|
||||
.Build();
|
||||
[Test]
|
||||
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "1\n2\n")]
|
||||
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "1\n2\n")]
|
||||
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "1\r\n2\r\n")]
|
||||
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "1\r\n2\r\n")]
|
||||
[TestCase(/* newLineForWriting: */ "!!!", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "1!!!2!!!")]
|
||||
[TestCase(/* newLineForWriting: */ "!!!", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "1!!!2!!!")]
|
||||
public void TestPlainOutputWhenConfiguringNewLine(string newLineForWriting, string markdownText, string expected)
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.ConfigureNewLine(newLineForWriting)
|
||||
.Build();
|
||||
|
||||
var actual = Markdown.ToPlainText(markdownText, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
var actual = Markdown.ToPlainText(markdownText, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
}
|
||||
@@ -1,189 +1,186 @@
|
||||
using System;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestContainerBlocks
|
||||
{
|
||||
public class TestContainerBlocks
|
||||
private class MockContainerBlock : ContainerBlock
|
||||
{
|
||||
private class MockContainerBlock : ContainerBlock
|
||||
public MockContainerBlock()
|
||||
: base(null)
|
||||
{
|
||||
public MockContainerBlock()
|
||||
: base(null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeCleared()
|
||||
{
|
||||
ContainerBlock container = new MockContainerBlock();
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.Null(container.LastChild);
|
||||
|
||||
var paragraph = new ParagraphBlock();
|
||||
Assert.Null(paragraph.Parent);
|
||||
|
||||
container.Add(paragraph);
|
||||
|
||||
Assert.AreEqual(1, container.Count);
|
||||
Assert.AreSame(container, paragraph.Parent);
|
||||
Assert.AreSame(paragraph, container.LastChild);
|
||||
|
||||
container.Clear();
|
||||
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.Null(container.LastChild);
|
||||
Assert.Null(paragraph.Parent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeInsertedInto()
|
||||
{
|
||||
ContainerBlock container = new MockContainerBlock();
|
||||
|
||||
var one = new ParagraphBlock();
|
||||
container.Insert(0, one);
|
||||
Assert.AreEqual(1, container.Count);
|
||||
Assert.AreSame(container[0], one);
|
||||
Assert.AreSame(container, one.Parent);
|
||||
|
||||
var two = new ParagraphBlock();
|
||||
container.Insert(1, two);
|
||||
Assert.AreEqual(2, container.Count);
|
||||
Assert.AreSame(container[0], one);
|
||||
Assert.AreSame(container[1], two);
|
||||
Assert.AreSame(container, two.Parent);
|
||||
|
||||
var three = new ParagraphBlock();
|
||||
container.Insert(0, three);
|
||||
Assert.AreEqual(3, container.Count);
|
||||
Assert.AreSame(container[0], three);
|
||||
Assert.AreSame(container[1], one);
|
||||
Assert.AreSame(container[2], two);
|
||||
Assert.AreSame(container, three.Parent);
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => container.Insert(0, null));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => container.Insert(4, new ParagraphBlock()));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => container.Insert(-1, new ParagraphBlock()));
|
||||
Assert.Throws<ArgumentException>(() => container.Insert(0, one)); // one already has a parent
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeSet()
|
||||
{
|
||||
ContainerBlock container = new MockContainerBlock();
|
||||
|
||||
var one = new ParagraphBlock();
|
||||
container.Insert(0, one);
|
||||
Assert.AreEqual(1, container.Count);
|
||||
Assert.AreSame(container[0], one);
|
||||
Assert.AreSame(container, one.Parent);
|
||||
|
||||
var two = new ParagraphBlock();
|
||||
container[0] = two;
|
||||
Assert.AreSame(container, two.Parent);
|
||||
Assert.Null(one.Parent);
|
||||
|
||||
Assert.Throws<ArgumentException>(() => container[0] = two); // two already has a parent
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Contains()
|
||||
{
|
||||
var container = new MockContainerBlock();
|
||||
var block = new ParagraphBlock();
|
||||
|
||||
Assert.False(container.Contains(block));
|
||||
|
||||
container.Add(block);
|
||||
Assert.True(container.Contains(block));
|
||||
|
||||
container.Add(new ParagraphBlock());
|
||||
Assert.True(container.Contains(block));
|
||||
|
||||
container.Insert(0, new ParagraphBlock());
|
||||
Assert.True(container.Contains(block));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Remove()
|
||||
{
|
||||
var container = new MockContainerBlock();
|
||||
var block = new ParagraphBlock();
|
||||
|
||||
Assert.False(container.Remove(block));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => container.RemoveAt(0));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
|
||||
container.Add(block);
|
||||
Assert.AreEqual(1, container.Count);
|
||||
Assert.True(container.Remove(block));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.False(container.Remove(block));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
|
||||
container.Add(block);
|
||||
Assert.AreEqual(1, container.Count);
|
||||
container.RemoveAt(0);
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => container.RemoveAt(0));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
|
||||
container.Add(new ParagraphBlock { Column = 1 });
|
||||
container.Add(new ParagraphBlock { Column = 2 });
|
||||
container.Add(new ParagraphBlock { Column = 3 });
|
||||
container.Add(new ParagraphBlock { Column = 4 });
|
||||
Assert.AreEqual(4, container.Count);
|
||||
|
||||
container.RemoveAt(2);
|
||||
Assert.AreEqual(3, container.Count);
|
||||
Assert.AreEqual(4, container[2].Column);
|
||||
|
||||
Assert.True(container.Remove(container[1]));
|
||||
Assert.AreEqual(2, container.Count);
|
||||
Assert.AreEqual(1, container[0].Column);
|
||||
Assert.AreEqual(4, container[1].Column);
|
||||
Assert.Throws<IndexOutOfRangeException>(() => _ = container[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyTo()
|
||||
{
|
||||
var container = new MockContainerBlock();
|
||||
|
||||
var destination = new Block[4];
|
||||
container.CopyTo(destination, 0);
|
||||
container.CopyTo(destination, 1);
|
||||
container.CopyTo(destination, -1);
|
||||
container.CopyTo(destination, 5);
|
||||
Assert.Null(destination[0]);
|
||||
|
||||
container.Add(new ParagraphBlock());
|
||||
container.CopyTo(destination, 0);
|
||||
Assert.NotNull(destination[0]);
|
||||
Assert.Null(destination[1]);
|
||||
Assert.Null(destination[2]);
|
||||
Assert.Null(destination[3]);
|
||||
|
||||
container.CopyTo(destination, 2);
|
||||
Assert.NotNull(destination[0]);
|
||||
Assert.Null(destination[1]);
|
||||
Assert.NotNull(destination[2]);
|
||||
Assert.Null(destination[3]);
|
||||
|
||||
Array.Clear(destination);
|
||||
|
||||
container.Add(new ParagraphBlock());
|
||||
container.CopyTo(destination, 1);
|
||||
Assert.Null(destination[0]);
|
||||
Assert.NotNull(destination[1]);
|
||||
Assert.NotNull(destination[2]);
|
||||
Assert.Null(destination[3]);
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() => container.CopyTo(destination, 3));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeCleared()
|
||||
{
|
||||
ContainerBlock container = new MockContainerBlock();
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.Null(container.LastChild);
|
||||
|
||||
var paragraph = new ParagraphBlock();
|
||||
Assert.Null(paragraph.Parent);
|
||||
|
||||
container.Add(paragraph);
|
||||
|
||||
Assert.AreEqual(1, container.Count);
|
||||
Assert.AreSame(container, paragraph.Parent);
|
||||
Assert.AreSame(paragraph, container.LastChild);
|
||||
|
||||
container.Clear();
|
||||
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.Null(container.LastChild);
|
||||
Assert.Null(paragraph.Parent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeInsertedInto()
|
||||
{
|
||||
ContainerBlock container = new MockContainerBlock();
|
||||
|
||||
var one = new ParagraphBlock();
|
||||
container.Insert(0, one);
|
||||
Assert.AreEqual(1, container.Count);
|
||||
Assert.AreSame(container[0], one);
|
||||
Assert.AreSame(container, one.Parent);
|
||||
|
||||
var two = new ParagraphBlock();
|
||||
container.Insert(1, two);
|
||||
Assert.AreEqual(2, container.Count);
|
||||
Assert.AreSame(container[0], one);
|
||||
Assert.AreSame(container[1], two);
|
||||
Assert.AreSame(container, two.Parent);
|
||||
|
||||
var three = new ParagraphBlock();
|
||||
container.Insert(0, three);
|
||||
Assert.AreEqual(3, container.Count);
|
||||
Assert.AreSame(container[0], three);
|
||||
Assert.AreSame(container[1], one);
|
||||
Assert.AreSame(container[2], two);
|
||||
Assert.AreSame(container, three.Parent);
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => container.Insert(0, null));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => container.Insert(4, new ParagraphBlock()));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => container.Insert(-1, new ParagraphBlock()));
|
||||
Assert.Throws<ArgumentException>(() => container.Insert(0, one)); // one already has a parent
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeSet()
|
||||
{
|
||||
ContainerBlock container = new MockContainerBlock();
|
||||
|
||||
var one = new ParagraphBlock();
|
||||
container.Insert(0, one);
|
||||
Assert.AreEqual(1, container.Count);
|
||||
Assert.AreSame(container[0], one);
|
||||
Assert.AreSame(container, one.Parent);
|
||||
|
||||
var two = new ParagraphBlock();
|
||||
container[0] = two;
|
||||
Assert.AreSame(container, two.Parent);
|
||||
Assert.Null(one.Parent);
|
||||
|
||||
Assert.Throws<ArgumentException>(() => container[0] = two); // two already has a parent
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Contains()
|
||||
{
|
||||
var container = new MockContainerBlock();
|
||||
var block = new ParagraphBlock();
|
||||
|
||||
Assert.False(container.Contains(block));
|
||||
|
||||
container.Add(block);
|
||||
Assert.True(container.Contains(block));
|
||||
|
||||
container.Add(new ParagraphBlock());
|
||||
Assert.True(container.Contains(block));
|
||||
|
||||
container.Insert(0, new ParagraphBlock());
|
||||
Assert.True(container.Contains(block));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Remove()
|
||||
{
|
||||
var container = new MockContainerBlock();
|
||||
var block = new ParagraphBlock();
|
||||
|
||||
Assert.False(container.Remove(block));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => container.RemoveAt(0));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
|
||||
container.Add(block);
|
||||
Assert.AreEqual(1, container.Count);
|
||||
Assert.True(container.Remove(block));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.False(container.Remove(block));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
|
||||
container.Add(block);
|
||||
Assert.AreEqual(1, container.Count);
|
||||
container.RemoveAt(0);
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => container.RemoveAt(0));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
|
||||
container.Add(new ParagraphBlock { Column = 1 });
|
||||
container.Add(new ParagraphBlock { Column = 2 });
|
||||
container.Add(new ParagraphBlock { Column = 3 });
|
||||
container.Add(new ParagraphBlock { Column = 4 });
|
||||
Assert.AreEqual(4, container.Count);
|
||||
|
||||
container.RemoveAt(2);
|
||||
Assert.AreEqual(3, container.Count);
|
||||
Assert.AreEqual(4, container[2].Column);
|
||||
|
||||
Assert.True(container.Remove(container[1]));
|
||||
Assert.AreEqual(2, container.Count);
|
||||
Assert.AreEqual(1, container[0].Column);
|
||||
Assert.AreEqual(4, container[1].Column);
|
||||
Assert.Throws<IndexOutOfRangeException>(() => _ = container[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyTo()
|
||||
{
|
||||
var container = new MockContainerBlock();
|
||||
|
||||
var destination = new Block[4];
|
||||
container.CopyTo(destination, 0);
|
||||
container.CopyTo(destination, 1);
|
||||
container.CopyTo(destination, -1);
|
||||
container.CopyTo(destination, 5);
|
||||
Assert.Null(destination[0]);
|
||||
|
||||
container.Add(new ParagraphBlock());
|
||||
container.CopyTo(destination, 0);
|
||||
Assert.NotNull(destination[0]);
|
||||
Assert.Null(destination[1]);
|
||||
Assert.Null(destination[2]);
|
||||
Assert.Null(destination[3]);
|
||||
|
||||
container.CopyTo(destination, 2);
|
||||
Assert.NotNull(destination[0]);
|
||||
Assert.Null(destination[1]);
|
||||
Assert.NotNull(destination[2]);
|
||||
Assert.Null(destination[3]);
|
||||
|
||||
Array.Clear(destination);
|
||||
|
||||
container.Add(new ParagraphBlock());
|
||||
container.CopyTo(destination, 1);
|
||||
Assert.Null(destination[0]);
|
||||
Assert.NotNull(destination[1]);
|
||||
Assert.NotNull(destination[2]);
|
||||
Assert.Null(destination[3]);
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() => container.CopyTo(destination, 3));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,38 @@
|
||||
using System;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestContainerInlines
|
||||
{
|
||||
public class TestContainerInlines
|
||||
private class MockLeafBlock : LeafBlock
|
||||
{
|
||||
private class MockLeafBlock : LeafBlock
|
||||
public MockLeafBlock()
|
||||
: base(null)
|
||||
{
|
||||
public MockLeafBlock()
|
||||
: base(null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeAddedToLeafBlock()
|
||||
{
|
||||
var leafBlock1 = new MockLeafBlock();
|
||||
|
||||
var one = new ContainerInline();
|
||||
Assert.Null(one.ParentBlock);
|
||||
|
||||
leafBlock1.Inline = one;
|
||||
Assert.AreSame(leafBlock1, one.ParentBlock);
|
||||
|
||||
var two = new ContainerInline();
|
||||
Assert.Null(two.ParentBlock);
|
||||
|
||||
leafBlock1.Inline = two;
|
||||
Assert.AreSame(leafBlock1, two.ParentBlock);
|
||||
Assert.Null(one.ParentBlock);
|
||||
|
||||
var leafBlock2 = new MockLeafBlock();
|
||||
Assert.Throws<ArgumentException>(() => leafBlock2.Inline = two);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeAddedToLeafBlock()
|
||||
{
|
||||
var leafBlock1 = new MockLeafBlock();
|
||||
|
||||
var one = new ContainerInline();
|
||||
Assert.Null(one.ParentBlock);
|
||||
|
||||
leafBlock1.Inline = one;
|
||||
Assert.AreSame(leafBlock1, one.ParentBlock);
|
||||
|
||||
var two = new ContainerInline();
|
||||
Assert.Null(two.ParentBlock);
|
||||
|
||||
leafBlock1.Inline = two;
|
||||
Assert.AreSame(leafBlock1, two.ParentBlock);
|
||||
Assert.Null(one.ParentBlock);
|
||||
|
||||
var leafBlock2 = new MockLeafBlock();
|
||||
Assert.Throws<ArgumentException>(() => leafBlock2.Inline = two);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,128 +1,124 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Markdig.Extensions.Emoji;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestCustomEmojis
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCustomEmojis
|
||||
[Test]
|
||||
[TestCase(":smiley:", "<p>♥</p>\n")]
|
||||
[TestCase(":confused:", "<p>:confused:</p>\n")] // default emoji does not work
|
||||
[TestCase(":/", "<p>:/</p>\n")] // default smiley does not work
|
||||
public void TestCustomEmoji(string input, string expected)
|
||||
{
|
||||
[Test]
|
||||
[TestCase(":smiley:", "<p>♥</p>\n")]
|
||||
[TestCase(":confused:", "<p>:confused:</p>\n")] // default emoji does not work
|
||||
[TestCase(":/", "<p>:/</p>\n")] // default smiley does not work
|
||||
public void TestCustomEmoji(string input, string expected)
|
||||
{
|
||||
var emojiToUnicode = new Dictionary<string, string>();
|
||||
var smileyToEmoji = new Dictionary<string, string>();
|
||||
var emojiToUnicode = new Dictionary<string, string>();
|
||||
var smileyToEmoji = new Dictionary<string, string>();
|
||||
|
||||
emojiToUnicode[":smiley:"] = "♥";
|
||||
emojiToUnicode[":smiley:"] = "♥";
|
||||
|
||||
var customMapping = new EmojiMapping(emojiToUnicode, smileyToEmoji);
|
||||
var customMapping = new EmojiMapping(emojiToUnicode, smileyToEmoji);
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmojiAndSmiley(customEmojiMapping: customMapping)
|
||||
.Build();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmojiAndSmiley(customEmojiMapping: customMapping)
|
||||
.Build();
|
||||
|
||||
var actual = Markdown.ToHtml(input, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
var actual = Markdown.ToHtml(input, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(":testheart:", "<p>♥</p>\n")]
|
||||
[TestCase("hello", "<p>♥</p>\n")]
|
||||
[TestCase(":confused:", "<p>:confused:</p>\n")] // default emoji does not work
|
||||
[TestCase(":/", "<p>:/</p>\n")] // default smiley does not work
|
||||
public void TestCustomSmiley(string input, string expected)
|
||||
{
|
||||
var emojiToUnicode = new Dictionary<string, string>();
|
||||
var smileyToEmoji = new Dictionary<string, string>();
|
||||
[Test]
|
||||
[TestCase(":testheart:", "<p>♥</p>\n")]
|
||||
[TestCase("hello", "<p>♥</p>\n")]
|
||||
[TestCase(":confused:", "<p>:confused:</p>\n")] // default emoji does not work
|
||||
[TestCase(":/", "<p>:/</p>\n")] // default smiley does not work
|
||||
public void TestCustomSmiley(string input, string expected)
|
||||
{
|
||||
var emojiToUnicode = new Dictionary<string, string>();
|
||||
var smileyToEmoji = new Dictionary<string, string>();
|
||||
|
||||
emojiToUnicode[":testheart:"] = "♥";
|
||||
smileyToEmoji["hello"] = ":testheart:";
|
||||
emojiToUnicode[":testheart:"] = "♥";
|
||||
smileyToEmoji["hello"] = ":testheart:";
|
||||
|
||||
var customMapping = new EmojiMapping(emojiToUnicode, smileyToEmoji);
|
||||
var customMapping = new EmojiMapping(emojiToUnicode, smileyToEmoji);
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmojiAndSmiley(customEmojiMapping: customMapping)
|
||||
.Build();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmojiAndSmiley(customEmojiMapping: customMapping)
|
||||
.Build();
|
||||
|
||||
var actual = Markdown.ToHtml(input, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
var actual = Markdown.ToHtml(input, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(":smiley:", "<p>♥</p>\n")]
|
||||
[TestCase(":)", "<p>♥</p>\n")]
|
||||
[TestCase(":confused:", "<p>😕</p>\n")] // default emoji still works
|
||||
[TestCase(":/", "<p>😕</p>\n")] // default smiley still works
|
||||
public void TestOverrideDefaultWithCustomEmoji(string input, string expected)
|
||||
{
|
||||
var emojiToUnicode = EmojiMapping.GetDefaultEmojiShortcodeToUnicode();
|
||||
var smileyToEmoji = EmojiMapping.GetDefaultSmileyToEmojiShortcode();
|
||||
[Test]
|
||||
[TestCase(":smiley:", "<p>♥</p>\n")]
|
||||
[TestCase(":)", "<p>♥</p>\n")]
|
||||
[TestCase(":confused:", "<p>😕</p>\n")] // default emoji still works
|
||||
[TestCase(":/", "<p>😕</p>\n")] // default smiley still works
|
||||
public void TestOverrideDefaultWithCustomEmoji(string input, string expected)
|
||||
{
|
||||
var emojiToUnicode = EmojiMapping.GetDefaultEmojiShortcodeToUnicode();
|
||||
var smileyToEmoji = EmojiMapping.GetDefaultSmileyToEmojiShortcode();
|
||||
|
||||
emojiToUnicode[":smiley:"] = "♥";
|
||||
emojiToUnicode[":smiley:"] = "♥";
|
||||
|
||||
var customMapping = new EmojiMapping(emojiToUnicode, smileyToEmoji);
|
||||
var customMapping = new EmojiMapping(emojiToUnicode, smileyToEmoji);
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmojiAndSmiley(customEmojiMapping: customMapping)
|
||||
.Build();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmojiAndSmiley(customEmojiMapping: customMapping)
|
||||
.Build();
|
||||
|
||||
var actual = Markdown.ToHtml(input, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
var actual = Markdown.ToHtml(input, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(":testheart:", "<p>♥</p>\n")]
|
||||
[TestCase("hello", "<p>♥</p>\n")]
|
||||
[TestCase(":confused:", "<p>😕</p>\n")] // default emoji still works
|
||||
[TestCase(":/", "<p>😕</p>\n")] // default smiley still works
|
||||
public void TestOverrideDefaultWithCustomSmiley(string input, string expected)
|
||||
{
|
||||
var emojiToUnicode = EmojiMapping.GetDefaultEmojiShortcodeToUnicode();
|
||||
var smileyToEmoji = EmojiMapping.GetDefaultSmileyToEmojiShortcode();
|
||||
[Test]
|
||||
[TestCase(":testheart:", "<p>♥</p>\n")]
|
||||
[TestCase("hello", "<p>♥</p>\n")]
|
||||
[TestCase(":confused:", "<p>😕</p>\n")] // default emoji still works
|
||||
[TestCase(":/", "<p>😕</p>\n")] // default smiley still works
|
||||
public void TestOverrideDefaultWithCustomSmiley(string input, string expected)
|
||||
{
|
||||
var emojiToUnicode = EmojiMapping.GetDefaultEmojiShortcodeToUnicode();
|
||||
var smileyToEmoji = EmojiMapping.GetDefaultSmileyToEmojiShortcode();
|
||||
|
||||
emojiToUnicode[":testheart:"] = "♥";
|
||||
smileyToEmoji["hello"] = ":testheart:";
|
||||
emojiToUnicode[":testheart:"] = "♥";
|
||||
smileyToEmoji["hello"] = ":testheart:";
|
||||
|
||||
var customMapping = new EmojiMapping(emojiToUnicode, smileyToEmoji);
|
||||
var customMapping = new EmojiMapping(emojiToUnicode, smileyToEmoji);
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmojiAndSmiley(customEmojiMapping: customMapping)
|
||||
.Build();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmojiAndSmiley(customEmojiMapping: customMapping)
|
||||
.Build();
|
||||
|
||||
var actual = Markdown.ToHtml(input, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
var actual = Markdown.ToHtml(input, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCustomEmojiValidation()
|
||||
{
|
||||
var emojiToUnicode = new Dictionary<string, string>();
|
||||
var smileyToEmoji = new Dictionary<string, string>();
|
||||
[Test]
|
||||
public void TestCustomEmojiValidation()
|
||||
{
|
||||
var emojiToUnicode = new Dictionary<string, string>();
|
||||
var smileyToEmoji = new Dictionary<string, string>();
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => new EmojiMapping(null, smileyToEmoji));
|
||||
Assert.Throws<ArgumentNullException>(() => new EmojiMapping(emojiToUnicode, null));
|
||||
Assert.Throws<ArgumentNullException>(() => new EmojiMapping(null, smileyToEmoji));
|
||||
Assert.Throws<ArgumentNullException>(() => new EmojiMapping(emojiToUnicode, null));
|
||||
|
||||
emojiToUnicode.Add("null-value", null);
|
||||
Assert.Throws<ArgumentException>(() => new EmojiMapping(emojiToUnicode, smileyToEmoji));
|
||||
emojiToUnicode.Clear();
|
||||
emojiToUnicode.Add("null-value", null);
|
||||
Assert.Throws<ArgumentException>(() => new EmojiMapping(emojiToUnicode, smileyToEmoji));
|
||||
emojiToUnicode.Clear();
|
||||
|
||||
smileyToEmoji.Add("null-value", null);
|
||||
Assert.Throws<ArgumentException>(() => new EmojiMapping(emojiToUnicode, smileyToEmoji));
|
||||
smileyToEmoji.Clear();
|
||||
smileyToEmoji.Add("null-value", null);
|
||||
Assert.Throws<ArgumentException>(() => new EmojiMapping(emojiToUnicode, smileyToEmoji));
|
||||
smileyToEmoji.Clear();
|
||||
|
||||
smileyToEmoji.Add("foo", "something-that-does-not-exist-in-emojiToUnicode");
|
||||
Assert.Throws<ArgumentException>(() => new EmojiMapping(emojiToUnicode, smileyToEmoji));
|
||||
smileyToEmoji.Clear();
|
||||
smileyToEmoji.Add("foo", "something-that-does-not-exist-in-emojiToUnicode");
|
||||
Assert.Throws<ArgumentException>(() => new EmojiMapping(emojiToUnicode, smileyToEmoji));
|
||||
smileyToEmoji.Clear();
|
||||
|
||||
emojiToUnicode.Add("a", "aaa");
|
||||
emojiToUnicode.Add("b", "bbb");
|
||||
emojiToUnicode.Add("c", "ccc");
|
||||
smileyToEmoji.Add("a", "c"); // "a" already exists in emojiToUnicode
|
||||
Assert.Throws<ArgumentException>(() => new EmojiMapping(emojiToUnicode, smileyToEmoji));
|
||||
}
|
||||
emojiToUnicode.Add("a", "aaa");
|
||||
emojiToUnicode.Add("b", "bbb");
|
||||
emojiToUnicode.Add("c", "ccc");
|
||||
smileyToEmoji.Add("a", "c"); // "a" already exists in emojiToUnicode
|
||||
Assert.Throws<ArgumentException>(() => new EmojiMapping(emojiToUnicode, smileyToEmoji));
|
||||
}
|
||||
}
|
||||
@@ -1,150 +1,145 @@
|
||||
using NUnit.Framework;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestDescendantsOrder
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestDescendantsOrder
|
||||
public static void TestSchemas(MarkdownDocument[] specsSyntaxTrees)
|
||||
{
|
||||
public static void TestSchemas(MarkdownDocument[] specsSyntaxTrees)
|
||||
foreach (var syntaxTree in specsSyntaxTrees)
|
||||
{
|
||||
foreach (var syntaxTree in specsSyntaxTrees)
|
||||
AssertIEnumerablesAreEqual(
|
||||
Descendants_Legacy(syntaxTree),
|
||||
syntaxTree.Descendants());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
syntaxTree.Descendants().OfType<ParagraphBlock>(),
|
||||
syntaxTree.Descendants<ParagraphBlock>());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
syntaxTree.Descendants().OfType<ParagraphBlock>(),
|
||||
(syntaxTree as ContainerBlock).Descendants<ParagraphBlock>());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
syntaxTree.Descendants().OfType<LiteralInline>(),
|
||||
syntaxTree.Descendants<LiteralInline>());
|
||||
|
||||
foreach (LiteralInline literalInline in syntaxTree.Descendants<LiteralInline>())
|
||||
{
|
||||
Assert.AreSame(Array.Empty<ListBlock>(), literalInline.Descendants<ListBlock>());
|
||||
Assert.AreSame(Array.Empty<ParagraphBlock>(), literalInline.Descendants<ParagraphBlock>());
|
||||
Assert.AreSame(Array.Empty<ContainerInline>(), literalInline.Descendants<ContainerInline>());
|
||||
}
|
||||
|
||||
foreach (ContainerInline containerInline in syntaxTree.Descendants<ContainerInline>())
|
||||
{
|
||||
AssertIEnumerablesAreEqual(
|
||||
Descendants_Legacy(syntaxTree),
|
||||
syntaxTree.Descendants());
|
||||
containerInline.FindDescendants<LiteralInline>(),
|
||||
containerInline.Descendants<LiteralInline>());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
syntaxTree.Descendants().OfType<ParagraphBlock>(),
|
||||
syntaxTree.Descendants<ParagraphBlock>());
|
||||
containerInline.FindDescendants<LiteralInline>(),
|
||||
(containerInline as MarkdownObject).Descendants<LiteralInline>());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
syntaxTree.Descendants().OfType<ParagraphBlock>(),
|
||||
(syntaxTree as ContainerBlock).Descendants<ParagraphBlock>());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
syntaxTree.Descendants().OfType<LiteralInline>(),
|
||||
syntaxTree.Descendants<LiteralInline>());
|
||||
|
||||
foreach (LiteralInline literalInline in syntaxTree.Descendants<LiteralInline>())
|
||||
if (containerInline.FirstChild is null)
|
||||
{
|
||||
Assert.AreSame(ArrayHelper.Empty<ListBlock>(), literalInline.Descendants<ListBlock>());
|
||||
Assert.AreSame(ArrayHelper.Empty<ParagraphBlock>(), literalInline.Descendants<ParagraphBlock>());
|
||||
Assert.AreSame(ArrayHelper.Empty<ContainerInline>(), literalInline.Descendants<ContainerInline>());
|
||||
Assert.AreSame(Array.Empty<LiteralInline>(), containerInline.Descendants<LiteralInline>());
|
||||
Assert.AreSame(Array.Empty<LiteralInline>(), containerInline.FindDescendants<LiteralInline>());
|
||||
Assert.AreSame(Array.Empty<LiteralInline>(), (containerInline as MarkdownObject).Descendants<LiteralInline>());
|
||||
}
|
||||
|
||||
foreach (ContainerInline containerInline in syntaxTree.Descendants<ContainerInline>())
|
||||
Assert.AreSame(Array.Empty<ListBlock>(), containerInline.Descendants<ListBlock>());
|
||||
Assert.AreSame(Array.Empty<ParagraphBlock>(), containerInline.Descendants<ParagraphBlock>());
|
||||
}
|
||||
|
||||
foreach (ParagraphBlock paragraphBlock in syntaxTree.Descendants<ParagraphBlock>())
|
||||
{
|
||||
AssertIEnumerablesAreEqual(
|
||||
(paragraphBlock as MarkdownObject).Descendants<LiteralInline>(),
|
||||
paragraphBlock.Descendants<LiteralInline>());
|
||||
|
||||
Assert.AreSame(Array.Empty<ParagraphBlock>(), paragraphBlock.Descendants<ParagraphBlock>());
|
||||
}
|
||||
|
||||
foreach (ContainerBlock containerBlock in syntaxTree.Descendants<ContainerBlock>())
|
||||
{
|
||||
AssertIEnumerablesAreEqual(
|
||||
containerBlock.Descendants<LiteralInline>(),
|
||||
(containerBlock as MarkdownObject).Descendants<LiteralInline>());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
containerBlock.Descendants<ParagraphBlock>(),
|
||||
(containerBlock as MarkdownObject).Descendants<ParagraphBlock>());
|
||||
|
||||
if (containerBlock.Count == 0)
|
||||
{
|
||||
AssertIEnumerablesAreEqual(
|
||||
containerInline.FindDescendants<LiteralInline>(),
|
||||
containerInline.Descendants<LiteralInline>());
|
||||
Assert.AreSame(Array.Empty<LiteralInline>(), containerBlock.Descendants<LiteralInline>());
|
||||
Assert.AreSame(Array.Empty<LiteralInline>(), (containerBlock as Block).Descendants<LiteralInline>());
|
||||
Assert.AreSame(Array.Empty<LiteralInline>(), (containerBlock as MarkdownObject).Descendants<LiteralInline>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
containerInline.FindDescendants<LiteralInline>(),
|
||||
(containerInline as MarkdownObject).Descendants<LiteralInline>());
|
||||
private static void AssertIEnumerablesAreEqual<T>(IEnumerable<T> first, IEnumerable<T> second)
|
||||
{
|
||||
var firstList = new List<T>(first);
|
||||
var secondList = new List<T>(second);
|
||||
|
||||
if (containerInline.FirstChild is null)
|
||||
Assert.AreEqual(firstList.Count, secondList.Count);
|
||||
|
||||
for (int i = 0; i < firstList.Count; i++)
|
||||
{
|
||||
Assert.AreSame(firstList[i], secondList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<MarkdownObject> Descendants_Legacy(MarkdownObject markdownObject)
|
||||
{
|
||||
// TODO: implement a recursiveless method
|
||||
|
||||
var block = markdownObject as ContainerBlock;
|
||||
if (block != null)
|
||||
{
|
||||
foreach (var subBlock in block)
|
||||
{
|
||||
yield return subBlock;
|
||||
|
||||
foreach (var sub in Descendants_Legacy(subBlock))
|
||||
{
|
||||
yield return sub;
|
||||
}
|
||||
|
||||
// Visit leaf block that have inlines
|
||||
var leafBlock = subBlock as LeafBlock;
|
||||
if (leafBlock?.Inline != null)
|
||||
{
|
||||
foreach (var subInline in Descendants_Legacy(leafBlock.Inline))
|
||||
{
|
||||
Assert.AreSame(ArrayHelper.Empty<LiteralInline>(), containerInline.Descendants<LiteralInline>());
|
||||
Assert.AreSame(ArrayHelper.Empty<LiteralInline>(), containerInline.FindDescendants<LiteralInline>());
|
||||
Assert.AreSame(ArrayHelper.Empty<LiteralInline>(), (containerInline as MarkdownObject).Descendants<LiteralInline>());
|
||||
}
|
||||
|
||||
Assert.AreSame(ArrayHelper.Empty<ListBlock>(), containerInline.Descendants<ListBlock>());
|
||||
Assert.AreSame(ArrayHelper.Empty<ParagraphBlock>(), containerInline.Descendants<ParagraphBlock>());
|
||||
}
|
||||
|
||||
foreach (ParagraphBlock paragraphBlock in syntaxTree.Descendants<ParagraphBlock>())
|
||||
{
|
||||
AssertIEnumerablesAreEqual(
|
||||
(paragraphBlock as MarkdownObject).Descendants<LiteralInline>(),
|
||||
paragraphBlock.Descendants<LiteralInline>());
|
||||
|
||||
Assert.AreSame(ArrayHelper.Empty<ParagraphBlock>(), paragraphBlock.Descendants<ParagraphBlock>());
|
||||
}
|
||||
|
||||
foreach (ContainerBlock containerBlock in syntaxTree.Descendants<ContainerBlock>())
|
||||
{
|
||||
AssertIEnumerablesAreEqual(
|
||||
containerBlock.Descendants<LiteralInline>(),
|
||||
(containerBlock as MarkdownObject).Descendants<LiteralInline>());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
containerBlock.Descendants<ParagraphBlock>(),
|
||||
(containerBlock as MarkdownObject).Descendants<ParagraphBlock>());
|
||||
|
||||
if (containerBlock.Count == 0)
|
||||
{
|
||||
Assert.AreSame(ArrayHelper.Empty<LiteralInline>(), containerBlock.Descendants<LiteralInline>());
|
||||
Assert.AreSame(ArrayHelper.Empty<LiteralInline>(), (containerBlock as Block).Descendants<LiteralInline>());
|
||||
Assert.AreSame(ArrayHelper.Empty<LiteralInline>(), (containerBlock as MarkdownObject).Descendants<LiteralInline>());
|
||||
yield return subInline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertIEnumerablesAreEqual<T>(IEnumerable<T> first, IEnumerable<T> second)
|
||||
else
|
||||
{
|
||||
var firstList = new List<T>(first);
|
||||
var secondList = new List<T>(second);
|
||||
|
||||
Assert.AreEqual(firstList.Count, secondList.Count);
|
||||
|
||||
for (int i = 0; i < firstList.Count; i++)
|
||||
var inline = markdownObject as ContainerInline;
|
||||
if (inline != null)
|
||||
{
|
||||
Assert.AreSame(firstList[i], secondList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<MarkdownObject> Descendants_Legacy(MarkdownObject markdownObject)
|
||||
{
|
||||
// TODO: implement a recursiveless method
|
||||
|
||||
var block = markdownObject as ContainerBlock;
|
||||
if (block != null)
|
||||
{
|
||||
foreach (var subBlock in block)
|
||||
var child = inline.FirstChild;
|
||||
while (child != null)
|
||||
{
|
||||
yield return subBlock;
|
||||
var next = child.NextSibling;
|
||||
yield return child;
|
||||
|
||||
foreach (var sub in Descendants_Legacy(subBlock))
|
||||
foreach (var sub in Descendants_Legacy(child))
|
||||
{
|
||||
yield return sub;
|
||||
}
|
||||
|
||||
// Visit leaf block that have inlines
|
||||
var leafBlock = subBlock as LeafBlock;
|
||||
if (leafBlock?.Inline != null)
|
||||
{
|
||||
foreach (var subInline in Descendants_Legacy(leafBlock.Inline))
|
||||
{
|
||||
yield return subInline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var inline = markdownObject as ContainerInline;
|
||||
if (inline != null)
|
||||
{
|
||||
var child = inline.FirstChild;
|
||||
while (child != null)
|
||||
{
|
||||
var next = child.NextSibling;
|
||||
yield return child;
|
||||
|
||||
foreach (var sub in Descendants_Legacy(child))
|
||||
{
|
||||
yield return sub;
|
||||
}
|
||||
|
||||
child = next;
|
||||
}
|
||||
child = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,169 +1,166 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
using Markdig.Parsers.Inlines;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestEmphasisExtended
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestEmphasisExtended
|
||||
class EmphasisTestExtension : IMarkdownExtension
|
||||
{
|
||||
class EmphasisTestExtension : IMarkdownExtension
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
var emphasisParser = pipeline.InlineParsers.Find<EmphasisInlineParser>();
|
||||
Debug.Assert(emphasisParser != null);
|
||||
|
||||
foreach (var emphasis in EmphasisTestDescriptors)
|
||||
{
|
||||
var emphasisParser = pipeline.InlineParsers.Find<EmphasisInlineParser>();
|
||||
Debug.Assert(emphasisParser != null);
|
||||
|
||||
foreach (var emphasis in EmphasisTestDescriptors)
|
||||
{
|
||||
emphasisParser.EmphasisDescriptors.Add(
|
||||
new EmphasisDescriptor(emphasis.Character, emphasis.Minimum, emphasis.Maximum, true));
|
||||
}
|
||||
emphasisParser.TryCreateEmphasisInlineList.Add((delimiterChar, delimiterCount) =>
|
||||
{
|
||||
return delimiterChar == '*' || delimiterChar == '_'
|
||||
? null
|
||||
: new CustomEmphasisInline() { DelimiterChar = delimiterChar, DelimiterCount = delimiterCount };
|
||||
});
|
||||
emphasisParser.EmphasisDescriptors.Add(
|
||||
new EmphasisDescriptor(emphasis.Character, emphasis.Minimum, emphasis.Maximum, true));
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
emphasisParser.TryCreateEmphasisInlineList.Add((delimiterChar, delimiterCount) =>
|
||||
{
|
||||
renderer.ObjectRenderers.Insert(0, new EmphasisRenderer());
|
||||
}
|
||||
|
||||
class EmphasisRenderer : HtmlObjectRenderer<CustomEmphasisInline>
|
||||
{
|
||||
protected override void Write(HtmlRenderer renderer, CustomEmphasisInline obj)
|
||||
{
|
||||
var tag = EmphasisTestDescriptors.First(test => test.Character == obj.DelimiterChar).Tags[obj.DelimiterCount];
|
||||
|
||||
renderer.Write(tag.OpeningTag);
|
||||
renderer.WriteChildren(obj);
|
||||
renderer.Write(tag.ClosingTag);
|
||||
}
|
||||
}
|
||||
return delimiterChar is '*' or '_'
|
||||
? null
|
||||
: new CustomEmphasisInline() { DelimiterChar = delimiterChar, DelimiterCount = delimiterCount };
|
||||
});
|
||||
}
|
||||
class Tag
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public int Level;
|
||||
#pragma warning restore CS0649
|
||||
public string RawTag;
|
||||
public string OpeningTag;
|
||||
public string ClosingTag;
|
||||
|
||||
public Tag(string tag)
|
||||
{
|
||||
RawTag = tag;
|
||||
OpeningTag = "<" + tag + ">";
|
||||
ClosingTag = "</" + tag + ">";
|
||||
}
|
||||
|
||||
public static implicit operator Tag(string tag)
|
||||
=> new Tag(tag);
|
||||
renderer.ObjectRenderers.Insert(0, new EmphasisRenderer());
|
||||
}
|
||||
class EmphasisTestDescriptor
|
||||
{
|
||||
public char Character;
|
||||
public int Minimum;
|
||||
public int Maximum;
|
||||
public Dictionary<int, Tag> Tags = new Dictionary<int, Tag>();
|
||||
|
||||
private EmphasisTestDescriptor(char character, int min, int max)
|
||||
class EmphasisRenderer : HtmlObjectRenderer<CustomEmphasisInline>
|
||||
{
|
||||
protected override void Write(HtmlRenderer renderer, CustomEmphasisInline obj)
|
||||
{
|
||||
Character = character;
|
||||
Minimum = min;
|
||||
Maximum = max;
|
||||
}
|
||||
public EmphasisTestDescriptor(char character, int min, int max, params Tag[] tags)
|
||||
: this(character, min, max)
|
||||
{
|
||||
Debug.Assert(tags.Length == max - min + 1);
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
Tags.Add(min++, tag);
|
||||
}
|
||||
}
|
||||
public EmphasisTestDescriptor(char character, int min, int max, string tag)
|
||||
: this(character, min, max, new Tag(tag)) { }
|
||||
}
|
||||
class CustomEmphasisInline : EmphasisInline { }
|
||||
static readonly EmphasisTestDescriptor[] EmphasisTestDescriptors = new[]
|
||||
{
|
||||
// Min Max
|
||||
new EmphasisTestDescriptor('"', 1, 1, "quotation"),
|
||||
new EmphasisTestDescriptor(',', 1, 2, "comma", "extra-comma"),
|
||||
new EmphasisTestDescriptor('!', 2, 3, "warning", "error"),
|
||||
new EmphasisTestDescriptor('=', 1, 3, "equal", "really-equal", "congruent"),
|
||||
new EmphasisTestDescriptor('1', 1, 1, "one-only"),
|
||||
new EmphasisTestDescriptor('2', 2, 2, "two-only"),
|
||||
new EmphasisTestDescriptor('3', 3, 3, "three-only"),
|
||||
};
|
||||
var tag = EmphasisTestDescriptors.First(test => test.Character == obj.DelimiterChar).Tags[obj.DelimiterCount];
|
||||
|
||||
static readonly MarkdownPipeline Pipeline = new MarkdownPipelineBuilder().Use<EmphasisTestExtension>().Build();
|
||||
|
||||
[Test]
|
||||
[TestCase("*foo**", "<em>foo</em>*")]
|
||||
[TestCase("**foo*", "*<em>foo</em>")]
|
||||
[TestCase("***foo***", "<em><strong>foo</strong></em>")]
|
||||
[TestCase("**_foo_**", "<strong><em>foo</em></strong>")]
|
||||
[TestCase("_**foo**_", "<em><strong>foo</strong></em>")]
|
||||
[TestCase("\"foo\"", "<quotation>foo</quotation>")]
|
||||
[TestCase("\"\"foo\"\"", "<quotation><quotation>foo</quotation></quotation>")]
|
||||
[TestCase("\"foo\"\"", "<quotation>foo</quotation>"")]
|
||||
[TestCase("\"\"foo\"", ""<quotation>foo</quotation>")]
|
||||
[TestCase(", foo", ", foo")]
|
||||
[TestCase(", foo,", ", foo,")]
|
||||
[TestCase(",some, foo,", "<comma>some</comma> foo,")]
|
||||
[TestCase(",,foo,,", "<extra-comma>foo</extra-comma>")]
|
||||
[TestCase(",foo,,", "<comma>foo</comma>,")]
|
||||
[TestCase(",,,foo,,,", "<comma><extra-comma>foo</extra-comma></comma>")]
|
||||
[TestCase("*foo*&_foo_", "<em>foo</em>&<em>foo</em>")]
|
||||
[TestCase("!1!", "!1!")]
|
||||
[TestCase("!!2!!", "<warning>2</warning>")]
|
||||
[TestCase("!!!3!!!", "<error>3</error>")]
|
||||
[TestCase("!!!34!!!!", "<error>34</error>!")]
|
||||
[TestCase("!!!!43!!!", "!<error>43</error>")]
|
||||
[TestCase("!!!!44!!!!", "!<error>44!</error>")] // This is a new case - should the second ! be before or after </error>?
|
||||
[TestCase("!!!!!5!!!!!", "<warning><error>5</error></warning>")]
|
||||
[TestCase("!!!!!!6!!!!!!", "<error><error>6</error></error>")]
|
||||
[TestCase("!! !mixed!!!", "!! !mixed!!!")] // can't open the delimiter because of the whitespace
|
||||
[TestCase("=", "=")]
|
||||
[TestCase("==", "==")]
|
||||
[TestCase("====", "====")]
|
||||
[TestCase("=a", "=a")]
|
||||
[TestCase("=a=", "<equal>a</equal>")]
|
||||
[TestCase("==a=", "=<equal>a</equal>")]
|
||||
[TestCase("==a==", "<really-equal>a</really-equal>")]
|
||||
[TestCase("==a===", "<really-equal>a</really-equal>=")]
|
||||
[TestCase("===a===", "<congruent>a</congruent>")]
|
||||
[TestCase("====a====", "<equal><congruent>a</congruent></equal>")]
|
||||
[TestCase("=====a=====", "<really-equal><congruent>a</congruent></really-equal>")]
|
||||
[TestCase("1", "1")]
|
||||
[TestCase("1 1", "1 1")]
|
||||
[TestCase("1Foo1", "<one-only>Foo</one-only>")]
|
||||
[TestCase("1121", "1<one-only>2</one-only>")]
|
||||
[TestCase("22322", "<two-only>3</two-only>")]
|
||||
[TestCase("2232", "2232")]
|
||||
[TestCase("333", "333")]
|
||||
[TestCase("3334333", "<three-only>4</three-only>")]
|
||||
[TestCase("33334333", "3<three-only>4</three-only>")]
|
||||
[TestCase("33343333", "<three-only>4</three-only>3")]
|
||||
[TestCase("122122", "<one-only>22</one-only>22")]
|
||||
[TestCase("221221", "<two-only>1</two-only>1")]
|
||||
[TestCase("122foo221", "<one-only><two-only>foo</two-only></one-only>")]
|
||||
[TestCase("122foo122", "<one-only>22foo</one-only>22")]
|
||||
[TestCase("!!!!!Attention:!! \"==1+1== 2\",but ===333 and 222===, mod 111!!!",
|
||||
"<error><warning>Attention:</warning> <quotation><really-equal><one-only>+</one-only></really-equal> 2</quotation><comma>but <congruent>333 and 222</congruent></comma> mod 111</error>")]
|
||||
public void TestEmphasis(string markdown, string expectedHtml)
|
||||
{
|
||||
TestParser.TestSpec(markdown, "<p>" + expectedHtml + "</p>", Pipeline);
|
||||
renderer.Write(tag.OpeningTag);
|
||||
renderer.WriteChildren(obj);
|
||||
renderer.Write(tag.ClosingTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
class Tag
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public int Level;
|
||||
#pragma warning restore CS0649
|
||||
public string RawTag;
|
||||
public string OpeningTag;
|
||||
public string ClosingTag;
|
||||
|
||||
public Tag(string tag)
|
||||
{
|
||||
RawTag = tag;
|
||||
OpeningTag = "<" + tag + ">";
|
||||
ClosingTag = "</" + tag + ">";
|
||||
}
|
||||
|
||||
public static implicit operator Tag(string tag)
|
||||
=> new Tag(tag);
|
||||
}
|
||||
class EmphasisTestDescriptor
|
||||
{
|
||||
public char Character;
|
||||
public int Minimum;
|
||||
public int Maximum;
|
||||
public Dictionary<int, Tag> Tags = new Dictionary<int, Tag>();
|
||||
|
||||
private EmphasisTestDescriptor(char character, int min, int max)
|
||||
{
|
||||
Character = character;
|
||||
Minimum = min;
|
||||
Maximum = max;
|
||||
}
|
||||
public EmphasisTestDescriptor(char character, int min, int max, params Tag[] tags)
|
||||
: this(character, min, max)
|
||||
{
|
||||
Debug.Assert(tags.Length == max - min + 1);
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
Tags.Add(min++, tag);
|
||||
}
|
||||
}
|
||||
public EmphasisTestDescriptor(char character, int min, int max, string tag)
|
||||
: this(character, min, max, new Tag(tag)) { }
|
||||
}
|
||||
class CustomEmphasisInline : EmphasisInline { }
|
||||
static readonly EmphasisTestDescriptor[] EmphasisTestDescriptors = new[]
|
||||
{
|
||||
// Min Max
|
||||
new EmphasisTestDescriptor('"', 1, 1, "quotation"),
|
||||
new EmphasisTestDescriptor(',', 1, 2, "comma", "extra-comma"),
|
||||
new EmphasisTestDescriptor('!', 2, 3, "warning", "error"),
|
||||
new EmphasisTestDescriptor('=', 1, 3, "equal", "really-equal", "congruent"),
|
||||
new EmphasisTestDescriptor('1', 1, 1, "one-only"),
|
||||
new EmphasisTestDescriptor('2', 2, 2, "two-only"),
|
||||
new EmphasisTestDescriptor('3', 3, 3, "three-only"),
|
||||
};
|
||||
|
||||
static readonly MarkdownPipeline Pipeline = new MarkdownPipelineBuilder().Use<EmphasisTestExtension>().Build();
|
||||
|
||||
[Test]
|
||||
[TestCase("*foo**", "<em>foo</em>*")]
|
||||
[TestCase("**foo*", "*<em>foo</em>")]
|
||||
[TestCase("***foo***", "<em><strong>foo</strong></em>")]
|
||||
[TestCase("**_foo_**", "<strong><em>foo</em></strong>")]
|
||||
[TestCase("_**foo**_", "<em><strong>foo</strong></em>")]
|
||||
[TestCase("\"foo\"", "<quotation>foo</quotation>")]
|
||||
[TestCase("\"\"foo\"\"", "<quotation><quotation>foo</quotation></quotation>")]
|
||||
[TestCase("\"foo\"\"", "<quotation>foo</quotation>"")]
|
||||
[TestCase("\"\"foo\"", ""<quotation>foo</quotation>")]
|
||||
[TestCase(", foo", ", foo")]
|
||||
[TestCase(", foo,", ", foo,")]
|
||||
[TestCase(",some, foo,", "<comma>some</comma> foo,")]
|
||||
[TestCase(",,foo,,", "<extra-comma>foo</extra-comma>")]
|
||||
[TestCase(",foo,,", "<comma>foo</comma>,")]
|
||||
[TestCase(",,,foo,,,", "<comma><extra-comma>foo</extra-comma></comma>")]
|
||||
[TestCase("*foo*&_foo_", "<em>foo</em>&<em>foo</em>")]
|
||||
[TestCase("!1!", "!1!")]
|
||||
[TestCase("!!2!!", "<warning>2</warning>")]
|
||||
[TestCase("!!!3!!!", "<error>3</error>")]
|
||||
[TestCase("!!!34!!!!", "<error>34</error>!")]
|
||||
[TestCase("!!!!43!!!", "!<error>43</error>")]
|
||||
[TestCase("!!!!44!!!!", "!<error>44!</error>")] // This is a new case - should the second ! be before or after </error>?
|
||||
[TestCase("!!!!!5!!!!!", "<warning><error>5</error></warning>")]
|
||||
[TestCase("!!!!!!6!!!!!!", "<error><error>6</error></error>")]
|
||||
[TestCase("!! !mixed!!!", "!! !mixed!!!")] // can't open the delimiter because of the whitespace
|
||||
[TestCase("=", "=")]
|
||||
[TestCase("==", "==")]
|
||||
[TestCase("====", "====")]
|
||||
[TestCase("=a", "=a")]
|
||||
[TestCase("=a=", "<equal>a</equal>")]
|
||||
[TestCase("==a=", "=<equal>a</equal>")]
|
||||
[TestCase("==a==", "<really-equal>a</really-equal>")]
|
||||
[TestCase("==a===", "<really-equal>a</really-equal>=")]
|
||||
[TestCase("===a===", "<congruent>a</congruent>")]
|
||||
[TestCase("====a====", "<equal><congruent>a</congruent></equal>")]
|
||||
[TestCase("=====a=====", "<really-equal><congruent>a</congruent></really-equal>")]
|
||||
[TestCase("1", "1")]
|
||||
[TestCase("1 1", "1 1")]
|
||||
[TestCase("1Foo1", "<one-only>Foo</one-only>")]
|
||||
[TestCase("1121", "1<one-only>2</one-only>")]
|
||||
[TestCase("22322", "<two-only>3</two-only>")]
|
||||
[TestCase("2232", "2232")]
|
||||
[TestCase("333", "333")]
|
||||
[TestCase("3334333", "<three-only>4</three-only>")]
|
||||
[TestCase("33334333", "3<three-only>4</three-only>")]
|
||||
[TestCase("33343333", "<three-only>4</three-only>3")]
|
||||
[TestCase("122122", "<one-only>22</one-only>22")]
|
||||
[TestCase("221221", "<two-only>1</two-only>1")]
|
||||
[TestCase("122foo221", "<one-only><two-only>foo</two-only></one-only>")]
|
||||
[TestCase("122foo122", "<one-only>22foo</one-only>22")]
|
||||
[TestCase("!!!!!Attention:!! \"==1+1== 2\",but ===333 and 222===, mod 111!!!",
|
||||
"<error><warning>Attention:</warning> <quotation><really-equal><one-only>+</one-only></really-equal> 2</quotation><comma>but <congruent>333 and 222</congruent></comma> mod 111</error>")]
|
||||
public void TestEmphasis(string markdown, string expectedHtml)
|
||||
{
|
||||
TestParser.TestSpec(markdown, "<p>" + expectedHtml + "</p>", Pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,43 @@
|
||||
using NUnit.Framework;
|
||||
using Markdig.Extensions.EmphasisExtras;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestEmphasisExtraOptions
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestEmphasisExtraOptions
|
||||
[Test]
|
||||
public void OnlyStrikethrough_Single()
|
||||
{
|
||||
[Test]
|
||||
public void OnlyStrikethrough_Single()
|
||||
{
|
||||
TestParser.TestSpec("~foo~", "<p>~foo~</p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough).Build());
|
||||
}
|
||||
TestParser.TestSpec("~foo~", "<p>~foo~</p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnlyStrikethrough_Double()
|
||||
{
|
||||
TestParser.TestSpec("~~foo~~", "<p><del>foo</del></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough).Build());
|
||||
}
|
||||
[Test]
|
||||
public void OnlyStrikethrough_Double()
|
||||
{
|
||||
TestParser.TestSpec("~~foo~~", "<p><del>foo</del></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnlySubscript_Single()
|
||||
{
|
||||
TestParser.TestSpec("~foo~", "<p><sub>foo</sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
[Test]
|
||||
public void OnlySubscript_Single()
|
||||
{
|
||||
TestParser.TestSpec("~foo~", "<p><sub>foo</sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnlySubscript_Double()
|
||||
{
|
||||
TestParser.TestSpec("~~foo~~", "<p><sub><sub>foo</sub></sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
[Test]
|
||||
public void OnlySubscript_Double()
|
||||
{
|
||||
TestParser.TestSpec("~~foo~~", "<p><sub><sub>foo</sub></sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SubscriptAndStrikethrough_Single()
|
||||
{
|
||||
TestParser.TestSpec("~foo~", "<p><sub>foo</sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough | EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
[Test]
|
||||
public void SubscriptAndStrikethrough_Single()
|
||||
{
|
||||
TestParser.TestSpec("~foo~", "<p><sub>foo</sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough | EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SubscriptAndStrikethrough_Double()
|
||||
{
|
||||
TestParser.TestSpec("~~foo~~", "<p><del>foo</del></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough | EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
[Test]
|
||||
public void SubscriptAndStrikethrough_Double()
|
||||
{
|
||||
TestParser.TestSpec("~~foo~~", "<p><del>foo</del></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough | EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,37 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public partial class TestEmphasisPlus
|
||||
{
|
||||
[TestFixture]
|
||||
public partial class TestEmphasisPlus
|
||||
[Test]
|
||||
public void StrongNormal()
|
||||
{
|
||||
[Test]
|
||||
public void StrongNormal()
|
||||
{
|
||||
TestParser.TestSpec("***Strong emphasis*** normal", "<p><em><strong>Strong emphasis</strong></em> 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 <em><strong>Strong emphasis</strong></em> normal</p>", "");
|
||||
}
|
||||
[Test]
|
||||
public void NormalStrongNormal()
|
||||
{
|
||||
TestParser.TestSpec("normal ***Strong emphasis*** normal", "<p>normal <em><strong>Strong emphasis</strong></em> normal</p>", "");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OpenEmphasisHasConvenientContentStringSlice()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().Build();
|
||||
|
||||
var document = Markdown.Parse("test*test", pipeline);
|
||||
|
||||
var emphasisDelimiterLiteral = (LiteralInline)((ParagraphBlock)document.LastChild).Inline.ElementAt(1);
|
||||
Assert.That(emphasisDelimiterLiteral.Content.Text == "test*test");
|
||||
Assert.That(emphasisDelimiterLiteral.Content.Start == 4);
|
||||
Assert.That(emphasisDelimiterLiteral.Content.End == 4);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,45 @@
|
||||
using NUnit.Framework;
|
||||
namespace Markdig.Tests;
|
||||
|
||||
namespace Markdig.Tests
|
||||
[TestFixture]
|
||||
public class TestExceptionNotThrown
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestExceptionNotThrown
|
||||
[Test]
|
||||
public void DoesNotThrowIndexOutOfRangeException1()
|
||||
{
|
||||
[Test]
|
||||
public void DoesNotThrowIndexOutOfRangeException1()
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
Markdown.ToHtml("+-\n|\n+", pipeline);
|
||||
});
|
||||
}
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
Markdown.ToHtml("+-\n|\n+", pipeline);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoesNotThrowIndexOutOfRangeException2()
|
||||
[Test]
|
||||
public void DoesNotThrowIndexOutOfRangeException2()
|
||||
{
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
Markdown.ToHtml("+--\n|\n+0", pipeline);
|
||||
});
|
||||
}
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
Markdown.ToHtml("+--\n|\n+0", pipeline);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoesNotThrowIndexOutOfRangeException3()
|
||||
[Test]
|
||||
public void DoesNotThrowIndexOutOfRangeException3()
|
||||
{
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
Markdown.ToHtml("+-\n|\n+\n0", pipeline);
|
||||
});
|
||||
}
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
Markdown.ToHtml("+-\n|\n+\n0", pipeline);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoesNotThrowIndexOutOfRangeException4()
|
||||
[Test]
|
||||
public void DoesNotThrowIndexOutOfRangeException4()
|
||||
{
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
Markdown.ToHtml("+-\n|\n+0", pipeline);
|
||||
});
|
||||
}
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
Markdown.ToHtml("+-\n|\n+0", pipeline);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,189 +1,186 @@
|
||||
using Markdig.Helpers;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Markdig.Tests
|
||||
using Markdig.Helpers;
|
||||
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestFastStringWriter
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestFastStringWriter
|
||||
private const string NewLineReplacement = "~~NEW_LINE~~";
|
||||
|
||||
private FastStringWriter _writer = new();
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
private const string NewLineReplacement = "~~NEW_LINE~~";
|
||||
|
||||
private FastStringWriter _writer = new();
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
_writer = new FastStringWriter
|
||||
{
|
||||
_writer = new FastStringWriter
|
||||
{
|
||||
NewLine = NewLineReplacement
|
||||
};
|
||||
}
|
||||
NewLine = NewLineReplacement
|
||||
};
|
||||
}
|
||||
|
||||
public void AssertToString(string value)
|
||||
{
|
||||
value = value.Replace("\n", NewLineReplacement);
|
||||
Assert.AreEqual(value, _writer.ToString());
|
||||
Assert.AreEqual(value, _writer.ToString());
|
||||
}
|
||||
public void AssertToString(string value)
|
||||
{
|
||||
value = value.Replace("\n", NewLineReplacement);
|
||||
Assert.AreEqual(value, _writer.ToString());
|
||||
Assert.AreEqual(value, _writer.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task NewLine()
|
||||
{
|
||||
Assert.AreEqual("\n", new FastStringWriter().NewLine);
|
||||
[Test]
|
||||
public async Task NewLine()
|
||||
{
|
||||
Assert.AreEqual("\n", new FastStringWriter().NewLine);
|
||||
|
||||
_writer.NewLine = "\r";
|
||||
Assert.AreEqual("\r", _writer.NewLine);
|
||||
_writer.NewLine = "\r";
|
||||
Assert.AreEqual("\r", _writer.NewLine);
|
||||
|
||||
_writer.NewLine = "foo";
|
||||
Assert.AreEqual("foo", _writer.NewLine);
|
||||
_writer.NewLine = "foo";
|
||||
Assert.AreEqual("foo", _writer.NewLine);
|
||||
|
||||
_writer.WriteLine();
|
||||
await _writer.WriteLineAsync();
|
||||
_writer.WriteLine("bar");
|
||||
Assert.AreEqual("foofoobarfoo", _writer.ToString());
|
||||
}
|
||||
_writer.WriteLine();
|
||||
await _writer.WriteLineAsync();
|
||||
_writer.WriteLine("bar");
|
||||
Assert.AreEqual("foofoobarfoo", _writer.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FlushCloseDispose()
|
||||
[Test]
|
||||
public async Task FlushCloseDispose()
|
||||
{
|
||||
_writer.Write('a');
|
||||
|
||||
// Nops
|
||||
_writer.Close();
|
||||
_writer.Dispose();
|
||||
await _writer.DisposeAsync();
|
||||
_writer.Flush();
|
||||
await _writer.FlushAsync();
|
||||
|
||||
_writer.Write('b');
|
||||
AssertToString("ab");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_Char()
|
||||
{
|
||||
_writer.Write('a');
|
||||
AssertToString("a");
|
||||
|
||||
_writer.Write('b');
|
||||
AssertToString("ab");
|
||||
|
||||
_writer.Write('\0');
|
||||
_writer.Write('\r');
|
||||
_writer.Write('\u1234');
|
||||
AssertToString("ab\0\r\u1234");
|
||||
|
||||
_writer.Reset();
|
||||
AssertToString("");
|
||||
|
||||
_writer.Write('a');
|
||||
_writer.WriteLine('b');
|
||||
_writer.Write('c');
|
||||
_writer.Write('d');
|
||||
_writer.WriteLine('e');
|
||||
AssertToString("ab\ncde\n");
|
||||
|
||||
await _writer.WriteAsync('f');
|
||||
await _writer.WriteLineAsync('g');
|
||||
AssertToString("ab\ncde\nfg\n");
|
||||
|
||||
_writer.Reset();
|
||||
|
||||
for (int i = 0; i < 2050; i++)
|
||||
{
|
||||
_writer.Write('a');
|
||||
|
||||
// Nops
|
||||
_writer.Close();
|
||||
_writer.Dispose();
|
||||
await _writer.DisposeAsync();
|
||||
_writer.Flush();
|
||||
await _writer.FlushAsync();
|
||||
|
||||
_writer.Write('b');
|
||||
AssertToString("ab");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_Char()
|
||||
{
|
||||
_writer.Write('a');
|
||||
AssertToString("a");
|
||||
|
||||
_writer.Write('b');
|
||||
AssertToString("ab");
|
||||
|
||||
_writer.Write('\0');
|
||||
_writer.Write('\r');
|
||||
_writer.Write('\u1234');
|
||||
AssertToString("ab\0\r\u1234");
|
||||
|
||||
_writer.Reset();
|
||||
AssertToString("");
|
||||
|
||||
_writer.Write('a');
|
||||
_writer.WriteLine('b');
|
||||
_writer.Write('c');
|
||||
_writer.Write('d');
|
||||
_writer.WriteLine('e');
|
||||
AssertToString("ab\ncde\n");
|
||||
|
||||
await _writer.WriteAsync('f');
|
||||
await _writer.WriteLineAsync('g');
|
||||
AssertToString("ab\ncde\nfg\n");
|
||||
|
||||
_writer.Reset();
|
||||
|
||||
for (int i = 0; i < 2050; i++)
|
||||
{
|
||||
_writer.Write('a');
|
||||
AssertToString(new string('a', i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_String()
|
||||
{
|
||||
_writer.Write("foo");
|
||||
AssertToString("foo");
|
||||
|
||||
_writer.WriteLine("bar");
|
||||
AssertToString("foobar\n");
|
||||
|
||||
await _writer.WriteAsync("baz");
|
||||
await _writer.WriteLineAsync("foo");
|
||||
AssertToString("foobar\nbazfoo\n");
|
||||
|
||||
_writer.Write(new string('a', 1050));
|
||||
AssertToString("foobar\nbazfoo\n" + new string('a', 1050));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_Span()
|
||||
{
|
||||
_writer.Write("foo".AsSpan());
|
||||
AssertToString("foo");
|
||||
|
||||
_writer.WriteLine("bar".AsSpan());
|
||||
AssertToString("foobar\n");
|
||||
|
||||
await _writer.WriteAsync("baz".AsMemory());
|
||||
await _writer.WriteLineAsync("foo".AsMemory());
|
||||
AssertToString("foobar\nbazfoo\n");
|
||||
|
||||
_writer.Write(new string('a', 1050).AsSpan());
|
||||
AssertToString("foobar\nbazfoo\n" + new string('a', 1050));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_CharArray()
|
||||
{
|
||||
_writer.Write("foo".ToCharArray());
|
||||
AssertToString("foo");
|
||||
|
||||
_writer.WriteLine("bar".ToCharArray());
|
||||
AssertToString("foobar\n");
|
||||
|
||||
await _writer.WriteAsync("baz".ToCharArray());
|
||||
await _writer.WriteLineAsync("foo".ToCharArray());
|
||||
AssertToString("foobar\nbazfoo\n");
|
||||
|
||||
_writer.Write(new string('a', 1050).ToCharArray());
|
||||
AssertToString("foobar\nbazfoo\n" + new string('a', 1050));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_CharArrayWithIndexes()
|
||||
{
|
||||
_writer.Write("foo".ToCharArray(), 1, 1);
|
||||
AssertToString("o");
|
||||
|
||||
_writer.WriteLine("bar".ToCharArray(), 0, 2);
|
||||
AssertToString("oba\n");
|
||||
|
||||
await _writer.WriteAsync("baz".ToCharArray(), 0, 1);
|
||||
await _writer.WriteLineAsync("foo".ToCharArray(), 0, 3);
|
||||
AssertToString("oba\nbfoo\n");
|
||||
|
||||
_writer.Write(new string('a', 1050).ToCharArray(), 10, 1035);
|
||||
AssertToString("oba\nbfoo\n" + new string('a', 1035));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_StringBuilder()
|
||||
{
|
||||
_writer.Write(new StringBuilder("foo"));
|
||||
AssertToString("foo");
|
||||
|
||||
_writer.WriteLine(new StringBuilder("bar"));
|
||||
AssertToString("foobar\n");
|
||||
|
||||
await _writer.WriteAsync(new StringBuilder("baz"));
|
||||
await _writer.WriteLineAsync(new StringBuilder("foo"));
|
||||
AssertToString("foobar\nbazfoo\n");
|
||||
|
||||
var sb = new StringBuilder("foo");
|
||||
sb.Append('a', 1050);
|
||||
_writer.Write(sb);
|
||||
AssertToString("foobar\nbazfoo\nfoo" + new string('a', 1050));
|
||||
AssertToString(new string('a', i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_String()
|
||||
{
|
||||
_writer.Write("foo");
|
||||
AssertToString("foo");
|
||||
|
||||
_writer.WriteLine("bar");
|
||||
AssertToString("foobar\n");
|
||||
|
||||
await _writer.WriteAsync("baz");
|
||||
await _writer.WriteLineAsync("foo");
|
||||
AssertToString("foobar\nbazfoo\n");
|
||||
|
||||
_writer.Write(new string('a', 1050));
|
||||
AssertToString("foobar\nbazfoo\n" + new string('a', 1050));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_Span()
|
||||
{
|
||||
_writer.Write("foo".AsSpan());
|
||||
AssertToString("foo");
|
||||
|
||||
_writer.WriteLine("bar".AsSpan());
|
||||
AssertToString("foobar\n");
|
||||
|
||||
await _writer.WriteAsync("baz".AsMemory());
|
||||
await _writer.WriteLineAsync("foo".AsMemory());
|
||||
AssertToString("foobar\nbazfoo\n");
|
||||
|
||||
_writer.Write(new string('a', 1050).AsSpan());
|
||||
AssertToString("foobar\nbazfoo\n" + new string('a', 1050));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_CharArray()
|
||||
{
|
||||
_writer.Write("foo".ToCharArray());
|
||||
AssertToString("foo");
|
||||
|
||||
_writer.WriteLine("bar".ToCharArray());
|
||||
AssertToString("foobar\n");
|
||||
|
||||
await _writer.WriteAsync("baz".ToCharArray());
|
||||
await _writer.WriteLineAsync("foo".ToCharArray());
|
||||
AssertToString("foobar\nbazfoo\n");
|
||||
|
||||
_writer.Write(new string('a', 1050).ToCharArray());
|
||||
AssertToString("foobar\nbazfoo\n" + new string('a', 1050));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_CharArrayWithIndexes()
|
||||
{
|
||||
_writer.Write("foo".ToCharArray(), 1, 1);
|
||||
AssertToString("o");
|
||||
|
||||
_writer.WriteLine("bar".ToCharArray(), 0, 2);
|
||||
AssertToString("oba\n");
|
||||
|
||||
await _writer.WriteAsync("baz".ToCharArray(), 0, 1);
|
||||
await _writer.WriteLineAsync("foo".ToCharArray(), 0, 3);
|
||||
AssertToString("oba\nbfoo\n");
|
||||
|
||||
_writer.Write(new string('a', 1050).ToCharArray(), 10, 1035);
|
||||
AssertToString("oba\nbfoo\n" + new string('a', 1035));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_StringBuilder()
|
||||
{
|
||||
_writer.Write(new StringBuilder("foo"));
|
||||
AssertToString("foo");
|
||||
|
||||
_writer.WriteLine(new StringBuilder("bar"));
|
||||
AssertToString("foobar\n");
|
||||
|
||||
await _writer.WriteAsync(new StringBuilder("baz"));
|
||||
await _writer.WriteLineAsync(new StringBuilder("foo"));
|
||||
AssertToString("foobar\nbazfoo\n");
|
||||
|
||||
var sb = new StringBuilder("foo");
|
||||
sb.Append('a', 1050);
|
||||
_writer.Write(sb);
|
||||
AssertToString("foobar\nbazfoo\nfoo" + new string('a', 1050));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,43 @@
|
||||
using System.Linq;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestFencedCodeBlocks
|
||||
{
|
||||
public class TestFencedCodeBlocks
|
||||
[Test]
|
||||
[TestCase("c#", "c#", "")]
|
||||
[TestCase("C#", "C#", "")]
|
||||
[TestCase(" c#", "c#", "")]
|
||||
[TestCase(" c# ", "c#", "")]
|
||||
[TestCase(" \tc# ", "c#", "")]
|
||||
[TestCase("\t c# \t", "c#", "")]
|
||||
[TestCase(" c# ", "c#", "")]
|
||||
[TestCase(" c# foo", "c#", "foo")]
|
||||
[TestCase(" c# \t fOo \t", "c#", "fOo")]
|
||||
[TestCase("in\\%fo arg\\%ument", "in%fo", "arg%ument")]
|
||||
[TestCase("info	 arg´ument", "info\t", "arg\u00B4ument")]
|
||||
public void TestInfoAndArguments(string infoString, string expectedInfo, string expectedArguments)
|
||||
{
|
||||
[Test]
|
||||
[TestCase("c#", "c#", "")]
|
||||
[TestCase("C#", "C#", "")]
|
||||
[TestCase(" c#", "c#", "")]
|
||||
[TestCase(" c# ", "c#", "")]
|
||||
[TestCase(" \tc# ", "c#", "")]
|
||||
[TestCase("\t c# \t", "c#", "")]
|
||||
[TestCase(" c# ", "c#", "")]
|
||||
[TestCase(" c# foo", "c#", "foo")]
|
||||
[TestCase(" c# \t fOo \t", "c#", "fOo")]
|
||||
[TestCase("in\\%fo arg\\%ument", "in%fo", "arg%ument")]
|
||||
[TestCase("info	 arg´ument", "info\t", "arg\u00B4ument")]
|
||||
public void TestInfoAndArguments(string infoString, string expectedInfo, string expectedArguments)
|
||||
Test('`');
|
||||
Test('~');
|
||||
|
||||
void Test(char fencedChar)
|
||||
{
|
||||
Test('`');
|
||||
Test('~');
|
||||
const string Contents = "Foo\nBar\n";
|
||||
|
||||
void Test(char fencedChar)
|
||||
{
|
||||
const string Contents = "Foo\nBar\n";
|
||||
var fence = new string(fencedChar, 3);
|
||||
string markdownText = $"{fence}{infoString}\n{Contents}\n{fence}\n";
|
||||
|
||||
string fence = new string(fencedChar, 3);
|
||||
string markdownText = $"{fence}{infoString}\n{Contents}\n{fence}\n";
|
||||
MarkdownDocument document = Markdown.Parse(markdownText);
|
||||
|
||||
MarkdownDocument document = Markdown.Parse(markdownText);
|
||||
FencedCodeBlock codeBlock = document.Descendants<FencedCodeBlock>().Single();
|
||||
|
||||
FencedCodeBlock codeBlock = document.Descendants<FencedCodeBlock>().Single();
|
||||
|
||||
Assert.AreEqual(fencedChar, codeBlock.FencedChar);
|
||||
Assert.AreEqual(3, codeBlock.OpeningFencedCharCount);
|
||||
Assert.AreEqual(3, codeBlock.ClosingFencedCharCount);
|
||||
Assert.AreEqual(expectedInfo, codeBlock.Info);
|
||||
Assert.AreEqual(expectedArguments, codeBlock.Arguments);
|
||||
Assert.AreEqual(Contents, codeBlock.Lines.ToString());
|
||||
}
|
||||
Assert.AreEqual(fencedChar, codeBlock.FencedChar);
|
||||
Assert.AreEqual(3, codeBlock.OpeningFencedCharCount);
|
||||
Assert.AreEqual(3, codeBlock.ClosingFencedCharCount);
|
||||
Assert.AreEqual(expectedInfo, codeBlock.Info);
|
||||
Assert.AreEqual(expectedArguments, codeBlock.Arguments);
|
||||
Assert.AreEqual(Contents, codeBlock.Lines.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,94 +3,91 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Renderers.Html;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture()]
|
||||
public class TestHtmlAttributes
|
||||
{
|
||||
[TestFixture()]
|
||||
public class TestHtmlAttributes
|
||||
[Test]
|
||||
public void TestAddClass()
|
||||
{
|
||||
[Test]
|
||||
public void TestAddClass()
|
||||
{
|
||||
var attributes = new HtmlAttributes();
|
||||
attributes.AddClass("test");
|
||||
Assert.NotNull(attributes.Classes);
|
||||
Assert.AreEqual(new List<string>() { "test" }, attributes.Classes);
|
||||
var attributes = new HtmlAttributes();
|
||||
attributes.AddClass("test");
|
||||
Assert.NotNull(attributes.Classes);
|
||||
Assert.AreEqual(new List<string>() { "test" }, attributes.Classes);
|
||||
|
||||
attributes.AddClass("test");
|
||||
Assert.AreEqual(1, attributes.Classes.Count);
|
||||
attributes.AddClass("test");
|
||||
Assert.AreEqual(1, attributes.Classes.Count);
|
||||
|
||||
attributes.AddClass("test1");
|
||||
Assert.AreEqual(new List<string>() { "test", "test1" }, attributes.Classes);
|
||||
}
|
||||
attributes.AddClass("test1");
|
||||
Assert.AreEqual(new List<string>() { "test", "test1" }, attributes.Classes);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAddProperty()
|
||||
{
|
||||
var attributes = new HtmlAttributes();
|
||||
attributes.AddProperty("key1", "1");
|
||||
Assert.NotNull(attributes.Properties);
|
||||
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1") }, attributes.Properties);
|
||||
[Test]
|
||||
public void TestAddProperty()
|
||||
{
|
||||
var attributes = new HtmlAttributes();
|
||||
attributes.AddProperty("key1", "1");
|
||||
Assert.NotNull(attributes.Properties);
|
||||
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1") }, attributes.Properties);
|
||||
|
||||
attributes.AddPropertyIfNotExist("key1", "1");
|
||||
Assert.NotNull(attributes.Properties);
|
||||
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1") }, attributes.Properties);
|
||||
attributes.AddPropertyIfNotExist("key1", "1");
|
||||
Assert.NotNull(attributes.Properties);
|
||||
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1") }, attributes.Properties);
|
||||
|
||||
attributes.AddPropertyIfNotExist("key2", "2");
|
||||
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1"), new KeyValuePair<string, string>("key2", "2") }, attributes.Properties);
|
||||
}
|
||||
attributes.AddPropertyIfNotExist("key2", "2");
|
||||
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1"), new KeyValuePair<string, string>("key2", "2") }, attributes.Properties);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCopyTo()
|
||||
{
|
||||
var from = new HtmlAttributes();
|
||||
from.AddClass("test");
|
||||
from.AddProperty("key1", "1");
|
||||
[Test]
|
||||
public void TestCopyTo()
|
||||
{
|
||||
var from = new HtmlAttributes();
|
||||
from.AddClass("test");
|
||||
from.AddProperty("key1", "1");
|
||||
|
||||
var to = new HtmlAttributes();
|
||||
from.CopyTo(to);
|
||||
var to = new HtmlAttributes();
|
||||
from.CopyTo(to);
|
||||
|
||||
Assert.True(ReferenceEquals(from.Classes, to.Classes));
|
||||
Assert.True(ReferenceEquals(from.Properties, to.Properties));
|
||||
Assert.True(ReferenceEquals(from.Classes, to.Classes));
|
||||
Assert.True(ReferenceEquals(from.Properties, to.Properties));
|
||||
|
||||
// From: Classes From: Properties To: Classes To: Properties
|
||||
// test1: null null null null
|
||||
from = new HtmlAttributes();
|
||||
to = new HtmlAttributes();
|
||||
from.CopyTo(to, false, false);
|
||||
Assert.Null(to.Classes);
|
||||
Assert.Null(to.Properties);
|
||||
// From: Classes From: Properties To: Classes To: Properties
|
||||
// test1: null null null null
|
||||
from = new HtmlAttributes();
|
||||
to = new HtmlAttributes();
|
||||
from.CopyTo(to, false, false);
|
||||
Assert.Null(to.Classes);
|
||||
Assert.Null(to.Properties);
|
||||
|
||||
// test2: ["test"] ["key1", "1"] null null
|
||||
from = new HtmlAttributes();
|
||||
to = new HtmlAttributes();
|
||||
from.AddClass("test");
|
||||
from.AddProperty("key1", "1");
|
||||
from.CopyTo(to, false, false);
|
||||
Assert.AreEqual(new List<string>() { "test" }, to.Classes);
|
||||
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1")}, to.Properties);
|
||||
// test2: ["test"] ["key1", "1"] null null
|
||||
from = new HtmlAttributes();
|
||||
to = new HtmlAttributes();
|
||||
from.AddClass("test");
|
||||
from.AddProperty("key1", "1");
|
||||
from.CopyTo(to, false, false);
|
||||
Assert.AreEqual(new List<string>() { "test" }, to.Classes);
|
||||
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1")}, to.Properties);
|
||||
|
||||
// test3: null null ["test"] ["key1", "1"]
|
||||
from = new HtmlAttributes();
|
||||
to = new HtmlAttributes();
|
||||
to.AddClass("test");
|
||||
to.AddProperty("key1", "1");
|
||||
from.CopyTo(to, false, false);
|
||||
Assert.AreEqual(new List<string>() { "test" }, to.Classes);
|
||||
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1") }, to.Properties);
|
||||
// test3: null null ["test"] ["key1", "1"]
|
||||
from = new HtmlAttributes();
|
||||
to = new HtmlAttributes();
|
||||
to.AddClass("test");
|
||||
to.AddProperty("key1", "1");
|
||||
from.CopyTo(to, false, false);
|
||||
Assert.AreEqual(new List<string>() { "test" }, to.Classes);
|
||||
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1") }, to.Properties);
|
||||
|
||||
// test4: ["test1"] ["key2", "2"] ["test"] ["key1", "1"]
|
||||
from = new HtmlAttributes();
|
||||
to = new HtmlAttributes();
|
||||
from.AddClass("test1");
|
||||
from.AddProperty("key2", "2");
|
||||
to.AddClass("test");
|
||||
to.AddProperty("key1", "1");
|
||||
from.CopyTo(to, false, false);
|
||||
Assert.AreEqual(new List<string>() { "test", "test1" }, to.Classes);
|
||||
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1"), new KeyValuePair<string, string>("key2", "2") }, to.Properties);
|
||||
}
|
||||
// test4: ["test1"] ["key2", "2"] ["test"] ["key1", "1"]
|
||||
from = new HtmlAttributes();
|
||||
to = new HtmlAttributes();
|
||||
from.AddClass("test1");
|
||||
from.AddProperty("key2", "2");
|
||||
to.AddClass("test");
|
||||
to.AddProperty("key1", "1");
|
||||
from.CopyTo(to, false, false);
|
||||
Assert.AreEqual(new List<string>() { "test", "test1" }, to.Classes);
|
||||
Assert.AreEqual(new List<KeyValuePair<string, string>>() { new KeyValuePair<string, string>("key1", "1"), new KeyValuePair<string, string>("key2", "2") }, to.Properties);
|
||||
}
|
||||
}
|
||||
35
src/Markdig.Tests/TestHtmlCodeBlocks.cs
Normal file
35
src/Markdig.Tests/TestHtmlCodeBlocks.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestHtmlCodeBlocks
|
||||
{
|
||||
// Start condition: line begins with the string < or </ followed by one of the strings (case-insensitive)
|
||||
// {list of all tags}, followed by a space, a tab, the end of the line, the string >, or the string />.
|
||||
public static string[] KnownSimpleHtmlTags =>
|
||||
[
|
||||
"address", "article", "aside", "base", "basefont", "blockquote", "body", "caption", "center", "col", "colgroup", "dd", "details",
|
||||
"dialog", "dir", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "frame", "frameset",
|
||||
"h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hr", "html", "iframe", "legend", "li", "link",
|
||||
"main", "menu", "menuitem", "nav", "noframes", "ol", "optgroup", "option", "p", "param",
|
||||
"search", "section", "summary", "table", "tbody", "td", "tfoot", "th", "thead", "title", "tr", "track", "ul",
|
||||
];
|
||||
|
||||
[Theory]
|
||||
[TestCaseSource(nameof(KnownSimpleHtmlTags))]
|
||||
public void TestKnownTags(string tag)
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse(
|
||||
$"""
|
||||
Hello
|
||||
<{tag} />
|
||||
World
|
||||
""".ReplaceLineEndings("\n"));
|
||||
|
||||
HtmlBlock[] htmlBlocks = document.Descendants<HtmlBlock>().ToArray();
|
||||
|
||||
Assert.AreEqual(1, htmlBlocks.Length);
|
||||
Assert.AreEqual(7, htmlBlocks[0].Span.Start);
|
||||
Assert.AreEqual(10 + tag.Length, htmlBlocks[0].Span.Length);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,28 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using NUnit.Framework;
|
||||
using Markdig.Helpers;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestHtmlHelper
|
||||
{
|
||||
[Test]
|
||||
public void TestParseHtmlTagSimple()
|
||||
{
|
||||
var inputTag = "<a>";
|
||||
var text = new StringSlice(inputTag);
|
||||
Assert.True(HtmlHelper.TryParseHtmlTag(ref text, out string outputTag));
|
||||
Assert.AreEqual(inputTag, outputTag);
|
||||
}
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[Test]
|
||||
public void TestParseHtmlTagSimpleWithAttribute()
|
||||
{
|
||||
var inputTag = "<a href='http://google.com'>";
|
||||
var text = new StringSlice(inputTag);
|
||||
Assert.True(HtmlHelper.TryParseHtmlTag(ref text, out string outputTag));
|
||||
Assert.AreEqual(inputTag, outputTag);
|
||||
}
|
||||
[TestFixture]
|
||||
public class TestHtmlHelper
|
||||
{
|
||||
[Test]
|
||||
public void TestParseHtmlTagSimple()
|
||||
{
|
||||
var inputTag = "<a>";
|
||||
var text = new StringSlice(inputTag);
|
||||
Assert.True(HtmlHelper.TryParseHtmlTag(ref text, out string outputTag));
|
||||
Assert.AreEqual(inputTag, outputTag);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestParseHtmlTagSimpleWithAttribute()
|
||||
{
|
||||
var inputTag = "<a href='http://google.com'>";
|
||||
var text = new StringSlice(inputTag);
|
||||
Assert.True(HtmlHelper.TryParseHtmlTag(ref text, out string outputTag));
|
||||
Assert.AreEqual(inputTag, outputTag);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,23 @@
|
||||
using NUnit.Framework;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestImageAltText
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestImageAltText
|
||||
[Test]
|
||||
[TestCase("", "")]
|
||||
[TestCase("", "foo")]
|
||||
[TestCase("![][1]\n\n[1]: image.jpg", "")]
|
||||
[TestCase("![bar][1]\n\n[1]: image.jpg", "bar")]
|
||||
[TestCase("", "")]
|
||||
[TestCase("", "foo")]
|
||||
[TestCase("![][1]\n\n[1]: image.jpg 'title'", "")]
|
||||
[TestCase("![bar][1]\n\n[1]: image.jpg 'title'", "bar")]
|
||||
public void TestImageHtmlAltText(string markdown, string expectedAltText)
|
||||
{
|
||||
[Test]
|
||||
[TestCase("", "")]
|
||||
[TestCase("", "foo")]
|
||||
[TestCase("![][1]\n\n[1]: image.jpg", "")]
|
||||
[TestCase("![bar][1]\n\n[1]: image.jpg", "bar")]
|
||||
[TestCase("", "")]
|
||||
[TestCase("", "foo")]
|
||||
[TestCase("![][1]\n\n[1]: image.jpg 'title'", "")]
|
||||
[TestCase("![bar][1]\n\n[1]: image.jpg 'title'", "bar")]
|
||||
public void TestImageHtmlAltText(string markdown, string expectedAltText)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown);
|
||||
string actualAltText = Regex.Match(html, "alt=\"(.*?)\"").Groups[1].Value;
|
||||
Assert.AreEqual(expectedAltText, actualAltText);
|
||||
}
|
||||
string html = Markdown.ToHtml(markdown);
|
||||
string actualAltText = Regex.Match(html, "alt=\"(.*?)\"").Groups[1].Value;
|
||||
Assert.AreEqual(expectedAltText, actualAltText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +1,65 @@
|
||||
using Markdig.Helpers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestLazySubstring
|
||||
{
|
||||
public class TestLazySubstring
|
||||
[Theory]
|
||||
[TestCase("")]
|
||||
[TestCase("a")]
|
||||
[TestCase("foo")]
|
||||
public void LazySubstring_ReturnsCorrectSubstring(string text)
|
||||
{
|
||||
[Theory]
|
||||
[TestCase("")]
|
||||
[TestCase("a")]
|
||||
[TestCase("foo")]
|
||||
public void LazySubstring_ReturnsCorrectSubstring(string text)
|
||||
{
|
||||
var substring = new LazySubstring(text);
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(text.Length, substring.Length);
|
||||
var substring = new LazySubstring(text);
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(text.Length, substring.Length);
|
||||
|
||||
Assert.AreEqual(text, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(text, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(text.Length, substring.Length);
|
||||
Assert.AreEqual(text, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(text, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(text.Length, substring.Length);
|
||||
|
||||
Assert.AreSame(substring.ToString(), substring.ToString());
|
||||
Assert.AreEqual(text, substring.ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(text.Length, substring.Length);
|
||||
Assert.AreSame(substring.ToString(), substring.ToString());
|
||||
Assert.AreEqual(text, substring.ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(text.Length, substring.Length);
|
||||
|
||||
Assert.AreEqual(text, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(text, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(text.Length, substring.Length);
|
||||
}
|
||||
Assert.AreEqual(text, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(text, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(text.Length, substring.Length);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[TestCase("", 0, 0)]
|
||||
[TestCase("a", 0, 0)]
|
||||
[TestCase("a", 1, 0)]
|
||||
[TestCase("a", 0, 1)]
|
||||
[TestCase("foo", 1, 0)]
|
||||
[TestCase("foo", 1, 1)]
|
||||
[TestCase("foo", 1, 2)]
|
||||
[TestCase("foo", 0, 3)]
|
||||
public void LazySubstring_ReturnsCorrectSubstring(string text, int start, int length)
|
||||
{
|
||||
var substring = new LazySubstring(text, start, length);
|
||||
Assert.AreEqual(start, substring.Offset);
|
||||
Assert.AreEqual(length, substring.Length);
|
||||
[Theory]
|
||||
[TestCase("", 0, 0)]
|
||||
[TestCase("a", 0, 0)]
|
||||
[TestCase("a", 1, 0)]
|
||||
[TestCase("a", 0, 1)]
|
||||
[TestCase("foo", 1, 0)]
|
||||
[TestCase("foo", 1, 1)]
|
||||
[TestCase("foo", 1, 2)]
|
||||
[TestCase("foo", 0, 3)]
|
||||
public void LazySubstring_ReturnsCorrectSubstring(string text, int start, int length)
|
||||
{
|
||||
var substring = new LazySubstring(text, start, length);
|
||||
Assert.AreEqual(start, substring.Offset);
|
||||
Assert.AreEqual(length, substring.Length);
|
||||
|
||||
string expectedSubstring = text.Substring(start, length);
|
||||
string expectedSubstring = text.Substring(start, length);
|
||||
|
||||
Assert.AreEqual(expectedSubstring, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(expectedSubstring, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(start, substring.Offset);
|
||||
Assert.AreEqual(length, substring.Length);
|
||||
Assert.AreEqual(expectedSubstring, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(expectedSubstring, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(start, substring.Offset);
|
||||
Assert.AreEqual(length, substring.Length);
|
||||
|
||||
Assert.AreSame(substring.ToString(), substring.ToString());
|
||||
Assert.AreEqual(expectedSubstring, substring.ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(length, substring.Length);
|
||||
Assert.AreSame(substring.ToString(), substring.ToString());
|
||||
Assert.AreEqual(expectedSubstring, substring.ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(length, substring.Length);
|
||||
|
||||
Assert.AreEqual(expectedSubstring, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(expectedSubstring, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(length, substring.Length);
|
||||
}
|
||||
Assert.AreEqual(expectedSubstring, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(expectedSubstring, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(length, substring.Length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,126 +2,124 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using NUnit.Framework;
|
||||
using Markdig.Helpers;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Test for <see cref="LineReader"/>.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class TestLineReader
|
||||
{
|
||||
/// <summary>
|
||||
/// Test for <see cref="LineReader"/>.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class TestLineReader
|
||||
[Test]
|
||||
public void TestEmpty()
|
||||
{
|
||||
[Test]
|
||||
public void TestEmpty()
|
||||
{
|
||||
var lineReader = new LineReader("");
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
var lineReader = new LineReader("");
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinesOnlyLf()
|
||||
{
|
||||
var lineReader = new LineReader("\n\n\n");
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(1, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(2, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
[Test]
|
||||
public void TestLinesOnlyLf()
|
||||
{
|
||||
var lineReader = new LineReader("\n\n\n");
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(1, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(2, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinesOnlyCr()
|
||||
{
|
||||
var lineReader = new LineReader("\r\r\r");
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(1, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(2, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
[Test]
|
||||
public void TestLinesOnlyCr()
|
||||
{
|
||||
var lineReader = new LineReader("\r\r\r");
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(1, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(2, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinesOnlyCrLf()
|
||||
{
|
||||
var lineReader = new LineReader("\r\n\r\n\r\n");
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(2, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
[Test]
|
||||
public void TestLinesOnlyCrLf()
|
||||
{
|
||||
var lineReader = new LineReader("\r\n\r\n\r\n");
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(2, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNoEndOfLine()
|
||||
{
|
||||
var lineReader = new LineReader("123");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
[Test]
|
||||
public void TestNoEndOfLine()
|
||||
{
|
||||
var lineReader = new LineReader("123");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLf()
|
||||
{
|
||||
var lineReader = new LineReader("123\n");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
[Test]
|
||||
public void TestLf()
|
||||
{
|
||||
var lineReader = new LineReader("123\n");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLf2()
|
||||
{
|
||||
// When limited == true, we limit the internal buffer exactly after the first new line char '\n'
|
||||
var lineReader = new LineReader("123\n456");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.AreEqual("456", lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
[Test]
|
||||
public void TestLf2()
|
||||
{
|
||||
// When limited == true, we limit the internal buffer exactly after the first new line char '\n'
|
||||
var lineReader = new LineReader("123\n456");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.AreEqual("456", lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCr()
|
||||
{
|
||||
var lineReader = new LineReader("123\r");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
[Test]
|
||||
public void TestCr()
|
||||
{
|
||||
var lineReader = new LineReader("123\r");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCr2()
|
||||
{
|
||||
var lineReader = new LineReader("123\r456");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.AreEqual("456", lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
[Test]
|
||||
public void TestCr2()
|
||||
{
|
||||
var lineReader = new LineReader("123\r456");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.AreEqual("456", lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCrLf()
|
||||
{
|
||||
// When limited == true, we limit the internal buffer exactly after the first new line char '\r'
|
||||
// and we check that we don't get a new line for `\n`
|
||||
var lineReader = new LineReader("123\r\n");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(5, lineReader.SourcePosition);
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
[Test]
|
||||
public void TestCrLf()
|
||||
{
|
||||
// When limited == true, we limit the internal buffer exactly after the first new line char '\r'
|
||||
// and we check that we don't get a new line for `\n`
|
||||
var lineReader = new LineReader("123\r\n");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(5, lineReader.SourcePosition);
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCrLf2()
|
||||
{
|
||||
var lineReader = new LineReader("123\r\n456");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(5, lineReader.SourcePosition);
|
||||
Assert.AreEqual("456", lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
[Test]
|
||||
public void TestCrLf2()
|
||||
{
|
||||
var lineReader = new LineReader("123\r\n456");
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(5, lineReader.SourcePosition);
|
||||
Assert.AreEqual("456", lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
}
|
||||
@@ -2,374 +2,395 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using NUnit.Framework;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestLinkHelper
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestLinkHelper
|
||||
[Test]
|
||||
public void TestUrlSimple()
|
||||
{
|
||||
[Test]
|
||||
public void TestUrlSimple()
|
||||
{
|
||||
var text = new StringSlice("toto tutu");
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
Assert.AreEqual("toto", link);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
var text = new StringSlice("toto tutu");
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
Assert.AreEqual("toto", link);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUrlUrl()
|
||||
{
|
||||
var text = new StringSlice("http://google.com)");
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual(')', text.CurrentChar);
|
||||
}
|
||||
[Test]
|
||||
public void TestUrlUrl()
|
||||
{
|
||||
var text = new StringSlice("http://google.com)");
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual(')', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("http://google.com.")]
|
||||
[TestCase("http://google.com. ")]
|
||||
public void TestUrlTrailingFullStop(string uri)
|
||||
{
|
||||
var text = new StringSlice(uri);
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _, true));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual('.', text.CurrentChar);
|
||||
}
|
||||
[Test]
|
||||
[TestCase("http://google.com.")]
|
||||
[TestCase("http://google.com. ")]
|
||||
public void TestUrlTrailingFullStop(string uri)
|
||||
{
|
||||
var text = new StringSlice(uri);
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _, true));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual('.', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUrlNestedParenthesis()
|
||||
{
|
||||
var text = new StringSlice("(toto)tutu(tata) nooo");
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
Assert.AreEqual("(toto)tutu(tata)", link);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
[Test]
|
||||
public void TestUrlNestedParenthesis()
|
||||
{
|
||||
var text = new StringSlice("(toto)tutu(tata) nooo");
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
Assert.AreEqual("(toto)tutu(tata)", link);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUrlAlternate()
|
||||
{
|
||||
var text = new StringSlice("<toto_tata_tutu> nooo");
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
Assert.AreEqual("toto_tata_tutu", link);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
[Test]
|
||||
public void TestUrlAlternate()
|
||||
{
|
||||
var text = new StringSlice("<toto_tata_tutu> nooo");
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
Assert.AreEqual("toto_tata_tutu", link);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUrlAlternateInvalid()
|
||||
{
|
||||
var text = new StringSlice("<toto_tata_tutu");
|
||||
Assert.False(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
}
|
||||
[Test]
|
||||
public void TestUrlAlternateInvalid()
|
||||
{
|
||||
var text = new StringSlice("<toto_tata_tutu");
|
||||
Assert.False(LinkHelper.TryParseUrl(ref text, out string link, out _));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTitleSimple()
|
||||
{
|
||||
var text = new StringSlice(@"'tata\tutu\''");
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title, out _));
|
||||
Assert.AreEqual(@"tata\tutu'", title);
|
||||
}
|
||||
[Test]
|
||||
public void TestTitleSimple()
|
||||
{
|
||||
var text = new StringSlice(@"'tata\tutu\''");
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title, out _));
|
||||
Assert.AreEqual(@"tata\tutu'", title);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTitleSimpleAlternate()
|
||||
{
|
||||
var text = new StringSlice(@"""tata\tutu\"""" ");
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title, out _));
|
||||
Assert.AreEqual(@"tata\tutu""", title);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
[Test]
|
||||
public void TestTitleSimpleAlternate()
|
||||
{
|
||||
var text = new StringSlice(@"""tata\tutu\"""" ");
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title, out _));
|
||||
Assert.AreEqual(@"tata\tutu""", title);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUrlAndTitle()
|
||||
{
|
||||
// 0 1 2 3
|
||||
// 0123456789012345678901234567890123456789
|
||||
var text = new StringSlice(@"(http://google.com 'this is a title')ABC");
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual("this is a title", title);
|
||||
Assert.AreEqual(new SourceSpan(1, 17), linkSpan);
|
||||
Assert.AreEqual(new SourceSpan(19, 35), titleSpan);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
}
|
||||
[Test]
|
||||
public void TestTitleMultiline()
|
||||
{
|
||||
var text = new StringSlice("'this\ris\r\na\ntitle'");
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title, out _));
|
||||
Assert.AreEqual("this\ris\r\na\ntitle", title);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUrlAndTitleEmpty()
|
||||
{
|
||||
// 01234
|
||||
var text = new StringSlice(@"(<>)A");
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(string.Empty, link);
|
||||
Assert.AreEqual(string.Empty, title);
|
||||
Assert.AreEqual(new SourceSpan(1, 2), linkSpan);
|
||||
Assert.AreEqual(SourceSpan.Empty, titleSpan);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
}
|
||||
[Test]
|
||||
public void TestTitleMultilineWithSpaceAndBackslash()
|
||||
{
|
||||
var text = new StringSlice("'a\n\\ \\\ntitle'");
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title, out _));
|
||||
Assert.AreEqual("a\n\\ \\\ntitle", title);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUrlAndTitleEmpty2()
|
||||
{
|
||||
// 012345
|
||||
var text = new StringSlice(@"( <> )A");
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(string.Empty, link);
|
||||
Assert.AreEqual(string.Empty, title);
|
||||
Assert.AreEqual(new SourceSpan(2, 3), linkSpan);
|
||||
Assert.AreEqual(SourceSpan.Empty, titleSpan);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
}
|
||||
[Test]
|
||||
public void TestUrlAndTitle()
|
||||
{
|
||||
// 0 1 2 3
|
||||
// 0123456789012345678901234567890123456789
|
||||
var text = new StringSlice(@"(http://google.com 'this is a title')ABC");
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual("this is a title", title);
|
||||
Assert.AreEqual(new SourceSpan(1, 17), linkSpan);
|
||||
Assert.AreEqual(new SourceSpan(19, 35), titleSpan);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUrlAndTitleEmpty()
|
||||
{
|
||||
// 01234
|
||||
var text = new StringSlice(@"(<>)A");
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(string.Empty, link);
|
||||
Assert.AreEqual(string.Empty, title);
|
||||
Assert.AreEqual(new SourceSpan(1, 2), linkSpan);
|
||||
Assert.AreEqual(SourceSpan.Empty, titleSpan);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUrlAndTitleEmpty2()
|
||||
{
|
||||
// 012345
|
||||
var text = new StringSlice(@"( <> )A");
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(string.Empty, link);
|
||||
Assert.AreEqual(string.Empty, title);
|
||||
Assert.AreEqual(new SourceSpan(2, 3), linkSpan);
|
||||
Assert.AreEqual(SourceSpan.Empty, titleSpan);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void TestUrlEmptyWithTitleWithMultipleSpaces()
|
||||
{
|
||||
// 0 1 2
|
||||
// 0123456789012345678901234567
|
||||
var text = new StringSlice(@"( <> 'toto' )A");
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(string.Empty, link);
|
||||
Assert.AreEqual("toto", title);
|
||||
Assert.AreEqual(new SourceSpan(4, 5), linkSpan);
|
||||
Assert.AreEqual(new SourceSpan(12, 17), titleSpan);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
}
|
||||
[Test]
|
||||
public void TestUrlEmptyWithTitleWithMultipleSpaces()
|
||||
{
|
||||
// 0 1 2
|
||||
// 0123456789012345678901234567
|
||||
var text = new StringSlice(@"( <> 'toto' )A");
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(string.Empty, link);
|
||||
Assert.AreEqual("toto", title);
|
||||
Assert.AreEqual(new SourceSpan(4, 5), linkSpan);
|
||||
Assert.AreEqual(new SourceSpan(12, 17), titleSpan);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUrlEmpty()
|
||||
{
|
||||
var text = new StringSlice(@"()A");
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(string.Empty, link);
|
||||
Assert.AreEqual(string.Empty, title);
|
||||
Assert.AreEqual(SourceSpan.Empty, linkSpan);
|
||||
Assert.AreEqual(SourceSpan.Empty, titleSpan);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
}
|
||||
[Test]
|
||||
public void TestUrlEmpty()
|
||||
{
|
||||
var text = new StringSlice(@"()A");
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(string.Empty, link);
|
||||
Assert.AreEqual(string.Empty, title);
|
||||
Assert.AreEqual(SourceSpan.Empty, linkSpan);
|
||||
Assert.AreEqual(SourceSpan.Empty, titleSpan);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultipleLines()
|
||||
{
|
||||
// 0 1 2 3
|
||||
// 01 2345678901234567890 1234567890123456789
|
||||
var text = new StringSlice("(\n<http://google.com>\n 'toto' )A");
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual("toto", title);
|
||||
Assert.AreEqual(new SourceSpan(2, 20), linkSpan);
|
||||
Assert.AreEqual(new SourceSpan(26, 31), titleSpan);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
}
|
||||
[Test]
|
||||
public void TestMultipleLines()
|
||||
{
|
||||
// 0 1 2 3
|
||||
// 01 2345678901234567890 1234567890123456789
|
||||
var text = new StringSlice("(\n<http://google.com>\n 'toto' )A");
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual("toto", title);
|
||||
Assert.AreEqual(new SourceSpan(2, 20), linkSpan);
|
||||
Assert.AreEqual(new SourceSpan(26, 31), titleSpan);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLabelSimple()
|
||||
{
|
||||
// 01234
|
||||
var text = new StringSlice("[foo]");
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out string label, out SourceSpan labelSpan));
|
||||
Assert.AreEqual(new SourceSpan(1, 3), labelSpan);
|
||||
Assert.AreEqual("foo", label);
|
||||
}
|
||||
[Test]
|
||||
public void TestLabelSimple()
|
||||
{
|
||||
// 01234
|
||||
var text = new StringSlice("[foo]");
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out string label, out SourceSpan labelSpan));
|
||||
Assert.AreEqual(new SourceSpan(1, 3), labelSpan);
|
||||
Assert.AreEqual("foo", label);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLabelEscape()
|
||||
{
|
||||
// 012345678
|
||||
var text = new StringSlice(@"[fo\[\]o]");
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out string label, out SourceSpan labelSpan));
|
||||
Assert.AreEqual(new SourceSpan(1, 7), labelSpan);
|
||||
Assert.AreEqual(@"fo[]o", label);
|
||||
}
|
||||
[Test]
|
||||
public void TestLabelEscape()
|
||||
{
|
||||
// 012345678
|
||||
var text = new StringSlice(@"[fo\[\]o]");
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out string label, out SourceSpan labelSpan));
|
||||
Assert.AreEqual(new SourceSpan(1, 7), labelSpan);
|
||||
Assert.AreEqual(@"fo[]o", label);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLabelEscape2()
|
||||
{
|
||||
// 0123
|
||||
var text = new StringSlice(@"[\]]");
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out string label, out SourceSpan labelSpan));
|
||||
Assert.AreEqual(new SourceSpan(1, 2), labelSpan);
|
||||
Assert.AreEqual(@"]", label);
|
||||
}
|
||||
[Test]
|
||||
public void TestLabelEscape2()
|
||||
{
|
||||
// 0123
|
||||
var text = new StringSlice(@"[\]]");
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out string label, out SourceSpan labelSpan));
|
||||
Assert.AreEqual(new SourceSpan(1, 2), labelSpan);
|
||||
Assert.AreEqual(@"]", label);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLabelInvalids()
|
||||
{
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"a"), out string label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"["), out label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"[\x]"), out label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"[[]"), out label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"[ ]"), out label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"[ \t \n ]"), out label));
|
||||
}
|
||||
[Test]
|
||||
public void TestLabelInvalids()
|
||||
{
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"a"), out string label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"["), out label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"[\x]"), out label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"[[]"), out label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"[ ]"), out label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"[ \t \n ]"), out label));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLabelWhitespaceCollapsedAndTrim()
|
||||
{
|
||||
// 0 1 2 3
|
||||
// 0123456789012345678901234567890123456789
|
||||
var text = new StringSlice(@"[ fo o z ]");
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out string label, out SourceSpan labelSpan));
|
||||
Assert.AreEqual(new SourceSpan(6, 17), labelSpan);
|
||||
Assert.AreEqual(@"fo o z", label);
|
||||
}
|
||||
[Test]
|
||||
public void TestLabelWhitespaceCollapsedAndTrim()
|
||||
{
|
||||
// 0 1 2 3
|
||||
// 0123456789012345678901234567890123456789
|
||||
var text = new StringSlice(@"[ fo o z ]");
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out string label, out SourceSpan labelSpan));
|
||||
Assert.AreEqual(new SourceSpan(6, 17), labelSpan);
|
||||
Assert.AreEqual(@"fo o z", label);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestlLinkReferenceDefinitionSimple()
|
||||
{
|
||||
// 0 1 2 3
|
||||
// 0123456789012345678901234567890123456789
|
||||
var text = new StringSlice(@"[foo]: /toto 'title'");
|
||||
Assert.True(LinkHelper.TryParseLinkReferenceDefinition(ref text, out string label, out string url, out string title, out SourceSpan labelSpan, out SourceSpan urlSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(@"foo", label);
|
||||
Assert.AreEqual(@"/toto", url);
|
||||
Assert.AreEqual(@"title", title);
|
||||
Assert.AreEqual(new SourceSpan(1, 3), labelSpan);
|
||||
Assert.AreEqual(new SourceSpan(7, 11), urlSpan);
|
||||
Assert.AreEqual(new SourceSpan(13, 19), titleSpan);
|
||||
[Test]
|
||||
public void TestlLinkReferenceDefinitionSimple()
|
||||
{
|
||||
// 0 1 2 3
|
||||
// 0123456789012345678901234567890123456789
|
||||
var text = new StringSlice(@"[foo]: /toto 'title'");
|
||||
Assert.True(LinkHelper.TryParseLinkReferenceDefinition(ref text, out string label, out string url, out string title, out SourceSpan labelSpan, out SourceSpan urlSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(@"foo", label);
|
||||
Assert.AreEqual(@"/toto", url);
|
||||
Assert.AreEqual(@"title", title);
|
||||
Assert.AreEqual(new SourceSpan(1, 3), labelSpan);
|
||||
Assert.AreEqual(new SourceSpan(7, 11), urlSpan);
|
||||
Assert.AreEqual(new SourceSpan(13, 19), titleSpan);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAutoLinkUrlSimple()
|
||||
{
|
||||
var text = new StringSlice(@"<http://google.com>");
|
||||
Assert.True(LinkHelper.TryParseAutolink(ref text, out string url, out bool isEmail));
|
||||
Assert.False(isEmail);
|
||||
Assert.AreEqual("http://google.com", url);
|
||||
}
|
||||
[Test]
|
||||
public void TestlLinkReferenceDefinitionInvalid()
|
||||
{
|
||||
var text = new StringSlice("[foo]: /url (title) x\n");
|
||||
Assert.False(LinkHelper.TryParseLinkReferenceDefinition(ref text, out _, out _, out _, out _, out _, out _));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAutoLinkEmailSimple()
|
||||
{
|
||||
var text = new StringSlice(@"<user@host.com>");
|
||||
Assert.True(LinkHelper.TryParseAutolink(ref text, out string email, out bool isEmail));
|
||||
Assert.True(isEmail);
|
||||
Assert.AreEqual("user@host.com", email);
|
||||
}
|
||||
[Test]
|
||||
public void TestAutoLinkUrlSimple()
|
||||
{
|
||||
var text = new StringSlice(@"<http://google.com>");
|
||||
Assert.True(LinkHelper.TryParseAutolink(ref text, out string url, out bool isEmail));
|
||||
Assert.False(isEmail);
|
||||
Assert.AreEqual("http://google.com", url);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAutolinkInvalid()
|
||||
{
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@""), out string text, out bool isEmail));
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@"<"), out text, out isEmail));
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@"<ab"), out text, out isEmail));
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@"<user@>"), out text, out isEmail));
|
||||
}
|
||||
[Test]
|
||||
public void TestAutoLinkEmailSimple()
|
||||
{
|
||||
var text = new StringSlice(@"<user@host.com>");
|
||||
Assert.True(LinkHelper.TryParseAutolink(ref text, out string email, out bool isEmail));
|
||||
Assert.True(isEmail);
|
||||
Assert.AreEqual("user@host.com", email);
|
||||
}
|
||||
|
||||
[TestCase("Header identifiers in HTML", "header-identifiers-in-html")]
|
||||
[TestCase("* Dogs*?--in *my* house?", "dogs-in-my-house")] // Not Pandoc equivalent: dogs--in...
|
||||
[TestCase("[HTML], [S5], or [RTF]?", "html-s5-or-rtf")]
|
||||
[TestCase("3. Applications", "applications")]
|
||||
[TestCase("33", "")]
|
||||
public void TestUrilizeNonAscii_Pandoc(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
|
||||
}
|
||||
[Test]
|
||||
public void TestAutolinkInvalid()
|
||||
{
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@""), out string text, out bool isEmail));
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@"<"), out text, out isEmail));
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@"<ab"), out text, out isEmail));
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@"<user@>"), out text, out isEmail));
|
||||
}
|
||||
|
||||
[TestCase("Header identifiers in HTML", "header-identifiers-in-html")]
|
||||
[TestCase("* Dogs*?--in *my* house?", "-dogs--in-my-house")]
|
||||
[TestCase("[HTML], [S5], or [RTF]?", "html-s5-or-rtf")]
|
||||
[TestCase("3. Applications", "3-applications")]
|
||||
[TestCase("33", "33")]
|
||||
public void TestUrilizeGfm(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.UrilizeAsGfm(input));
|
||||
}
|
||||
[TestCase("Header identifiers in HTML", "header-identifiers-in-html")]
|
||||
[TestCase("* Dogs*?--in *my* house?", "dogs-in-my-house")] // Not Pandoc equivalent: dogs--in...
|
||||
[TestCase("[HTML], [S5], or [RTF]?", "html-s5-or-rtf")]
|
||||
[TestCase("3. Applications", "applications")]
|
||||
[TestCase("33", "")]
|
||||
public void TestUrilizeNonAscii_Pandoc(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
|
||||
}
|
||||
|
||||
[TestCase("abc", "abc")]
|
||||
[TestCase("a-c", "a-c")]
|
||||
[TestCase("a c", "a-c")]
|
||||
[TestCase("a_c", "a_c")]
|
||||
[TestCase("a.c", "a.c")]
|
||||
[TestCase("a,c", "ac")]
|
||||
[TestCase("a--", "a")] // Not Pandoc-equivalent: a--
|
||||
[TestCase("a__", "a")] // Not Pandoc-equivalent: a__
|
||||
[TestCase("a..", "a")] // Not Pandoc-equivalent: a..
|
||||
[TestCase("a??", "a")]
|
||||
[TestCase("a ", "a")]
|
||||
[TestCase("a--d", "a-d")]
|
||||
[TestCase("a__d", "a_d")]
|
||||
[TestCase("a??d", "ad")]
|
||||
[TestCase("a d", "a-d")]
|
||||
[TestCase("a..d", "a.d")]
|
||||
[TestCase("-bc", "bc")]
|
||||
[TestCase("_bc", "bc")]
|
||||
[TestCase(" bc", "bc")]
|
||||
[TestCase("?bc", "bc")]
|
||||
[TestCase(".bc", "bc")]
|
||||
[TestCase("a-.-", "a")] // Not Pandoc equivalent: a-.-
|
||||
public void TestUrilizeOnlyAscii_Simple(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, true));
|
||||
}
|
||||
[TestCase("Header identifiers in HTML", "header-identifiers-in-html")]
|
||||
[TestCase("* Dogs*?--in *my* house?", "-dogs--in-my-house")]
|
||||
[TestCase("[HTML], [S5], or [RTF]?", "html-s5-or-rtf")]
|
||||
[TestCase("3. Applications", "3-applications")]
|
||||
[TestCase("33", "33")]
|
||||
public void TestUrilizeGfm(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.UrilizeAsGfm(input));
|
||||
}
|
||||
|
||||
[TestCase("bær", "br")]
|
||||
[TestCase("bør", "br")]
|
||||
[TestCase("bΘr", "br")]
|
||||
[TestCase("四五", "")]
|
||||
public void TestUrilizeOnlyAscii_NonAscii(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, true));
|
||||
}
|
||||
[TestCase("abc", "abc")]
|
||||
[TestCase("a-c", "a-c")]
|
||||
[TestCase("a c", "a-c")]
|
||||
[TestCase("a_c", "a_c")]
|
||||
[TestCase("a.c", "a.c")]
|
||||
[TestCase("a,c", "ac")]
|
||||
[TestCase("a--", "a")] // Not Pandoc-equivalent: a--
|
||||
[TestCase("a__", "a")] // Not Pandoc-equivalent: a__
|
||||
[TestCase("a..", "a")] // Not Pandoc-equivalent: a..
|
||||
[TestCase("a??", "a")]
|
||||
[TestCase("a ", "a")]
|
||||
[TestCase("a--d", "a-d")]
|
||||
[TestCase("a__d", "a_d")]
|
||||
[TestCase("a??d", "ad")]
|
||||
[TestCase("a d", "a-d")]
|
||||
[TestCase("a..d", "a.d")]
|
||||
[TestCase("-bc", "bc")]
|
||||
[TestCase("_bc", "bc")]
|
||||
[TestCase(" bc", "bc")]
|
||||
[TestCase("?bc", "bc")]
|
||||
[TestCase(".bc", "bc")]
|
||||
[TestCase("a-.-", "a")] // Not Pandoc equivalent: a-.-
|
||||
public void TestUrilizeOnlyAscii_Simple(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, true));
|
||||
}
|
||||
|
||||
[TestCase("bár", "bar")]
|
||||
[TestCase("àrrivé", "arrive")]
|
||||
public void TestUrilizeOnlyAscii_Normalization(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, true));
|
||||
}
|
||||
[TestCase("bær", "br")]
|
||||
[TestCase("bør", "br")]
|
||||
[TestCase("bΘr", "br")]
|
||||
[TestCase("四五", "")]
|
||||
public void TestUrilizeOnlyAscii_NonAscii(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, true));
|
||||
}
|
||||
|
||||
[TestCase("123", "")]
|
||||
[TestCase("1,-b", "b")]
|
||||
[TestCase("b1,-", "b1")] // Not Pandoc equivalent: b1-
|
||||
[TestCase("ab3", "ab3")]
|
||||
[TestCase("ab3de", "ab3de")]
|
||||
public void TestUrilizeOnlyAscii_Numeric(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, true));
|
||||
}
|
||||
[TestCase("bár", "bar")]
|
||||
[TestCase("àrrivé", "arrive")]
|
||||
public void TestUrilizeOnlyAscii_Normalization(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, true));
|
||||
}
|
||||
|
||||
[TestCase("一二三四五", "一二三四五")]
|
||||
[TestCase("一,-b", "一-b")]
|
||||
public void TestUrilizeNonAscii_NonAsciiNumeric(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
|
||||
}
|
||||
[TestCase("123", "")]
|
||||
[TestCase("1,-b", "b")]
|
||||
[TestCase("b1,-", "b1")] // Not Pandoc equivalent: b1-
|
||||
[TestCase("ab3", "ab3")]
|
||||
[TestCase("ab3de", "ab3de")]
|
||||
public void TestUrilizeOnlyAscii_Numeric(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, true));
|
||||
}
|
||||
|
||||
[TestCase("bær", "bær")]
|
||||
[TestCase("æ5el", "æ5el")]
|
||||
[TestCase("-æ5el", "æ5el")]
|
||||
[TestCase("-frø-", "frø")]
|
||||
[TestCase("-fr-ø", "fr-ø")]
|
||||
public void TestUrilizeNonAscii_Simple(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
|
||||
}
|
||||
[TestCase("一二三四五", "一二三四五")]
|
||||
[TestCase("一,-b", "一-b")]
|
||||
public void TestUrilizeNonAscii_NonAsciiNumeric(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
|
||||
}
|
||||
|
||||
// Just to be sure, test for characters expressly forbidden in URI fragments:
|
||||
[TestCase("b#r", "br")]
|
||||
[TestCase("b%r", "br")] // Invalid except as an escape character
|
||||
[TestCase("b^r", "br")]
|
||||
[TestCase("b[r", "br")]
|
||||
[TestCase("b]r", "br")]
|
||||
[TestCase("b{r", "br")]
|
||||
[TestCase("b}r", "br")]
|
||||
[TestCase("b<r", "br")]
|
||||
[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));
|
||||
}
|
||||
[TestCase("bær", "bær")]
|
||||
[TestCase("æ5el", "æ5el")]
|
||||
[TestCase("-æ5el", "æ5el")]
|
||||
[TestCase("-frø-", "frø")]
|
||||
[TestCase("-fr-ø", "fr-ø")]
|
||||
public void TestUrilizeNonAscii_Simple(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUnicodeInDomainNameOfLinkReferenceDefinition()
|
||||
{
|
||||
TestParser.TestSpec("[Foo]\n\n[Foo]: http://ünicode.com", "<p><a href=\"http://xn--nicode-2ya.com\">Foo</a></p>");
|
||||
}
|
||||
// Just to be sure, test for characters expressly forbidden in URI fragments:
|
||||
[TestCase("b#r", "br")]
|
||||
[TestCase("b%r", "br")] // Invalid except as an escape character
|
||||
[TestCase("b^r", "br")]
|
||||
[TestCase("b[r", "br")]
|
||||
[TestCase("b]r", "br")]
|
||||
[TestCase("b{r", "br")]
|
||||
[TestCase("b}r", "br")]
|
||||
[TestCase("b<r", "br")]
|
||||
[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));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUnicodeInDomainNameOfLinkReferenceDefinition()
|
||||
{
|
||||
TestParser.TestSpec("[Foo]\n\n[Foo]: http://ünicode.com", "<p><a href=\"http://xn--nicode-2ya.com\">Foo</a></p>");
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,40 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Renderers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestLinkRewriter
|
||||
{
|
||||
public class TestLinkRewriter
|
||||
[Test]
|
||||
public void ReplacesRelativeLinks()
|
||||
{
|
||||
[Test]
|
||||
public void ReplacesRelativeLinks()
|
||||
{
|
||||
TestSpec(s => "abc" + s, "Link: [hello](/relative.jpg)", "abc/relative.jpg");
|
||||
TestSpec(s => s + "xyz", "Link: [hello](relative.jpg)", "relative.jpgxyz");
|
||||
TestSpec(null, "Link: [hello](relative.jpg)", "relative.jpg");
|
||||
TestSpec(null, "Link: [hello](/relative.jpg)", "/relative.jpg");
|
||||
}
|
||||
TestSpec(s => "abc" + s, "Link: [hello](/relative.jpg)", "abc/relative.jpg");
|
||||
TestSpec(s => s + "xyz", "Link: [hello](relative.jpg)", "relative.jpgxyz");
|
||||
TestSpec(null, "Link: [hello](relative.jpg)", "relative.jpg");
|
||||
TestSpec(null, "Link: [hello](/relative.jpg)", "/relative.jpg");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReplacesRelativeImageSources()
|
||||
{
|
||||
TestSpec(s => "abc" + s, "Image: ", "abc/image.jpg");
|
||||
TestSpec(s => "abc" + s, "Image: ", "abcimage.jpg");
|
||||
TestSpec(null, "Image: ", "/image.jpg");
|
||||
}
|
||||
[Test]
|
||||
public void ReplacesRelativeImageSources()
|
||||
{
|
||||
TestSpec(s => "abc" + s, "Image: ", "abc/image.jpg");
|
||||
TestSpec(s => "abc" + s, "Image: ", "abcimage.jpg");
|
||||
TestSpec(null, "Image: ", "/image.jpg");
|
||||
}
|
||||
|
||||
public static void TestSpec(Func<string,string> linkRewriter, string markdown, string expectedLink)
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().Build();
|
||||
public static void TestSpec(Func<string,string> linkRewriter, string markdown, string expectedLink)
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().Build();
|
||||
|
||||
var writer = new StringWriter();
|
||||
var renderer = new HtmlRenderer(writer);
|
||||
renderer.LinkRewriter = linkRewriter;
|
||||
pipeline.Setup(renderer);
|
||||
var writer = new StringWriter();
|
||||
var renderer = new HtmlRenderer(writer);
|
||||
renderer.LinkRewriter = linkRewriter;
|
||||
pipeline.Setup(renderer);
|
||||
|
||||
var document = MarkdownParser.Parse(markdown, pipeline);
|
||||
renderer.Render(document);
|
||||
writer.Flush();
|
||||
var document = MarkdownParser.Parse(markdown, pipeline);
|
||||
renderer.Render(document);
|
||||
writer.Flush();
|
||||
|
||||
Assert.That(writer.ToString(), Contains.Substring("=\"" + expectedLink + "\""));
|
||||
}
|
||||
Assert.That(writer.ToString(), Contains.Substring("=\"" + expectedLink + "\""));
|
||||
}
|
||||
}
|
||||
@@ -1,217 +1,214 @@
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestMarkdigCoreApi
|
||||
{
|
||||
public class TestMarkdigCoreApi
|
||||
[Test]
|
||||
public void TestToHtml()
|
||||
{
|
||||
[Test]
|
||||
public void TestToHtml()
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string html = Markdown.ToHtml("This is a text with some *emphasis*");
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
string html = Markdown.ToHtml("This is a text with some *emphasis*");
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
|
||||
html = Markdown.ToHtml("This is a text with a https://link.tld/");
|
||||
Assert.AreNotEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
}
|
||||
html = Markdown.ToHtml("This is a text with a https://link.tld/");
|
||||
Assert.AreNotEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToHtmlWithPipeline()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string html = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
|
||||
html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
|
||||
Assert.AreNotEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToHtmlWithPipeline()
|
||||
pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.Build();
|
||||
string html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string html = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
[Test]
|
||||
public void TestToHtmlWithWriter()
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
|
||||
html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
|
||||
Assert.AreNotEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
}
|
||||
|
||||
pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
}
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_ = Markdown.ToHtml("This is a text with some *emphasis*", writer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToHtmlWithWriter()
|
||||
writer = new StringWriter();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_ = Markdown.ToHtml("This is a text with a https://link.tld/", writer, pipeline);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDocumentToHtmlWithWriter()
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse("This is a text with some *emphasis*");
|
||||
document.ToHtml(writer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
|
||||
writer = new StringWriter();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse("This is a text with a https://link.tld/", pipeline);
|
||||
document.ToHtml(writer, pipeline);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConvert()
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
var renderer = new HtmlRenderer(writer);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_ = Markdown.Convert("This is a text with some *emphasis*", renderer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
|
||||
writer = new StringWriter();
|
||||
renderer = new HtmlRenderer(writer);
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_ = Markdown.Convert("This is a text with a https://link.tld/", renderer, pipeline);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestParse()
|
||||
{
|
||||
const string markdown = "This is a text with some *emphasis*";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UsePreciseSourceLocation()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse(markdown, pipeline);
|
||||
|
||||
Assert.AreEqual(1, document.LineCount);
|
||||
Assert.AreEqual(markdown.Length, document.Span.Length);
|
||||
Assert.AreEqual(1, document.LineStartIndexes.Count);
|
||||
Assert.AreEqual(0, document.LineStartIndexes[0]);
|
||||
|
||||
Assert.AreEqual(1, document.Count);
|
||||
ParagraphBlock paragraph = document[0] as ParagraphBlock;
|
||||
Assert.NotNull(paragraph);
|
||||
Assert.AreEqual(markdown.Length, paragraph.Span.Length);
|
||||
LiteralInline literal = paragraph.Inline.FirstChild as LiteralInline;
|
||||
Assert.NotNull(literal);
|
||||
Assert.AreEqual("This is a text with some ", literal.ToString());
|
||||
EmphasisInline emphasis = literal.NextSibling as EmphasisInline;
|
||||
Assert.NotNull(emphasis);
|
||||
Assert.AreEqual("*emphasis*".Length, emphasis.Span.Length);
|
||||
LiteralInline emphasisLiteral = emphasis.FirstChild as LiteralInline;
|
||||
Assert.NotNull(emphasisLiteral);
|
||||
Assert.AreEqual("emphasis", emphasisLiteral.ToString());
|
||||
Assert.Null(emphasisLiteral.NextSibling);
|
||||
Assert.Null(emphasis.NextSibling);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormalize()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string normalized = Markdown.Normalize("Heading\n=======");
|
||||
Assert.AreEqual("# Heading", normalized);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormalizeWithWriter()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_ = Markdown.ToHtml("This is a text with some *emphasis*", writer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
|
||||
writer = new StringWriter();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_ = Markdown.ToHtml("This is a text with a https://link.tld/", writer, pipeline);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
_ = Markdown.Normalize("Heading\n=======", writer);
|
||||
string normalized = writer.ToString();
|
||||
Assert.AreEqual("# Heading", normalized);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDocumentToHtmlWithWriter()
|
||||
[Test]
|
||||
public void TestToPlainText()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string plainText = Markdown.ToPlainText("*Hello*, [world](http://example.com)!");
|
||||
Assert.AreEqual("Hello, world!\n", plainText);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToPlainTextWithWriter()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse("This is a text with some *emphasis*");
|
||||
document.ToHtml(writer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
|
||||
writer = new StringWriter();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse("This is a text with a https://link.tld/", pipeline);
|
||||
document.ToHtml(writer, pipeline);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConvert()
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
var renderer = new HtmlRenderer(writer);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_ = Markdown.Convert("This is a text with some *emphasis*", renderer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
|
||||
writer = new StringWriter();
|
||||
renderer = new HtmlRenderer(writer);
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
_ = Markdown.Convert("This is a text with a https://link.tld/", renderer, pipeline);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestParse()
|
||||
{
|
||||
const string markdown = "This is a text with some *emphasis*";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UsePreciseSourceLocation()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse(markdown, pipeline);
|
||||
|
||||
Assert.AreEqual(1, document.LineCount);
|
||||
Assert.AreEqual(markdown.Length, document.Span.Length);
|
||||
Assert.AreEqual(1, document.LineStartIndexes.Count);
|
||||
Assert.AreEqual(0, document.LineStartIndexes[0]);
|
||||
|
||||
Assert.AreEqual(1, document.Count);
|
||||
ParagraphBlock paragraph = document[0] as ParagraphBlock;
|
||||
Assert.NotNull(paragraph);
|
||||
Assert.AreEqual(markdown.Length, paragraph.Span.Length);
|
||||
LiteralInline literal = paragraph.Inline.FirstChild as LiteralInline;
|
||||
Assert.NotNull(literal);
|
||||
Assert.AreEqual("This is a text with some ", literal.ToString());
|
||||
EmphasisInline emphasis = literal.NextSibling as EmphasisInline;
|
||||
Assert.NotNull(emphasis);
|
||||
Assert.AreEqual("*emphasis*".Length, emphasis.Span.Length);
|
||||
LiteralInline emphasisLiteral = emphasis.FirstChild as LiteralInline;
|
||||
Assert.NotNull(emphasisLiteral);
|
||||
Assert.AreEqual("emphasis", emphasisLiteral.ToString());
|
||||
Assert.Null(emphasisLiteral.NextSibling);
|
||||
Assert.Null(emphasis.NextSibling);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormalize()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string normalized = Markdown.Normalize("Heading\n=======");
|
||||
Assert.AreEqual("# Heading", normalized);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormalizeWithWriter()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
|
||||
_ = Markdown.Normalize("Heading\n=======", writer);
|
||||
string normalized = writer.ToString();
|
||||
Assert.AreEqual("# Heading", normalized);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToPlainText()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
string plainText = Markdown.ToPlainText("*Hello*, [world](http://example.com)!");
|
||||
Assert.AreEqual("Hello, world!\n", plainText);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToPlainTextWithWriter()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
|
||||
_ = Markdown.ToPlainText("*Hello*, [world](http://example.com)!", writer);
|
||||
string plainText = writer.ToString();
|
||||
Assert.AreEqual("Hello, world!\n", plainText);
|
||||
}
|
||||
_ = Markdown.ToPlainText("*Hello*, [world](http://example.com)!", writer);
|
||||
string plainText = writer.ToString();
|
||||
Assert.AreEqual("Hello, world!\n", plainText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,104 +1,113 @@
|
||||
using Markdig.Extensions.MediaLinks;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Markdig.Tests
|
||||
using Markdig.Extensions.MediaLinks;
|
||||
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestMediaLinks
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestMediaLinks
|
||||
private MarkdownPipeline GetPipeline(MediaOptions options = null)
|
||||
{
|
||||
private MarkdownPipeline GetPipeline(MediaOptions options = null)
|
||||
return new MarkdownPipelineBuilder()
|
||||
.UseMediaLinks(options)
|
||||
.Build();
|
||||
}
|
||||
|
||||
private MarkdownPipeline GetPipelineWithBootstrap(MediaOptions options = null)
|
||||
{
|
||||
return new MarkdownPipelineBuilder()
|
||||
.UseBootstrap()
|
||||
.UseMediaLinks(options)
|
||||
.Build();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("", "<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"https://sample.com/video.mp4\"></source></video></p>\n")]
|
||||
[TestCase("", "<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"//sample.com/video.mp4\"></source></video></p>\n")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/6BUptHVuvyI\" class=\"youtubeshort\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
[TestCase("", "<p><iframe src=\"https://music.yandex.ru/iframe/#track/4402274/411845/\" class=\"yandex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n")]
|
||||
[TestCase("", "<p><iframe src=\"https://player.vimeo.com/video/8607834\" class=\"vimeo\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
[TestCase("", "<p><iframe src=\"https://ok.ru/videoembed/26870090463\" class=\"odnoklassniki\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
[TestCase("", "<p><iframe src=\"https://ok.ru/videoembed/26870090463\" class=\"odnoklassniki\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
public void TestBuiltInHosts(string markdown, string expected)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipeline());
|
||||
Assert.AreEqual(expected, html);
|
||||
}
|
||||
|
||||
[TestCase("",
|
||||
"<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"./video.mp4\"></source></video></p>\n")]
|
||||
[TestCase("",
|
||||
"<p><audio width=\"500\" controls=\"\"><source type=\"audio/mpeg\" src=\"./audio.mp3\"></source></audio></p>\n")]
|
||||
public void TestBuiltInHostsWithRelativePaths(string markdown, string expected)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipeline());
|
||||
Assert.AreEqual(expected, html);
|
||||
}
|
||||
|
||||
private class TestHostProvider : IHostProvider
|
||||
{
|
||||
public string Class { get; } = "regex";
|
||||
public bool AllowFullScreen { get; }
|
||||
|
||||
public bool TryHandle(Uri mediaUri, bool isSchemaRelative, out string iframeUrl)
|
||||
{
|
||||
return new MarkdownPipelineBuilder()
|
||||
.UseMediaLinks(options)
|
||||
.Build();
|
||||
iframeUrl = null;
|
||||
var uri = isSchemaRelative ? "//" + mediaUri.GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Scheme, UriFormat.UriEscaped) : mediaUri.ToString();
|
||||
if (!matcher.IsMatch(uri))
|
||||
return false;
|
||||
iframeUrl = matcher.Replace(uri, replacement);
|
||||
return true;
|
||||
}
|
||||
|
||||
private MarkdownPipeline GetPipelineWithBootstrap(MediaOptions options = null)
|
||||
private Regex matcher;
|
||||
private string replacement;
|
||||
|
||||
public TestHostProvider(string provider, string replace)
|
||||
{
|
||||
return new MarkdownPipelineBuilder()
|
||||
.UseBootstrap()
|
||||
.UseMediaLinks(options)
|
||||
.Build();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("", "<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"https://sample.com/video.mp4\"></source></video></p>\n")]
|
||||
[TestCase("", "<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"//sample.com/video.mp4\"></source></video></p>\n")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
[TestCase("", "<p><iframe src=\"https://music.yandex.ru/iframe/#track/4402274/411845/\" class=\"yandex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n")]
|
||||
[TestCase("", "<p><iframe src=\"https://player.vimeo.com/video/8607834\" class=\"vimeo\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
[TestCase("", "<p><iframe src=\"https://ok.ru/videoembed/26870090463\" class=\"odnoklassniki\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
[TestCase("", "<p><iframe src=\"https://ok.ru/videoembed/26870090463\" class=\"odnoklassniki\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
public void TestBuiltInHosts(string markdown, string expected)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipeline());
|
||||
Assert.AreEqual(html, expected);
|
||||
}
|
||||
|
||||
private class TestHostProvider : IHostProvider
|
||||
{
|
||||
public string Class { get; } = "regex";
|
||||
public bool AllowFullScreen { get; }
|
||||
|
||||
public bool TryHandle(Uri mediaUri, bool isSchemaRelative, out string iframeUrl)
|
||||
{
|
||||
iframeUrl = null;
|
||||
var uri = isSchemaRelative ? "//" + mediaUri.GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Scheme, UriFormat.UriEscaped) : mediaUri.ToString();
|
||||
if (!matcher.IsMatch(uri))
|
||||
return false;
|
||||
iframeUrl = matcher.Replace(uri, replacement);
|
||||
return true;
|
||||
}
|
||||
|
||||
private Regex matcher;
|
||||
private string replacement;
|
||||
|
||||
public TestHostProvider(string provider, string replace)
|
||||
{
|
||||
matcher = new Regex(provider);
|
||||
replacement = replace;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("", "<p><iframe src=\"https://example.com/video.mp4\" class=\"regex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n", @"^https?://sample.com/(.+)$", @"https://example.com/$1")]
|
||||
[TestCase("", "<p><iframe src=\"https://example.com/video.mp4\" class=\"regex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n", @"^//sample.com/(.+)$", @"https://example.com/$1")]
|
||||
[TestCase("", "<p><iframe src=\"https://example.com/video.mp4?token=aaabbb\" class=\"regex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n", @"^https?://sample.com/(.+)$", @"https://example.com/$1?token=aaabbb")]
|
||||
public void TestCustomHostProvider(string markdown, string expected, string provider, string replace)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipeline(new MediaOptions
|
||||
{
|
||||
Hosts =
|
||||
{
|
||||
new TestHostProvider(provider, replace),
|
||||
}
|
||||
}));
|
||||
Assert.AreEqual(html, expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("", "<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"//sample.com/video.mp4\"></source></video></p>\n", "")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n", "")]
|
||||
[TestCase("", "<p><video class=\"k\" width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"//sample.com/video.mp4\"></source></video></p>\n", "k")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"k youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n", "k")]
|
||||
public void TestCustomClass(string markdown, string expected, string klass)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipeline(new MediaOptions
|
||||
{
|
||||
Class = klass,
|
||||
}));
|
||||
Assert.AreEqual(html, expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("", "<p><video class=\"img-fluid\" width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"//sample.com/video.mp4\"></source></video></p>\n")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"img-fluid youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
public void TestWithBootstrap(string markdown, string expected)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipelineWithBootstrap());
|
||||
Assert.AreEqual(html, expected);
|
||||
matcher = new Regex(provider);
|
||||
replacement = replace;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("", "<p><iframe src=\"https://example.com/video.mp4\" class=\"regex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n", @"^https?://sample.com/(.+)$", @"https://example.com/$1")]
|
||||
[TestCase("", "<p><iframe src=\"https://example.com/video.mp4\" class=\"regex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n", @"^//sample.com/(.+)$", @"https://example.com/$1")]
|
||||
[TestCase("", "<p><iframe src=\"https://example.com/video.mp4?token=aaabbb\" class=\"regex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n", @"^https?://sample.com/(.+)$", @"https://example.com/$1?token=aaabbb")]
|
||||
public void TestCustomHostProvider(string markdown, string expected, string provider, string replace)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipeline(new MediaOptions
|
||||
{
|
||||
Hosts =
|
||||
{
|
||||
new TestHostProvider(provider, replace),
|
||||
}
|
||||
}));
|
||||
Assert.AreEqual(html, expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("", "<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"//sample.com/video.mp4\"></source></video></p>\n", "")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n", "")]
|
||||
[TestCase("", "<p><video class=\"k\" width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"//sample.com/video.mp4\"></source></video></p>\n", "k")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"k youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n", "k")]
|
||||
public void TestCustomClass(string markdown, string expected, string klass)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipeline(new MediaOptions
|
||||
{
|
||||
Class = klass,
|
||||
}));
|
||||
Assert.AreEqual(html, expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("", "<p><video class=\"img-fluid\" width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"//sample.com/video.mp4\"></source></video></p>\n")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"img-fluid youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
public void TestWithBootstrap(string markdown, string expected)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipelineWithBootstrap());
|
||||
Assert.AreEqual(html, expected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,28 @@
|
||||
using System.Linq;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestNewLine
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestNewLine
|
||||
[TestCase("a \nb", "<p>a<br />\nb</p>\n")]
|
||||
[TestCase("a\\\nb", "<p>a<br />\nb</p>\n")]
|
||||
[TestCase("a `b\nc`", "<p>a <code>b c</code></p>\n")]
|
||||
[TestCase("# Text A\nText B\n\n## Text C", "<h1>Text A</h1>\n<p>Text B</p>\n<h2>Text C</h2>\n")]
|
||||
public void Test(string value, string expectedHtml)
|
||||
{
|
||||
[TestCase("a \nb", "<p>a<br />\nb</p>\n")]
|
||||
[TestCase("a\\\nb", "<p>a<br />\nb</p>\n")]
|
||||
[TestCase("a `b\nc`", "<p>a <code>b c</code></p>\n")]
|
||||
[TestCase("# Text A\nText B\n\n## Text C", "<h1>Text A</h1>\n<p>Text B</p>\n<h2>Text C</h2>\n")]
|
||||
public void Test(string value, string expectedHtml)
|
||||
{
|
||||
Assert.AreEqual(expectedHtml, Markdown.ToHtml(value));
|
||||
Assert.AreEqual(expectedHtml, Markdown.ToHtml(value.Replace("\n", "\r\n")));
|
||||
}
|
||||
Assert.AreEqual(expectedHtml, Markdown.ToHtml(value));
|
||||
Assert.AreEqual(expectedHtml, Markdown.ToHtml(value.Replace("\n", "\r\n")));
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public void TestEscapeLineBreak()
|
||||
{
|
||||
var input = "test\\\r\ntest1\r\n";
|
||||
var doc = Markdown.Parse(input);
|
||||
var inlines = doc.Descendants<LineBreakInline>().ToList();
|
||||
Assert.AreEqual(1, inlines.Count, "Invalid number of LineBreakInline");
|
||||
Assert.True(inlines[0].IsBackslash);
|
||||
}
|
||||
[Test()]
|
||||
public void TestEscapeLineBreak()
|
||||
{
|
||||
var input = "test\\\r\ntest1\r\n";
|
||||
var doc = Markdown.Parse(input);
|
||||
var inlines = doc.Descendants<LineBreakInline>().ToList();
|
||||
Assert.AreEqual(1, inlines.Count, "Invalid number of LineBreakInline");
|
||||
Assert.True(inlines[0].IsBackslash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,180 +2,187 @@
|
||||
// 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.Helpers;
|
||||
using Markdig.Renderers.Normalize;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using System.IO;
|
||||
using Markdig.Renderers.Normalize;
|
||||
using Markdig.Helpers;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestNormalize
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestNormalize
|
||||
[Test]
|
||||
public void SyntaxCodeBlock()
|
||||
{
|
||||
[Test]
|
||||
public void SyntaxCodeBlock()
|
||||
AssertSyntax("````csharp\npublic void HelloWorld()\n{\n}\n````", new FencedCodeBlock(null)
|
||||
{
|
||||
AssertSyntax("````csharp\npublic void HelloWorld()\n{\n}\n````", new FencedCodeBlock(null)
|
||||
FencedChar = '`',
|
||||
OpeningFencedCharCount = 4,
|
||||
ClosingFencedCharCount = 4,
|
||||
Info = "csharp",
|
||||
Lines = new StringLineGroup(4)
|
||||
{
|
||||
FencedChar = '`',
|
||||
OpeningFencedCharCount = 4,
|
||||
ClosingFencedCharCount = 4,
|
||||
Info = "csharp",
|
||||
Lines = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("public void HelloWorld()"),
|
||||
new StringSlice("{"),
|
||||
new StringSlice("}"),
|
||||
}
|
||||
});
|
||||
new StringSlice("public void HelloWorld()"),
|
||||
new StringSlice("{"),
|
||||
new StringSlice("}"),
|
||||
}
|
||||
});
|
||||
|
||||
AssertSyntax(" public void HelloWorld()\n {\n }", new CodeBlock(null)
|
||||
AssertSyntax(" public void HelloWorld()\n {\n }", new CodeBlock(null)
|
||||
{
|
||||
Lines = new StringLineGroup(4)
|
||||
{
|
||||
Lines = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("public void HelloWorld()"),
|
||||
new StringSlice("{"),
|
||||
new StringSlice("}"),
|
||||
}
|
||||
});
|
||||
}
|
||||
new StringSlice("public void HelloWorld()"),
|
||||
new StringSlice("{"),
|
||||
new StringSlice("}"),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SyntaxHeadline()
|
||||
[Test]
|
||||
public void SyntaxHeadline()
|
||||
{
|
||||
AssertSyntax("## Headline", new HeadingBlock(null)
|
||||
{
|
||||
AssertSyntax("## Headline", new HeadingBlock(null)
|
||||
{
|
||||
HeaderChar = '#',
|
||||
Level = 2,
|
||||
Inline = new ContainerInline().AppendChild(new LiteralInline("Headline")),
|
||||
});
|
||||
}
|
||||
HeaderChar = '#',
|
||||
Level = 2,
|
||||
Inline = new ContainerInline().AppendChild(new LiteralInline("Headline")),
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SyntaxParagraph()
|
||||
[Test]
|
||||
public void SyntaxHeadlineLevel7()
|
||||
{
|
||||
AssertSyntax("####### Headline", new HeadingBlock(null) {
|
||||
HeaderChar = '#',
|
||||
Level = 7,
|
||||
Inline = new ContainerInline().AppendChild(new LiteralInline("Headline")),
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SyntaxParagraph()
|
||||
{
|
||||
AssertSyntax("This is a normal paragraph", new ParagraphBlock()
|
||||
{
|
||||
AssertSyntax("This is a normal paragraph", new ParagraphBlock()
|
||||
{
|
||||
Inline = new ContainerInline()
|
||||
.AppendChild(new LiteralInline("This is a normal paragraph")),
|
||||
});
|
||||
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()
|
||||
AssertSyntax("This is a\nnormal\nparagraph", new ParagraphBlock()
|
||||
{
|
||||
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````");
|
||||
}
|
||||
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 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 });
|
||||
[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````");
|
||||
}
|
||||
|
||||
AssertNormalizeNoTrim("Heading\n=======\n\ntext after two newlines", "# Heading\n\ntext after two newlines");
|
||||
}
|
||||
[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 });
|
||||
|
||||
[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");
|
||||
}
|
||||
AssertNormalizeNoTrim("Heading\n=======\n\ntext after two newlines", "# Heading\n\ntext after two newlines");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HtmlBlock()
|
||||
{
|
||||
/*AssertNormalizeNoTrim(@"<div id=""foo"" class=""bar
|
||||
baz"">
|
||||
[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
|
||||
[Test]
|
||||
public void Paragraph()
|
||||
{
|
||||
AssertNormalizeNoTrim("This is a plain paragraph");
|
||||
AssertNormalizeNoTrim(@"This
|
||||
is
|
||||
a
|
||||
plain
|
||||
paragraph");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParagraphMulti()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"line1
|
||||
[Test]
|
||||
public void ParagraphMulti()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"line1
|
||||
|
||||
line2
|
||||
|
||||
line3");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ListUnordered()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"- a
|
||||
[Test]
|
||||
public void ListUnordered()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"- a
|
||||
- b
|
||||
- c");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ListUnorderedLoose()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"- a
|
||||
[Test]
|
||||
public void ListUnorderedLoose()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"- a
|
||||
|
||||
- b
|
||||
|
||||
- c");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ListOrderedLooseAndCodeBlock()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"1. ```
|
||||
[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
|
||||
[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(
|
||||
[Test]
|
||||
public void ListUnorderedLooseMultiParagraph()
|
||||
{
|
||||
AssertNormalizeNoTrim(
|
||||
@"- a
|
||||
|
||||
And another paragraph a
|
||||
@@ -185,22 +192,22 @@ line3");
|
||||
And another paragraph b
|
||||
|
||||
- c");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void ListOrdered()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"1. a
|
||||
[Test]
|
||||
public void ListOrdered()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"1. a
|
||||
2. b
|
||||
3. c");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void ListOrderedAndIntended()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"1. a
|
||||
[Test]
|
||||
public void ListOrderedAndIntended()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"1. a
|
||||
2. b
|
||||
- foo
|
||||
- bar
|
||||
@@ -218,169 +225,169 @@ line3");
|
||||
- Bar
|
||||
11. c
|
||||
12. c");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HeaderAndParagraph()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"# heading
|
||||
[Test]
|
||||
public void HeaderAndParagraph()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"# heading
|
||||
|
||||
paragraph
|
||||
|
||||
paragraph2 without newlines");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void QuoteBlock()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"> test1
|
||||
[Test]
|
||||
public void QuoteBlock()
|
||||
{
|
||||
AssertNormalizeNoTrim(@"> test1
|
||||
>
|
||||
> test2");
|
||||
|
||||
AssertNormalizeNoTrim(@"> test1
|
||||
AssertNormalizeNoTrim(@"> test1
|
||||
This is a continuation
|
||||
> test2",
|
||||
@"> test1
|
||||
@"> test1
|
||||
> This is a continuation
|
||||
> test2"
|
||||
);
|
||||
|
||||
AssertNormalizeNoTrim(@"> test1
|
||||
AssertNormalizeNoTrim(@"> test1
|
||||
> -foobar
|
||||
|
||||
asdf
|
||||
|
||||
> test2
|
||||
> -foobar sen.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ThematicBreak()
|
||||
{
|
||||
AssertNormalizeNoTrim("***\n");
|
||||
[Test]
|
||||
public void ThematicBreak()
|
||||
{
|
||||
AssertNormalizeNoTrim("***\n");
|
||||
|
||||
AssertNormalizeNoTrim("* * *\n", "***\n");
|
||||
}
|
||||
AssertNormalizeNoTrim("* * *\n", "***\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AutolinkInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("This has a <auto.link.com>");
|
||||
}
|
||||
[Test]
|
||||
public void AutolinkInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("This has a <auto.link.com>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CodeInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("This has a ` ` in it");
|
||||
AssertNormalizeNoTrim("This has a `HelloWorld()` in it");
|
||||
AssertNormalizeNoTrim(@"This has a ``Hello`World()`` in it");
|
||||
AssertNormalizeNoTrim(@"This has a ``` Hello`World() ``` in it", @"This has a ``Hello`World()`` in it");
|
||||
AssertNormalizeNoTrim(@"This has a ``Hello`World()` `` in it");
|
||||
AssertNormalizeNoTrim(@"This has a ```` ``Hello```World()` ```` in it");
|
||||
AssertNormalizeNoTrim(@"This has a `` `Hello`World()`` in it");
|
||||
AssertNormalizeNoTrim(@"This has a ``` ``Hello`World()` ``` in it");
|
||||
}
|
||||
[Test]
|
||||
public void CodeInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("This has a ` ` in it");
|
||||
AssertNormalizeNoTrim("This has a `HelloWorld()` in it");
|
||||
AssertNormalizeNoTrim(@"This has a ``Hello`World()`` in it");
|
||||
AssertNormalizeNoTrim(@"This has a ``` Hello`World() ``` in it", @"This has a ``Hello`World()`` in it");
|
||||
AssertNormalizeNoTrim(@"This has a ``Hello`World()` `` in it");
|
||||
AssertNormalizeNoTrim(@"This has a ```` ``Hello```World()` ```` in it");
|
||||
AssertNormalizeNoTrim(@"This has a `` `Hello`World()`` in it");
|
||||
AssertNormalizeNoTrim(@"This has a ``` ``Hello`World()` ``` in it");
|
||||
}
|
||||
|
||||
[Test]
|
||||
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 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 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 ");
|
||||
[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"")");
|
||||
}
|
||||
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");
|
||||
[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("[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");
|
||||
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");
|
||||
// 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");
|
||||
}
|
||||
// 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 EscapeInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("This is an escape \\* with another \\[");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HtmlEntityInline()
|
||||
{
|
||||
AssertNormalizeNoTrim("This is a ä blank");
|
||||
}
|
||||
[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 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 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 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 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 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
|
||||
[Test]
|
||||
public void BiggerSample()
|
||||
{
|
||||
var input = @"# Heading 1
|
||||
|
||||
This is a paragraph
|
||||
|
||||
@@ -400,100 +407,99 @@ This is a code block
|
||||
line 2 of indented
|
||||
|
||||
This is a last line";
|
||||
AssertNormalizeNoTrim(input);
|
||||
}
|
||||
AssertNormalizeNoTrim(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TaskLists()
|
||||
[Test]
|
||||
public void TaskLists()
|
||||
{
|
||||
AssertNormalizeNoTrim("- [X] This is done");
|
||||
AssertNormalizeNoTrim("- [x] This is done",
|
||||
"- [X] This is done");
|
||||
AssertNormalizeNoTrim("- [ ] This is not done");
|
||||
|
||||
// ignore
|
||||
AssertNormalizeNoTrim("[x] This is not a task list");
|
||||
AssertNormalizeNoTrim("[ ] This is not a task list");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void JiraLinks()
|
||||
{
|
||||
AssertNormalizeNoTrim("FOO-1234");
|
||||
AssertNormalizeNoTrim("AB-1");
|
||||
|
||||
AssertNormalizeNoTrim("**Hello World AB-1**");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AutoLinks()
|
||||
{
|
||||
AssertNormalizeNoTrim("Hello from http://example.com/foo", "Hello from [http://example.com/foo](http://example.com/foo)", new NormalizeOptions() { ExpandAutoLinks = true, });
|
||||
AssertNormalizeNoTrim("Hello from www.example.com/foo", "Hello from [www.example.com/foo](http://www.example.com/foo)", new NormalizeOptions() { ExpandAutoLinks = true, });
|
||||
AssertNormalizeNoTrim("Hello from ftp://example.com", "Hello from [ftp://example.com](ftp://example.com)", new NormalizeOptions() { ExpandAutoLinks = true, });
|
||||
AssertNormalizeNoTrim("Hello from mailto:hello@example.com", "Hello from [hello@example.com](mailto:hello@example.com)", new NormalizeOptions() { ExpandAutoLinks = true, });
|
||||
|
||||
AssertNormalizeNoTrim("Hello from http://example.com/foo", "Hello from http://example.com/foo", new NormalizeOptions() { ExpandAutoLinks = false, });
|
||||
AssertNormalizeNoTrim("Hello from www.example.com/foo", "Hello from http://www.example.com/foo", new NormalizeOptions() { ExpandAutoLinks = false, });
|
||||
AssertNormalizeNoTrim("Hello from mailto:hello@example.com", "Hello from mailto:hello@example.com", new NormalizeOptions() { ExpandAutoLinks = false, });
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
AssertNormalizeNoTrim("- [X] This is done");
|
||||
AssertNormalizeNoTrim("- [x] This is done",
|
||||
"- [X] This is done");
|
||||
AssertNormalizeNoTrim("- [ ] This is not done");
|
||||
|
||||
// ignore
|
||||
AssertNormalizeNoTrim("[x] This is not a task list");
|
||||
AssertNormalizeNoTrim("[ ] This is not a task list");
|
||||
document.Add(syntax as Block);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void JiraLinks()
|
||||
else
|
||||
{
|
||||
AssertNormalizeNoTrim("FOO-1234");
|
||||
AssertNormalizeNoTrim("AB-1");
|
||||
|
||||
AssertNormalizeNoTrim("**Hello World AB-1**");
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
normalizer.Render(document);
|
||||
|
||||
[Test]
|
||||
public void AutoLinks()
|
||||
var actual = writer.ToString();
|
||||
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null, string context = null)
|
||||
{
|
||||
foreach (var pipeline in TestParser.GetPipeline(extensions))
|
||||
{
|
||||
AssertNormalizeNoTrim("Hello from http://example.com/foo", "Hello from [http://example.com/foo](http://example.com/foo)", new NormalizeOptions() { ExpandAutoLinks = true, });
|
||||
AssertNormalizeNoTrim("Hello from www.example.com/foo", "Hello from [www.example.com/foo](http://www.example.com/foo)", new NormalizeOptions() { ExpandAutoLinks = true, });
|
||||
AssertNormalizeNoTrim("Hello from ftp://example.com", "Hello from [ftp://example.com](ftp://example.com)", new NormalizeOptions() { ExpandAutoLinks = true, });
|
||||
AssertNormalizeNoTrim("Hello from mailto:hello@example.com", "Hello from [hello@example.com](mailto:hello@example.com)", new NormalizeOptions() { ExpandAutoLinks = true, });
|
||||
|
||||
AssertNormalizeNoTrim("Hello from http://example.com/foo", "Hello from http://example.com/foo", new NormalizeOptions() { ExpandAutoLinks = false, });
|
||||
AssertNormalizeNoTrim("Hello from www.example.com/foo", "Hello from http://www.example.com/foo", new NormalizeOptions() { ExpandAutoLinks = false, });
|
||||
AssertNormalizeNoTrim("Hello from mailto:hello@example.com", "Hello from mailto:hello@example.com", new NormalizeOptions() { ExpandAutoLinks = false, });
|
||||
}
|
||||
|
||||
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 static void TestSpec(string inputText, string expectedOutputText, string extensions = null, string context = null)
|
||||
{
|
||||
foreach (var pipeline in TestParser.GetPipeline(extensions))
|
||||
{
|
||||
AssertNormalize(inputText, expectedOutputText, trim: false, pipeline: pipeline.Value, context: context);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AssertNormalizeNoTrim(string input, string expected = null, NormalizeOptions options = null)
|
||||
=> AssertNormalize(input, expected, false, options);
|
||||
|
||||
public static void AssertNormalize(string input, string expected = null, bool trim = true, NormalizeOptions options = null, MarkdownPipeline pipeline = null, string context = null)
|
||||
{
|
||||
expected = expected ?? input;
|
||||
input = NormText(input, trim);
|
||||
expected = NormText(expected, trim);
|
||||
|
||||
pipeline = pipeline ?? new MarkdownPipelineBuilder()
|
||||
.UseAutoLinks()
|
||||
.UseJiraLinks(new Extensions.JiraLinks.JiraLinkOptions("https://jira.example.com"))
|
||||
.UseTaskLists()
|
||||
.Build();
|
||||
|
||||
var result = Markdown.Normalize(input, options, pipeline: pipeline);
|
||||
result = NormText(result, trim);
|
||||
|
||||
TestParser.PrintAssertExpected(input, result, expected, context);
|
||||
}
|
||||
|
||||
private static string NormText(string text, bool trim)
|
||||
{
|
||||
if (trim)
|
||||
{
|
||||
text = text.Trim();
|
||||
}
|
||||
return text.Replace("\r\n", "\n").Replace('\r', '\n');
|
||||
AssertNormalize(inputText, expectedOutputText, trim: false, pipeline: pipeline.Value, context: context);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AssertNormalizeNoTrim(string input, string expected = null, NormalizeOptions options = null)
|
||||
=> AssertNormalize(input, expected, false, options);
|
||||
|
||||
public static void AssertNormalize(string input, string expected = null, bool trim = true, NormalizeOptions options = null, MarkdownPipeline pipeline = null, string context = null)
|
||||
{
|
||||
expected = expected ?? input;
|
||||
input = NormText(input, trim);
|
||||
expected = NormText(expected, trim);
|
||||
|
||||
pipeline = pipeline ?? new MarkdownPipelineBuilder()
|
||||
.UseAutoLinks()
|
||||
.UseJiraLinks(new Extensions.JiraLinks.JiraLinkOptions("https://jira.example.com"))
|
||||
.UseTaskLists()
|
||||
.Build();
|
||||
|
||||
var result = Markdown.Normalize(input, options, pipeline: pipeline);
|
||||
result = NormText(result, trim);
|
||||
|
||||
TestParser.PrintAssertExpected(input, result, expected, context);
|
||||
}
|
||||
|
||||
private static string NormText(string text, bool trim)
|
||||
{
|
||||
if (trim)
|
||||
{
|
||||
text = text.Trim();
|
||||
}
|
||||
return text.Replace("\r\n", "\n").Replace('\r', '\n');
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,41 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestOrderedList
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestOrderedList
|
||||
[Test]
|
||||
public void TestReplace()
|
||||
{
|
||||
[Test]
|
||||
public void TestReplace()
|
||||
var list = new OrderedList<ITest>
|
||||
{
|
||||
var list = new OrderedList<ITest>
|
||||
{
|
||||
new A(),
|
||||
new B(),
|
||||
new C(),
|
||||
};
|
||||
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 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
|
||||
// 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,191 +1,196 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using Markdig.Extensions.JiraLinks;
|
||||
using Markdig.Renderers.Roundtrip;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestParser
|
||||
{
|
||||
public class TestParser
|
||||
[Test]
|
||||
public void EnsureSpecsAreUpToDate()
|
||||
{
|
||||
[Test]
|
||||
public void EnsureSpecsAreUpToDate()
|
||||
// In CI, SpecFileGen is guaranteed to run
|
||||
if (IsContinuousIntegration)
|
||||
return;
|
||||
|
||||
var specsFilePaths = Directory.GetDirectories(TestsDirectory)
|
||||
.Where(dir => dir.EndsWith("Specs"))
|
||||
.SelectMany(dir => Directory.GetFiles(dir)
|
||||
.Where(file => file.EndsWith(".md", StringComparison.OrdinalIgnoreCase))
|
||||
.Where(file => file.IndexOf("readme", StringComparison.OrdinalIgnoreCase) == -1))
|
||||
.ToArray();
|
||||
|
||||
var specsMarkdown = new string[specsFilePaths.Length];
|
||||
var specsSyntaxTrees = new MarkdownDocument[specsFilePaths.Length];
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < specsFilePaths.Length; i++)
|
||||
{
|
||||
// In CI, SpecFileGen is guaranteed to run
|
||||
if (IsContinuousIntegration)
|
||||
return;
|
||||
|
||||
var specsFilePaths = Directory.GetDirectories(TestsDirectory)
|
||||
.Where(dir => dir.EndsWith("Specs"))
|
||||
.SelectMany(dir => Directory.GetFiles(dir)
|
||||
.Where(file => file.EndsWith(".md", StringComparison.OrdinalIgnoreCase))
|
||||
.Where(file => file.IndexOf("readme", StringComparison.OrdinalIgnoreCase) == -1))
|
||||
.ToArray();
|
||||
|
||||
var specsMarkdown = new string[specsFilePaths.Length];
|
||||
var specsSyntaxTrees = new MarkdownDocument[specsFilePaths.Length];
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < specsFilePaths.Length; i++)
|
||||
{
|
||||
string markdown = specsMarkdown[i] = File.ReadAllText(specsFilePaths[i]);
|
||||
specsSyntaxTrees[i] = Markdown.Parse(markdown, pipeline);
|
||||
}
|
||||
|
||||
foreach (var specFilePath in specsFilePaths)
|
||||
{
|
||||
string testFilePath = Path.ChangeExtension(specFilePath, ".generated.cs");
|
||||
|
||||
Assert.True(File.Exists(testFilePath),
|
||||
"A new specification file has been added. Add the spec to the list in SpecFileGen and regenerate the tests.");
|
||||
|
||||
DateTime specTime = File.GetLastWriteTimeUtc(specFilePath);
|
||||
DateTime testTime = File.GetLastWriteTimeUtc(testFilePath);
|
||||
|
||||
// If file creation times aren't preserved by git, add some leeway
|
||||
// If specs have come from git, assume that they were regenerated since CI would fail otherwise
|
||||
testTime = testTime.AddMinutes(3);
|
||||
|
||||
// This might not catch a changed spec every time, but should at least sometimes. Otherwise CI will catch it
|
||||
|
||||
// This could also trigger, if a user has modified the spec file but reverted the change - can't think of a good workaround
|
||||
Assert.Less(specTime, testTime,
|
||||
$"{Path.GetFileName(specFilePath)} has been modified. Run SpecFileGen to regenerate the tests. " +
|
||||
"If you have modified a specification file, but reverted all changes, ignore this error or revert the 'changed' timestamp metadata on the file.");
|
||||
}
|
||||
|
||||
TestDescendantsOrder.TestSchemas(specsSyntaxTrees);
|
||||
string markdown = specsMarkdown[i] = File.ReadAllText(specsFilePaths[i]);
|
||||
specsSyntaxTrees[i] = Markdown.Parse(markdown, pipeline);
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null, bool plainText = false, string context = null)
|
||||
foreach (var specFilePath in specsFilePaths)
|
||||
{
|
||||
context ??= string.Empty;
|
||||
if (!string.IsNullOrEmpty(context))
|
||||
{
|
||||
context += "\n";
|
||||
}
|
||||
foreach (var pipeline in GetPipeline(extensions))
|
||||
{
|
||||
TestSpec(inputText, expectedOutputText, pipeline.Value, plainText, context: context + $"Pipeline configured with extensions: {pipeline.Key}");
|
||||
}
|
||||
string testFilePath = Path.ChangeExtension(specFilePath, ".generated.cs");
|
||||
|
||||
Assert.True(File.Exists(testFilePath),
|
||||
"A new specification file has been added. Add the spec to the list in SpecFileGen and regenerate the tests.");
|
||||
|
||||
DateTime specTime = File.GetLastWriteTimeUtc(specFilePath);
|
||||
DateTime testTime = File.GetLastWriteTimeUtc(testFilePath);
|
||||
|
||||
// If file creation times aren't preserved by git, add some leeway
|
||||
// If specs have come from git, assume that they were regenerated since CI would fail otherwise
|
||||
testTime = testTime.AddMinutes(3);
|
||||
|
||||
// This might not catch a changed spec every time, but should at least sometimes. Otherwise CI will catch it
|
||||
|
||||
// This could also trigger, if a user has modified the spec file but reverted the change - can't think of a good workaround
|
||||
Assert.Less(specTime, testTime,
|
||||
$"{Path.GetFileName(specFilePath)} has been modified. Run SpecFileGen to regenerate the tests. " +
|
||||
"If you have modified a specification file, but reverted all changes, ignore this error or revert the 'changed' timestamp metadata on the file.");
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, MarkdownPipeline pipeline, bool plainText = false, string context = null)
|
||||
TestDescendantsOrder.TestSchemas(specsSyntaxTrees);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParseEmptyDocumentWithTrackTriviaEnabled()
|
||||
{
|
||||
var document = Markdown.Parse("", trackTrivia: true);
|
||||
using var sw = new StringWriter();
|
||||
new RoundtripRenderer(sw).Render(document);
|
||||
Assert.AreEqual("", sw.ToString());
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null, bool plainText = false, string context = null)
|
||||
{
|
||||
context ??= string.Empty;
|
||||
if (!string.IsNullOrEmpty(context))
|
||||
{
|
||||
// Uncomment this line to get more debug information for process inlines.
|
||||
//pipeline.DebugLog = Console.Out;
|
||||
var result = plainText ? Markdown.ToPlainText(inputText, pipeline) : Markdown.ToHtml(inputText, pipeline);
|
||||
|
||||
result = Compact(result);
|
||||
expectedOutputText = Compact(expectedOutputText);
|
||||
|
||||
PrintAssertExpected(inputText, result, expectedOutputText, context);
|
||||
context += "\n";
|
||||
}
|
||||
|
||||
public static void PrintAssertExpected(string source, string result, string expected, string context = null)
|
||||
foreach (var pipeline in GetPipeline(extensions))
|
||||
{
|
||||
if (expected != result)
|
||||
TestSpec(inputText, expectedOutputText, pipeline.Value, plainText, context: context + $"Pipeline configured with extensions: {pipeline.Key}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, MarkdownPipeline pipeline, bool plainText = false, string context = null)
|
||||
{
|
||||
// Uncomment this line to get more debug information for process inlines.
|
||||
//pipeline.DebugLog = Console.Out;
|
||||
var result = plainText ? Markdown.ToPlainText(inputText, pipeline) : Markdown.ToHtml(inputText, pipeline);
|
||||
|
||||
result = Compact(result);
|
||||
expectedOutputText = Compact(expectedOutputText);
|
||||
|
||||
PrintAssertExpected(inputText, result, expectedOutputText, context);
|
||||
}
|
||||
|
||||
public static void PrintAssertExpected(string source, string result, string expected, string context = null)
|
||||
{
|
||||
if (expected != result)
|
||||
{
|
||||
if (context != null)
|
||||
{
|
||||
if (context != null)
|
||||
{
|
||||
Console.WriteLine(context);
|
||||
}
|
||||
Console.WriteLine("```````````````````Source");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(source));
|
||||
Console.WriteLine("```````````````````Result");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(result));
|
||||
Console.WriteLine("```````````````````Expected");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(expected));
|
||||
Console.WriteLine("```````````````````");
|
||||
Console.WriteLine();
|
||||
TextAssert.AreEqual(expected, result);
|
||||
Console.WriteLine(context);
|
||||
}
|
||||
Console.WriteLine("```````````````````Source");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(source));
|
||||
Console.WriteLine("```````````````````Result");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(result));
|
||||
Console.WriteLine("```````````````````Expected");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(expected));
|
||||
Console.WriteLine("```````````````````");
|
||||
Console.WriteLine();
|
||||
TextAssert.AreEqual(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<KeyValuePair<string, MarkdownPipeline>> GetPipeline(string extensionsGroupText)
|
||||
{
|
||||
// For the standard case, we make sure that both the CommmonMark core and Extra/Advanced are CommonMark compliant!
|
||||
if (string.IsNullOrEmpty(extensionsGroupText))
|
||||
{
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>("default", new MarkdownPipelineBuilder().Build());
|
||||
|
||||
//yield return new KeyValuePair<string, MarkdownPipeline>("default-trivia", new MarkdownPipelineBuilder().EnableTrackTrivia().Build());
|
||||
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>("advanced", new MarkdownPipelineBuilder() // Use similar to advanced extension without auto-identifier
|
||||
.UseAbbreviations()
|
||||
//.UseAutoIdentifiers()
|
||||
.UseCitations()
|
||||
.UseCustomContainers()
|
||||
.UseDefinitionLists()
|
||||
.UseEmphasisExtras()
|
||||
.UseFigures()
|
||||
.UseFooters()
|
||||
.UseFootnotes()
|
||||
.UseGridTables()
|
||||
.UseMathematics()
|
||||
.UseMediaLinks()
|
||||
.UsePipeTables()
|
||||
.UseListExtras()
|
||||
.UseGenericAttributes().Build());
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
public static IEnumerable<KeyValuePair<string, MarkdownPipeline>> GetPipeline(string extensionsGroupText)
|
||||
var extensionGroups = extensionsGroupText.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var extensionsText in extensionGroups)
|
||||
{
|
||||
// For the standard case, we make sure that both the CommmonMark core and Extra/Advanced are CommonMark compliant!
|
||||
if (string.IsNullOrEmpty(extensionsGroupText))
|
||||
var builder = new MarkdownPipelineBuilder();
|
||||
builder.DebugLog = Console.Out;
|
||||
if (extensionsText == "jiralinks")
|
||||
{
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>("default", new MarkdownPipelineBuilder().Build());
|
||||
|
||||
//yield return new KeyValuePair<string, MarkdownPipeline>("default-trivia", new MarkdownPipelineBuilder().EnableTrackTrivia().Build());
|
||||
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>("advanced", new MarkdownPipelineBuilder() // Use similar to advanced extension without auto-identifier
|
||||
.UseAbbreviations()
|
||||
//.UseAutoIdentifiers()
|
||||
.UseCitations()
|
||||
.UseCustomContainers()
|
||||
.UseDefinitionLists()
|
||||
.UseEmphasisExtras()
|
||||
.UseFigures()
|
||||
.UseFooters()
|
||||
.UseFootnotes()
|
||||
.UseGridTables()
|
||||
.UseMathematics()
|
||||
.UseMediaLinks()
|
||||
.UsePipeTables()
|
||||
.UseListExtras()
|
||||
.UseGenericAttributes().Build());
|
||||
|
||||
yield break;
|
||||
builder.UseJiraLinks(new JiraLinkOptions("http://your.company.abc"));
|
||||
}
|
||||
|
||||
var extensionGroups = extensionsGroupText.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var extensionsText in extensionGroups)
|
||||
else
|
||||
{
|
||||
var builder = new MarkdownPipelineBuilder();
|
||||
builder.DebugLog = Console.Out;
|
||||
if (extensionsText == "jiralinks")
|
||||
{
|
||||
builder.UseJiraLinks(new JiraLinkOptions("http://your.company.abc"));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder = extensionsText == "self" ? builder.UseSelfPipeline() : builder.Configure(extensionsText);
|
||||
}
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>(extensionsText, builder.Build());
|
||||
builder = extensionsText == "self" ? builder.UseSelfPipeline() : builder.Configure(extensionsText);
|
||||
}
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>(extensionsText, builder.Build());
|
||||
}
|
||||
}
|
||||
|
||||
public static string DisplaySpaceAndTabs(string text)
|
||||
public static string DisplaySpaceAndTabs(string text)
|
||||
{
|
||||
// Output special characters to check correctly the results
|
||||
return text.Replace('\t', '→').Replace(' ', '·');
|
||||
}
|
||||
|
||||
private static string Compact(string html)
|
||||
{
|
||||
// Normalize the output to make it compatible with CommonMark specs
|
||||
html = html.Replace("\r\n", "\n").Replace(@"\r", @"\n").Trim();
|
||||
html = Regex.Replace(html, @"\s+</li>", "</li>");
|
||||
html = Regex.Replace(html, @"<li>\s+", "<li>");
|
||||
html = html.Normalize(NormalizationForm.FormKD);
|
||||
return html;
|
||||
}
|
||||
|
||||
public static readonly bool IsContinuousIntegration = Environment.GetEnvironmentVariable("CI") != null;
|
||||
|
||||
public static readonly string TestsDirectory = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(typeof(TestParser).Assembly.Location), "../../.."));
|
||||
|
||||
static TestParser()
|
||||
{
|
||||
const string RunningInsideVisualStudioPath = "\\src\\.vs\\markdig\\";
|
||||
int index = TestsDirectory.IndexOf(RunningInsideVisualStudioPath);
|
||||
if (index != -1)
|
||||
{
|
||||
// Output special characters to check correctly the results
|
||||
return text.Replace('\t', '→').Replace(' ', '·');
|
||||
}
|
||||
|
||||
private static string Compact(string html)
|
||||
{
|
||||
// Normalize the output to make it compatible with CommonMark specs
|
||||
html = html.Replace("\r\n", "\n").Replace(@"\r", @"\n").Trim();
|
||||
html = Regex.Replace(html, @"\s+</li>", "</li>");
|
||||
html = Regex.Replace(html, @"<li>\s+", "<li>");
|
||||
html = html.Normalize(NormalizationForm.FormKD);
|
||||
return html;
|
||||
}
|
||||
|
||||
public static readonly bool IsContinuousIntegration = Environment.GetEnvironmentVariable("CI") != null;
|
||||
|
||||
public static readonly string TestsDirectory = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(typeof(TestParser).Assembly.Location), "../../.."));
|
||||
|
||||
static TestParser()
|
||||
{
|
||||
const string RunningInsideVisualStudioPath = "\\src\\.vs\\markdig\\";
|
||||
int index = TestsDirectory.IndexOf(RunningInsideVisualStudioPath);
|
||||
if (index != -1)
|
||||
{
|
||||
TestsDirectory = TestsDirectory.Substring(0, index) + "\\src\\Markdig.Tests";
|
||||
}
|
||||
TestsDirectory = TestsDirectory.Substring(0, index) + "\\src\\Markdig.Tests";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,58 @@
|
||||
using Markdig.Extensions.Tables;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
using System.Linq;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class TestPipeTable
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class TestPipeTable
|
||||
[TestCase("| S | T |\r\n|---|---| \r\n| G | H |")]
|
||||
[TestCase("| S | T |\r\n|---|---|\t\r\n| G | H |")]
|
||||
[TestCase("| S | T |\r\n|---|---|\f\r\n| G | H |")]
|
||||
[TestCase("| S | \r\n|---|\r\n| G |\r\n\r\n| D | D |\r\n| ---| ---| \r\n| V | V |", 2)]
|
||||
public void TestTableBug(string markdown, int tableCount = 1)
|
||||
{
|
||||
[TestCase("| S | T |\r\n|---|---| \r\n| G | H |")]
|
||||
[TestCase("| S | T |\r\n|---|---|\t\r\n| G | H |")]
|
||||
[TestCase("| S | T |\r\n|---|---|\v\r\n| G | H |")]
|
||||
[TestCase("| S | T |\r\n|---|---|\f\r\n| G | H |")]
|
||||
[TestCase("| S | T |\r\n|---|---|\f\v\t \r\n| G | H |")]
|
||||
[TestCase("| S | \r\n|---|\r\n| G |\r\n\r\n| D | D |\r\n| ---| ---| \r\n| V | V |", 2)]
|
||||
public void TestTableBug(string markdown, int tableCount = 1)
|
||||
MarkdownDocument document =
|
||||
Markdown.Parse(markdown, new MarkdownPipelineBuilder().UseAdvancedExtensions().Build());
|
||||
|
||||
Table[] tables = document.Descendants().OfType<Table>().ToArray();
|
||||
|
||||
Assert.AreEqual(tableCount, tables.Length);
|
||||
}
|
||||
|
||||
[TestCase("A | B\r\n---|---", new[] {50.0f, 50.0f})]
|
||||
[TestCase("A | B\r\n-|---", new[] {25.0f, 75.0f})]
|
||||
[TestCase("A | B\r\n-|---\r\nA | B\r\n---|---", new[] {25.0f, 75.0f})]
|
||||
[TestCase("A | B\r\n---|---|---", new[] {33.33f, 33.33f, 33.33f})]
|
||||
[TestCase("A | B\r\n---|---|---|", new[] {33.33f, 33.33f, 33.33f})]
|
||||
public void TestColumnWidthByHeaderLines(string markdown, float[] expectedWidth)
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UsePipeTables(new PipeTableOptions() {InferColumnWidthsFromSeparator = true})
|
||||
.Build();
|
||||
var document = Markdown.Parse(markdown, pipeline);
|
||||
var table = document.Descendants().OfType<Table>().FirstOrDefault();
|
||||
Assert.IsNotNull(table);
|
||||
var actualWidths = table.ColumnDefinitions.Select(x => x.Width).ToList();
|
||||
Assert.AreEqual(actualWidths.Count, expectedWidth.Length);
|
||||
for (int i = 0; i < expectedWidth.Length; i++)
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse(markdown, new MarkdownPipelineBuilder().UseAdvancedExtensions().Build());
|
||||
Assert.AreEqual(actualWidths[i], expectedWidth[i], 0.01);
|
||||
}
|
||||
}
|
||||
|
||||
Table[] tables = document.Descendants().OfType<Table>().ToArray();
|
||||
|
||||
Assert.AreEqual(tableCount, tables.Length);
|
||||
[Test]
|
||||
public void TestColumnWidthIsNotSetWithoutConfigurationFlag()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UsePipeTables(new PipeTableOptions() {InferColumnWidthsFromSeparator = false})
|
||||
.Build();
|
||||
var document = Markdown.Parse("| A | B | C |\r\n|---|---|---|", pipeline);
|
||||
var table = document.Descendants().OfType<Table>().FirstOrDefault();
|
||||
Assert.IsNotNull(table);
|
||||
foreach (var column in table.ColumnDefinitions)
|
||||
{
|
||||
Assert.AreEqual(0, column.Width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,42 @@
|
||||
using NUnit.Framework;
|
||||
namespace Markdig.Tests;
|
||||
|
||||
namespace Markdig.Tests
|
||||
[TestFixture]
|
||||
public class TestPlainText
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestPlainText
|
||||
[Test]
|
||||
[TestCase(/* markdownText: */ "foo bar", /* expected: */ "foo bar\n")]
|
||||
[TestCase(/* markdownText: */ "foo\nbar", /* expected: */ "foo\nbar\n")]
|
||||
[TestCase(/* markdownText: */ "*foo\nbar*", /* expected: */ "foo\nbar\n")]
|
||||
[TestCase(/* markdownText: */ "[foo\nbar](http://example.com)", /* expected: */ "foo\nbar\n")]
|
||||
[TestCase(/* markdownText: */ "<http://foo.bar.baz>", /* expected: */ "http://foo.bar.baz\n")]
|
||||
[TestCase(/* markdownText: */ "# foo bar", /* expected: */ "foo bar\n")]
|
||||
[TestCase(/* markdownText: */ "# foo\nbar", /* expected: */ "foo\nbar\n")]
|
||||
[TestCase(/* markdownText: */ "> foo", /* expected: */ "foo\n")]
|
||||
[TestCase(/* markdownText: */ "> foo\nbar\n> baz", /* expected: */ "foo\nbar\nbaz\n")]
|
||||
[TestCase(/* markdownText: */ "`foo`", /* expected: */ "foo\n")]
|
||||
[TestCase(/* markdownText: */ "`foo\nbar`", /* expected: */ "foo bar\n")] // new line within codespan is treated as whitespace (Example317)
|
||||
[TestCase(/* markdownText: */ "```\nfoo bar\n```", /* expected: */ "foo bar\n")]
|
||||
[TestCase(/* markdownText: */ "- foo\n- bar\n- baz", /* expected: */ "foo\nbar\nbaz\n")]
|
||||
[TestCase(/* markdownText: */ "- foo<baz", /* expected: */ "foo<baz\n")]
|
||||
[TestCase(/* markdownText: */ "- foo<baz", /* expected: */ "foo<baz\n")]
|
||||
[TestCase(/* markdownText: */ "## foo `bar::baz >`", /* expected: */ "foo bar::baz >\n")]
|
||||
public void TestPlainEnsureNewLine(string markdownText, string expected)
|
||||
{
|
||||
[Test]
|
||||
[TestCase(/* markdownText: */ "foo bar", /* expected: */ "foo bar\n")]
|
||||
[TestCase(/* markdownText: */ "foo\nbar", /* expected: */ "foo\nbar\n")]
|
||||
[TestCase(/* markdownText: */ "*foo\nbar*", /* expected: */ "foo\nbar\n")]
|
||||
[TestCase(/* markdownText: */ "[foo\nbar](http://example.com)", /* expected: */ "foo\nbar\n")]
|
||||
[TestCase(/* markdownText: */ "<http://foo.bar.baz>", /* expected: */ "http://foo.bar.baz\n")]
|
||||
[TestCase(/* markdownText: */ "# foo bar", /* expected: */ "foo bar\n")]
|
||||
[TestCase(/* markdownText: */ "# foo\nbar", /* expected: */ "foo\nbar\n")]
|
||||
[TestCase(/* markdownText: */ "> foo", /* expected: */ "foo\n")]
|
||||
[TestCase(/* markdownText: */ "> foo\nbar\n> baz", /* expected: */ "foo\nbar\nbaz\n")]
|
||||
[TestCase(/* markdownText: */ "`foo`", /* expected: */ "foo\n")]
|
||||
[TestCase(/* markdownText: */ "`foo\nbar`", /* expected: */ "foo bar\n")] // new line within codespan is treated as whitespace (Example317)
|
||||
[TestCase(/* markdownText: */ "```\nfoo bar\n```", /* expected: */ "foo bar\n")]
|
||||
[TestCase(/* markdownText: */ "- foo\n- bar\n- baz", /* expected: */ "foo\nbar\nbaz\n")]
|
||||
[TestCase(/* markdownText: */ "- foo<baz", /* expected: */ "foo<baz\n")]
|
||||
[TestCase(/* markdownText: */ "- foo<baz", /* expected: */ "foo<baz\n")]
|
||||
[TestCase(/* markdownText: */ "## foo `bar::baz >`", /* expected: */ "foo bar::baz >\n")]
|
||||
public void TestPlainEnsureNewLine(string markdownText, string expected)
|
||||
{
|
||||
var actual = Markdown.ToPlainText(markdownText);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
var actual = Markdown.ToPlainText(markdownText);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(/* markdownText: */ ":::\nfoo\n:::", /* expected: */ "foo\n", /*extensions*/ "customcontainers|advanced")]
|
||||
[TestCase(/* markdownText: */ ":::bar\nfoo\n:::", /* expected: */ "foo\n", /*extensions*/ "customcontainers+attributes|advanced")]
|
||||
[TestCase(/* markdownText: */ "| Header1 | Header2 | Header3 |\n|--|--|--|\nt**es**t|value2|value3", /* expected: */ "Header1 Header2 Header3 test value2 value3","pipetables")]
|
||||
public void TestPlainWithExtensions(string markdownText, string expected, string extensions)
|
||||
{
|
||||
TestParser.TestSpec(markdownText, expected, extensions, plainText: true);
|
||||
}
|
||||
[Test]
|
||||
[TestCase(/* markdownText: */ ":::\nfoo\n:::", /* expected: */ "foo\n", /*extensions*/ "customcontainers|advanced")]
|
||||
[TestCase(/* markdownText: */ ":::bar\nfoo\n:::", /* expected: */ "foo\n", /*extensions*/ "customcontainers+attributes|advanced")]
|
||||
[TestCase(/* markdownText: */ "| Header1 | Header2 | Header3 |\n|--|--|--|\nt**es**t|value2|value3", /* expected: */ "Header1 Header2 Header3 test value2 value3","pipetables")]
|
||||
public void TestPlainWithExtensions(string markdownText, string expected, string extensions)
|
||||
{
|
||||
TestParser.TestSpec(markdownText, expected, extensions, plainText: true);
|
||||
}
|
||||
|
||||
public static void TestSpec(string markdownText, string expected, string extensions, string context = null)
|
||||
{
|
||||
TestParser.TestSpec(markdownText, expected, extensions, plainText: true, context: context);
|
||||
}
|
||||
public static void TestSpec(string markdownText, string expected, string extensions, string context = null)
|
||||
{
|
||||
TestParser.TestSpec(markdownText, expected, extensions, plainText: true, context: context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +1,109 @@
|
||||
// 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.Linq;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestPlayParser
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestPlayParser
|
||||
|
||||
[Test]
|
||||
public void TestInvalidSetext()
|
||||
{
|
||||
[Test]
|
||||
public void TestBugWithEmphasisAndTable()
|
||||
{
|
||||
TestParser.TestSpec("**basics | 8:00**", "<p><strong>basics | 8:00</strong></p>", "advanced");
|
||||
}
|
||||
TestParser.TestSpec("test\n===n", "<p>test\n===n</p>", "advanced");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinksWithCarriageReturn()
|
||||
{
|
||||
var text = "[Link 1][link-1], [link 2][link-2].\r\n\r\n[link-1]: https://example.com\r\n[link-2]: https://example.com";
|
||||
var result = Markdown.ToHtml(text).TrimEnd();
|
||||
Assert.AreEqual("<p><a href=\"https://example.com\">Link 1</a>, <a href=\"https://example.com\">link 2</a>.</p>", result);
|
||||
}
|
||||
[Test]
|
||||
public void TestBugWithEmphasisAndTable()
|
||||
{
|
||||
TestParser.TestSpec("**basics | 8:00**", "<p><strong>basics | 8:00</strong></p>", "advanced");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinksWithTitleAndCarriageReturn()
|
||||
{
|
||||
var text = "[Link 1][link-1], [link 2][link-2].\r\n\r\n[link-1]: https://example.com \"title 1\" \r\n[link-2]: https://example.com \"title 2\"";
|
||||
var result = Markdown.ToHtml(text).TrimEnd();
|
||||
Assert.AreEqual("<p><a href=\"https://example.com\" title=\"title 1\">Link 1</a>, <a href=\"https://example.com\" title=\"title 2\">link 2</a>.</p>", result);
|
||||
}
|
||||
[Test]
|
||||
public void TestLinksWithCarriageReturn()
|
||||
{
|
||||
var text = "[Link 1][link-1], [link 2][link-2].\r\n\r\n[link-1]: https://example.com\r\n[link-2]: https://example.com";
|
||||
var result = Markdown.ToHtml(text).TrimEnd();
|
||||
Assert.AreEqual("<p><a href=\"https://example.com\">Link 1</a>, <a href=\"https://example.com\">link 2</a>.</p>", result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLink()
|
||||
{
|
||||
var doc = Markdown.Parse("There is a ");
|
||||
var link = doc.Descendants<ParagraphBlock>().SelectMany(x => x.Inline.Descendants<LinkInline>()).FirstOrDefault(l => l.IsImage);
|
||||
Assert.AreEqual("/yoyo", link?.Url);
|
||||
}
|
||||
[Test]
|
||||
public void TestLinksWithTitleAndCarriageReturn()
|
||||
{
|
||||
var text = "[Link 1][link-1], [link 2][link-2].\r\n\r\n[link-1]: https://example.com \"title 1\" \r\n[link-2]: https://example.com \"title 2\"";
|
||||
var result = Markdown.ToHtml(text).TrimEnd();
|
||||
Assert.AreEqual("<p><a href=\"https://example.com\" title=\"title 1\">Link 1</a>, <a href=\"https://example.com\" title=\"title 2\">link 2</a>.</p>", result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestListBug2()
|
||||
{
|
||||
TestParser.TestSpec("10.\t*test* – test\n\n11.\t__test__ test\n\n", @"<ol start=""10"">
|
||||
[Test]
|
||||
public void TestLink()
|
||||
{
|
||||
var doc = Markdown.Parse("There is a ");
|
||||
var link = doc.Descendants<ParagraphBlock>().SelectMany(x => x.Inline.Descendants<LinkInline>()).FirstOrDefault(l => l.IsImage);
|
||||
Assert.AreEqual("/yoyo", link?.Url);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinkWithMultipleBackslashesInTitle()
|
||||
{
|
||||
var doc = Markdown.Parse(@"[link](/uri '\\\\127.0.0.1')");
|
||||
var link = doc.Descendants<LinkInline>().FirstOrDefault();
|
||||
Assert.AreEqual(@"\\127.0.0.1", link?.Title);
|
||||
}
|
||||
|
||||
[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()
|
||||
{
|
||||
var text = @" *[HTML]: Hypertext Markup Language
|
||||
[Test]
|
||||
public void TestSimple()
|
||||
{
|
||||
var text = @" *[HTML]: Hypertext Markup Language
|
||||
|
||||
Later in a text we are using HTML and it becomes an abbr tag HTML
|
||||
";
|
||||
// var reader = new StringReader(@"> > toto tata
|
||||
//> titi toto
|
||||
//");
|
||||
// var reader = new StringReader(@"> > toto tata
|
||||
//> titi toto
|
||||
//");
|
||||
|
||||
//var result = Markdown.ToHtml(text, new MarkdownPipeline().UseFootnotes().UseEmphasisExtras());
|
||||
var result = Markdown.ToHtml(text, new MarkdownPipelineBuilder().UseAbbreviations().Build());
|
||||
//File.WriteAllText("test.html", result, Encoding.UTF8);
|
||||
//Console.WriteLine(result);
|
||||
}
|
||||
//var result = Markdown.ToHtml(text, new MarkdownPipeline().UseFootnotes().UseEmphasisExtras());
|
||||
var result = Markdown.ToHtml(text, new MarkdownPipelineBuilder().UseAbbreviations().Build());
|
||||
//File.WriteAllText("test.html", result, Encoding.UTF8);
|
||||
//Console.WriteLine(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmptyLiteral()
|
||||
{
|
||||
var text = @"> *some text*
|
||||
[Test]
|
||||
public void TestEmptyLiteral()
|
||||
{
|
||||
var text = @"> *some text*
|
||||
> some other text";
|
||||
var doc = Markdown.Parse(text);
|
||||
var doc = Markdown.Parse(text);
|
||||
|
||||
Assert.True(doc.Descendants<LiteralInline>().All(x => !x.Content.IsEmpty),
|
||||
"There should not have any empty literals");
|
||||
}
|
||||
Assert.True(doc.Descendants<LiteralInline>().All(x => !x.Content.IsEmpty),
|
||||
"There should not have any empty literals");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelfPipeline1()
|
||||
{
|
||||
var text = @" <!--markdig:pipetables-->
|
||||
[Test]
|
||||
public void TestSelfPipeline1()
|
||||
{
|
||||
var text = @" <!--markdig:pipetables-->
|
||||
|
||||
a | b
|
||||
- | -
|
||||
0 | 1
|
||||
";
|
||||
TestParser.TestSpec(text, @"<!--markdig:pipetables-->
|
||||
TestParser.TestSpec(text, @"<!--markdig:pipetables-->
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -107,17 +119,17 @@ a | b
|
||||
</tbody>
|
||||
</table>
|
||||
", "self");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestListBug()
|
||||
{
|
||||
// TODO: Add this test back to the CommonMark specs
|
||||
var text = @"- item1
|
||||
[Test]
|
||||
public void TestListBug()
|
||||
{
|
||||
// TODO: Add this test back to the CommonMark specs
|
||||
var text = @"- item1
|
||||
- item2
|
||||
- item3
|
||||
- item4";
|
||||
TestParser.TestSpec(text, @"<ul>
|
||||
TestParser.TestSpec(text, @"<ul>
|
||||
<li>item1
|
||||
<ul>
|
||||
<li>item2
|
||||
@@ -130,13 +142,13 @@ a | b
|
||||
</ul></li>
|
||||
</ul>
|
||||
");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void TestHtmlBug()
|
||||
{
|
||||
TestParser.TestSpec(@" # header1
|
||||
[Test]
|
||||
public void TestHtmlBug()
|
||||
{
|
||||
TestParser.TestSpec(@" # header1
|
||||
|
||||
<pre class='copy'>
|
||||
blabla
|
||||
@@ -148,43 +160,43 @@ blabla
|
||||
blabla
|
||||
</pre>
|
||||
<h1>header2</h1>");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHtmlh4Bug()
|
||||
{
|
||||
TestParser.TestSpec(@"<h4>foobar</h4>", @"<h4>foobar</h4>");
|
||||
}
|
||||
[Test]
|
||||
public void TestHtmlh4Bug()
|
||||
{
|
||||
TestParser.TestSpec(@"<h4>foobar</h4>", @"<h4>foobar</h4>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStandardUriEscape()
|
||||
{
|
||||
TestParser.TestSpec(@"", "<p><img src=\"你好.png\" alt=\"你好\" /></p>", "nonascii-noescape");
|
||||
}
|
||||
[Test]
|
||||
public void TestStandardUriEscape()
|
||||
{
|
||||
TestParser.TestSpec(@"", "<p><img src=\"你好.png\" alt=\"你好\" /></p>", "nonascii-noescape");
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void TestBugAdvanced()
|
||||
{
|
||||
TestParser.TestSpec(@"`https://{domain}/callbacks`
|
||||
[Test]
|
||||
public void TestBugAdvanced()
|
||||
{
|
||||
TestParser.TestSpec(@"`https://{domain}/callbacks`
|
||||
#### HEADING
|
||||
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 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 |
|
||||
[Test]
|
||||
public void TestBugPipeTables()
|
||||
{
|
||||
// https://github.com/lunet-io/markdig/issues/73
|
||||
TestParser.TestSpec(@"| abc | def |
|
||||
| --- | --- |
|
||||
| 1 | ~3 |
|
||||
", @"<table>
|
||||
@@ -201,12 +213,12 @@ Paragraph
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>", "advanced");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGridTableWithCustomAttributes() {
|
||||
[Test]
|
||||
public void TestGridTableWithCustomAttributes() {
|
||||
|
||||
var input = @"
|
||||
var input = @"
|
||||
{.table}
|
||||
+---+---+
|
||||
| a | b |
|
||||
@@ -215,7 +227,7 @@ Paragraph
|
||||
+---+---+
|
||||
";
|
||||
|
||||
var expected = @"<table class=""table"">
|
||||
var expected = @"<table class=""table"">
|
||||
<col style=""width:50%"" />
|
||||
<col style=""width:50%"" />
|
||||
<thead>
|
||||
@@ -232,76 +244,75 @@ Paragraph
|
||||
</tbody>
|
||||
</table>
|
||||
";
|
||||
TestParser.TestSpec(input, expected, "advanced");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSamePipelineAllExtensions()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
|
||||
// Reuse the same pipeline
|
||||
var result1 = Markdown.ToHtml("This is a \"\"citation\"\"", pipeline);
|
||||
var result2 = Markdown.ToHtml("This is a \"\"citation\"\"", pipeline);
|
||||
|
||||
Assert.AreEqual("<p>This is a <cite>citation</cite></p>", result1.Trim());
|
||||
Assert.AreEqual(result1, result2);
|
||||
}
|
||||
|
||||
// Test for emoji and smileys
|
||||
// var text = @" This is a test with a :) and a :angry: smiley";
|
||||
|
||||
|
||||
// Test for definition lists:
|
||||
//
|
||||
// var text = @"
|
||||
//Term 1
|
||||
//: This is a definition item
|
||||
// With a paragraph
|
||||
// > This is a block quote
|
||||
|
||||
// - This is a list
|
||||
// - item2
|
||||
|
||||
// ```java
|
||||
// Test
|
||||
|
||||
|
||||
// ```
|
||||
|
||||
// And a lazy line
|
||||
//: This ia another definition item
|
||||
|
||||
//Term2
|
||||
//Term3 *with some inline*
|
||||
//: This is another definition for term2
|
||||
//";
|
||||
|
||||
|
||||
// Test for grid table
|
||||
|
||||
|
||||
// var text = @"
|
||||
//+-----------------------------------+--------------------------------------+
|
||||
//| - this is a list | > We have a blockquote
|
||||
//| - this is a second item |
|
||||
//| |
|
||||
//| ``` |
|
||||
//| Yes |
|
||||
//| ``` |
|
||||
//+===================================+======================================+
|
||||
//| This is a second line |
|
||||
//+-----------------------------------+--------------------------------------+
|
||||
|
||||
//:::spoiler {#yessss}
|
||||
//This is a spoiler
|
||||
//:::
|
||||
|
||||
///| we have mult | paragraph |
|
||||
///| we have a new colspan with a long line
|
||||
///| and lots of text
|
||||
//";
|
||||
|
||||
|
||||
TestParser.TestSpec(input, expected, "advanced");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSamePipelineAllExtensions()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
|
||||
// Reuse the same pipeline
|
||||
var result1 = Markdown.ToHtml("This is a \"\"citation\"\"", pipeline);
|
||||
var result2 = Markdown.ToHtml("This is a \"\"citation\"\"", pipeline);
|
||||
|
||||
Assert.AreEqual("<p>This is a <cite>citation</cite></p>", result1.Trim());
|
||||
Assert.AreEqual(result1, result2);
|
||||
}
|
||||
|
||||
// Test for emoji and smileys
|
||||
// var text = @" This is a test with a :) and a :angry: smiley";
|
||||
|
||||
|
||||
// Test for definition lists:
|
||||
//
|
||||
// var text = @"
|
||||
//Term 1
|
||||
//: This is a definition item
|
||||
// With a paragraph
|
||||
// > This is a block quote
|
||||
|
||||
// - This is a list
|
||||
// - item2
|
||||
|
||||
// ```java
|
||||
// Test
|
||||
|
||||
|
||||
// ```
|
||||
|
||||
// And a lazy line
|
||||
//: This ia another definition item
|
||||
|
||||
//Term2
|
||||
//Term3 *with some inline*
|
||||
//: This is another definition for term2
|
||||
//";
|
||||
|
||||
|
||||
// Test for grid table
|
||||
|
||||
|
||||
// var text = @"
|
||||
//+-----------------------------------+--------------------------------------+
|
||||
//| - this is a list | > We have a blockquote
|
||||
//| - this is a second item |
|
||||
//| |
|
||||
//| ``` |
|
||||
//| Yes |
|
||||
//| ``` |
|
||||
//+===================================+======================================+
|
||||
//| This is a second line |
|
||||
//+-----------------------------------+--------------------------------------+
|
||||
|
||||
//:::spoiler {#yessss}
|
||||
//This is a spoiler
|
||||
//:::
|
||||
|
||||
///| we have mult | paragraph |
|
||||
///| we have a new colspan with a long line
|
||||
///| and lots of text
|
||||
//";
|
||||
|
||||
|
||||
}
|
||||
@@ -1,19 +1,18 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestPragmaLines
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestPragmaLines
|
||||
[Test]
|
||||
public void TestFindClosest()
|
||||
{
|
||||
[Test]
|
||||
public void TestFindClosest()
|
||||
{
|
||||
var doc = Markdown.Parse(
|
||||
var doc = Markdown.Parse(
|
||||
"test1\n" + // 0
|
||||
"\n" + // 1
|
||||
"test2\n" + // 2
|
||||
@@ -37,27 +36,27 @@ namespace Markdig.Tests
|
||||
"- item3\n" + // 20
|
||||
"\n" + // 21
|
||||
"This is a last paragraph\n" // 22
|
||||
, new MarkdownPipelineBuilder().UsePragmaLines().Build());
|
||||
, new MarkdownPipelineBuilder().UsePragmaLines().Build());
|
||||
|
||||
foreach (var exact in new int[] {0, 2, 4, 6, 8, 10, 18, 19, 20, 22})
|
||||
{
|
||||
Assert.AreEqual(exact, doc.FindClosestLine(exact));
|
||||
}
|
||||
|
||||
Assert.AreEqual(22, doc.FindClosestLine(23));
|
||||
|
||||
Assert.AreEqual(10, doc.FindClosestLine(11));
|
||||
Assert.AreEqual(10, doc.FindClosestLine(12));
|
||||
Assert.AreEqual(10, doc.FindClosestLine(13));
|
||||
Assert.AreEqual(18, doc.FindClosestLine(14)); // > 50% of the paragraph, we switch to next
|
||||
Assert.AreEqual(18, doc.FindClosestLine(15));
|
||||
Assert.AreEqual(18, doc.FindClosestLine(16));
|
||||
foreach (var exact in new int[] {0, 2, 4, 6, 8, 10, 18, 19, 20, 22})
|
||||
{
|
||||
Assert.AreEqual(exact, doc.FindClosestLine(exact));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFindClosest1()
|
||||
{
|
||||
var text =
|
||||
Assert.AreEqual(22, doc.FindClosestLine(23));
|
||||
|
||||
Assert.AreEqual(10, doc.FindClosestLine(11));
|
||||
Assert.AreEqual(10, doc.FindClosestLine(12));
|
||||
Assert.AreEqual(10, doc.FindClosestLine(13));
|
||||
Assert.AreEqual(18, doc.FindClosestLine(14)); // > 50% of the paragraph, we switch to next
|
||||
Assert.AreEqual(18, doc.FindClosestLine(15));
|
||||
Assert.AreEqual(18, doc.FindClosestLine(16));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFindClosest1()
|
||||
{
|
||||
var text =
|
||||
"- item1\n" + // 0
|
||||
" - item11\n" + // 1
|
||||
" - item12\n" + // 2
|
||||
@@ -66,15 +65,14 @@ namespace Markdig.Tests
|
||||
" - item131\n" + // 5
|
||||
" - item1311\n"; // 6
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder().UsePragmaLines().Build();
|
||||
var doc = Markdown.Parse(text, pipeline);
|
||||
var pipeline = new MarkdownPipelineBuilder().UsePragmaLines().Build();
|
||||
var doc = Markdown.Parse(text, pipeline);
|
||||
|
||||
for (int exact = 0; exact < 7; exact++)
|
||||
{
|
||||
Assert.AreEqual(exact, doc.FindClosestLine(exact));
|
||||
}
|
||||
|
||||
Assert.AreEqual(6, doc.FindClosestLine(50));
|
||||
for (int exact = 0; exact < 7; exact++)
|
||||
{
|
||||
Assert.AreEqual(exact, doc.FindClosestLine(exact));
|
||||
}
|
||||
|
||||
Assert.AreEqual(6, doc.FindClosestLine(50));
|
||||
}
|
||||
}
|
||||
@@ -1,57 +1,54 @@
|
||||
using NUnit.Framework;
|
||||
namespace Markdig.Tests;
|
||||
|
||||
namespace Markdig.Tests
|
||||
[TestFixture]
|
||||
public class TestReferralLinks
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestReferralLinks
|
||||
[Test]
|
||||
public void TestLinksWithNoFollowRel()
|
||||
{
|
||||
[Test]
|
||||
public void TestLinksWithNoFollowRel()
|
||||
{
|
||||
var markdown = "[world](http://example.com)";
|
||||
var expected = "nofollow";
|
||||
var markdown = "[world](http://example.com)";
|
||||
var expected = "nofollow";
|
||||
|
||||
#pragma warning disable 0618
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseNoFollowLinks()
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseNoFollowLinks()
|
||||
#pragma warning restore 0618
|
||||
.Build();
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
.Build();
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
Assert.That(html, Contains.Substring($"rel=\"{expected}\""));
|
||||
}
|
||||
Assert.That(html, Contains.Substring($"rel=\"{expected}\""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(new[] { "nofollow" }, "nofollow")]
|
||||
[TestCase(new[] { "noopener" }, "noopener")]
|
||||
[TestCase(new[] { "nofollow", "noopener"}, "nofollow noopener")]
|
||||
public void TestLinksWithCustomRel(string[] rels, string expected)
|
||||
{
|
||||
var markdown = "[world](http://example.com)";
|
||||
[Test]
|
||||
[TestCase(new[] { "nofollow" }, "nofollow")]
|
||||
[TestCase(new[] { "noopener" }, "noopener")]
|
||||
[TestCase(new[] { "nofollow", "noopener"}, "nofollow noopener")]
|
||||
public void TestLinksWithCustomRel(string[] rels, string expected)
|
||||
{
|
||||
var markdown = "[world](http://example.com)";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseReferralLinks(rels)
|
||||
.Build();
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseReferralLinks(rels)
|
||||
.Build();
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
Assert.That(html, Contains.Substring($"rel=\"{expected}\""));
|
||||
}
|
||||
Assert.That(html, Contains.Substring($"rel=\"{expected}\""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(new[] { "noopener" }, "noopener")]
|
||||
[TestCase(new[] { "nofollow" }, "nofollow")]
|
||||
[TestCase(new[] { "nofollow", "noopener" }, "nofollow noopener")]
|
||||
public void TestAutoLinksWithCustomRel(string[] rels, string expected)
|
||||
{
|
||||
var markdown = "http://example.com";
|
||||
[Test]
|
||||
[TestCase(new[] { "noopener" }, "noopener")]
|
||||
[TestCase(new[] { "nofollow" }, "nofollow")]
|
||||
[TestCase(new[] { "nofollow", "noopener" }, "nofollow noopener")]
|
||||
public void TestAutoLinksWithCustomRel(string[] rels, string expected)
|
||||
{
|
||||
var markdown = "http://example.com";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAutoLinks()
|
||||
.UseReferralLinks(rels)
|
||||
.Build();
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAutoLinks()
|
||||
.UseReferralLinks(rels)
|
||||
.Build();
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
Assert.That(html, Contains.Substring($"rel=\"{expected}\""));
|
||||
}
|
||||
Assert.That(html, Contains.Substring($"rel=\"{expected}\""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +1,44 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Renderers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestRelativeUrlReplacement
|
||||
{
|
||||
public class TestRelativeUrlReplacement
|
||||
[Test]
|
||||
public void ReplacesRelativeLinks()
|
||||
{
|
||||
[Test]
|
||||
public void ReplacesRelativeLinks()
|
||||
{
|
||||
TestSpec("https://example.com", "Link: [hello](/relative.jpg)", "https://example.com/relative.jpg");
|
||||
TestSpec("https://example.com", "Link: [hello](relative.jpg)", "https://example.com/relative.jpg");
|
||||
TestSpec("https://example.com/", "Link: [hello](/relative.jpg?a=b)", "https://example.com/relative.jpg?a=b");
|
||||
TestSpec("https://example.com/", "Link: [hello](relative.jpg#x)", "https://example.com/relative.jpg#x");
|
||||
TestSpec(null, "Link: [hello](relative.jpg)", "relative.jpg");
|
||||
TestSpec(null, "Link: [hello](/relative.jpg)", "/relative.jpg");
|
||||
TestSpec("https://example.com", "Link: [hello](/relative.jpg)", "https://example.com/relative.jpg");
|
||||
}
|
||||
TestSpec("https://example.com", "Link: [hello](/relative.jpg)", "https://example.com/relative.jpg");
|
||||
TestSpec("https://example.com", "Link: [hello](relative.jpg)", "https://example.com/relative.jpg");
|
||||
TestSpec("https://example.com/", "Link: [hello](/relative.jpg?a=b)", "https://example.com/relative.jpg?a=b");
|
||||
TestSpec("https://example.com/", "Link: [hello](relative.jpg#x)", "https://example.com/relative.jpg#x");
|
||||
TestSpec(null, "Link: [hello](relative.jpg)", "relative.jpg");
|
||||
TestSpec(null, "Link: [hello](/relative.jpg)", "/relative.jpg");
|
||||
TestSpec("https://example.com", "Link: [hello](/relative.jpg)", "https://example.com/relative.jpg");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReplacesRelativeImageSources()
|
||||
{
|
||||
TestSpec("https://example.com", "Image: ", "https://example.com/image.jpg");
|
||||
TestSpec("https://example.com", "Image: ", "https://example.com/image.jpg");
|
||||
TestSpec(null, "Image: ", "/image.jpg");
|
||||
}
|
||||
[Test]
|
||||
public void ReplacesRelativeImageSources()
|
||||
{
|
||||
TestSpec("https://example.com", "Image: ", "https://example.com/image.jpg");
|
||||
TestSpec("https://example.com", "Image: ", "https://example.com/image.jpg");
|
||||
TestSpec(null, "Image: ", "/image.jpg");
|
||||
}
|
||||
|
||||
public static void TestSpec(string baseUrl, string markdown, string expectedLink)
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().Build();
|
||||
public static void TestSpec(string baseUrl, string markdown, string expectedLink)
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().Build();
|
||||
|
||||
var writer = new StringWriter();
|
||||
var renderer = new HtmlRenderer(writer);
|
||||
if (baseUrl != null)
|
||||
renderer.BaseUrl = new Uri(baseUrl);
|
||||
pipeline.Setup(renderer);
|
||||
var writer = new StringWriter();
|
||||
var renderer = new HtmlRenderer(writer);
|
||||
if (baseUrl != null)
|
||||
renderer.BaseUrl = new Uri(baseUrl);
|
||||
pipeline.Setup(renderer);
|
||||
|
||||
var document = MarkdownParser.Parse(markdown, pipeline);
|
||||
renderer.Render(document);
|
||||
writer.Flush();
|
||||
var document = MarkdownParser.Parse(markdown, pipeline);
|
||||
renderer.Render(document);
|
||||
writer.Flush();
|
||||
|
||||
Assert.That(writer.ToString(), Contains.Substring("=\"" + expectedLink + "\""));
|
||||
}
|
||||
Assert.That(writer.ToString(), Contains.Substring("=\"" + expectedLink + "\""));
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,29 @@
|
||||
using Markdig.Renderers.Roundtrip;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
internal static class TestRoundtrip
|
||||
{
|
||||
internal static class TestRoundtrip
|
||||
internal static void TestSpec(string markdownText, string expected, string extensions, string context = null)
|
||||
{
|
||||
internal static void TestSpec(string markdownText, string expected, string extensions, string context = null)
|
||||
{
|
||||
RoundTrip(markdownText, context);
|
||||
}
|
||||
RoundTrip(markdownText, context);
|
||||
}
|
||||
|
||||
internal static void RoundTrip(string markdown, string context = null)
|
||||
{
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
pipelineBuilder.EnableTrackTrivia();
|
||||
pipelineBuilder.UseYamlFrontMatter();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var sw = new StringWriter();
|
||||
var nr = new RoundtripRenderer(sw);
|
||||
pipeline.Setup(nr);
|
||||
internal static void RoundTrip(string markdown, string context = null)
|
||||
{
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
pipelineBuilder.EnableTrackTrivia();
|
||||
pipelineBuilder.UseYamlFrontMatter();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var sw = new StringWriter();
|
||||
var nr = new RoundtripRenderer(sw);
|
||||
pipeline.Setup(nr);
|
||||
|
||||
nr.Write(markdownDocument);
|
||||
nr.Write(markdownDocument);
|
||||
|
||||
var result = sw.ToString();
|
||||
TestParser.PrintAssertExpected("", result, markdown, context);
|
||||
}
|
||||
var result = sw.ToString();
|
||||
TestParser.PrintAssertExpected("", result, markdown, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,34 @@
|
||||
using Markdig.Extensions.SmartyPants;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestSmartyPants
|
||||
{
|
||||
public class TestSmartyPants
|
||||
[Test]
|
||||
public void MappingCanBeReconfigured()
|
||||
{
|
||||
[Test]
|
||||
public void MappingCanBeReconfigured()
|
||||
{
|
||||
SmartyPantOptions options = new SmartyPantOptions();
|
||||
options.Mapping[SmartyPantType.LeftAngleQuote] = "foo";
|
||||
options.Mapping[SmartyPantType.RightAngleQuote] = "bar";
|
||||
var options = new SmartyPantOptions();
|
||||
options.Mapping[SmartyPantType.LeftAngleQuote] = "foo";
|
||||
options.Mapping[SmartyPantType.RightAngleQuote] = "bar";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseSmartyPants(options)
|
||||
.Build();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseSmartyPants(options)
|
||||
.Build();
|
||||
|
||||
TestParser.TestSpec("<<test>>", "<p>footestbar</p>", pipeline);
|
||||
}
|
||||
TestParser.TestSpec("<<test>>", "<p>footestbar</p>", pipeline);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MappingCanBeReconfigured_HandlesRemovedMappings()
|
||||
{
|
||||
SmartyPantOptions options = new SmartyPantOptions();
|
||||
options.Mapping.Remove(SmartyPantType.LeftAngleQuote);
|
||||
options.Mapping.Remove(SmartyPantType.RightAngleQuote);
|
||||
[Test]
|
||||
public void MappingCanBeReconfigured_HandlesRemovedMappings()
|
||||
{
|
||||
var options = new SmartyPantOptions();
|
||||
options.Mapping.Remove(SmartyPantType.LeftAngleQuote);
|
||||
options.Mapping.Remove(SmartyPantType.RightAngleQuote);
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseSmartyPants(options)
|
||||
.Build();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseSmartyPants(options)
|
||||
.Build();
|
||||
|
||||
TestParser.TestSpec("<<test>>", "<p>«test»</p>", pipeline);
|
||||
}
|
||||
TestParser.TestSpec("<<test>>", "<p>«test»</p>", pipeline);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,185 +1,242 @@
|
||||
using NUnit.Framework;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
|
||||
using Markdig.Helpers;
|
||||
using System;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
[TestFixture]
|
||||
public class TestStringSliceList
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestStringSliceList
|
||||
// TODO: Add tests for StringSlice
|
||||
// TODO: Add more tests for StringLineGroup
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupSimple()
|
||||
{
|
||||
// TODO: Add tests for StringSlice
|
||||
// TODO: Add more tests for StringLineGroup
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupSimple()
|
||||
var text = new StringLineGroup(4)
|
||||
{
|
||||
var text = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("ABC", NewLine.LineFeed),
|
||||
new StringSlice("E", NewLine.LineFeed),
|
||||
new StringSlice("F")
|
||||
};
|
||||
new StringSlice("ABC", NewLine.LineFeed),
|
||||
new StringSlice("E", NewLine.LineFeed),
|
||||
new StringSlice("F")
|
||||
};
|
||||
|
||||
var iterator = text.ToCharIterator();
|
||||
Assert.AreEqual("ABC\nE\nF".Length, iterator.End - iterator.Start + 1);
|
||||
var iterator = text.ToCharIterator();
|
||||
Assert.AreEqual("ABC\nE\nF".Length, iterator.End - iterator.Start + 1);
|
||||
|
||||
var chars = ToString(text.ToCharIterator());
|
||||
TextAssert.AreEqual("ABC\nE\nF", chars.ToString());
|
||||
var chars = ToString(text.ToCharIterator());
|
||||
TextAssert.AreEqual("ABC\nE\nF", chars.ToString());
|
||||
|
||||
TextAssert.AreEqual("ABC\nE\nF", text.ToString());
|
||||
}
|
||||
TextAssert.AreEqual("ABC\nE\nF", text.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupWithSlices()
|
||||
[Test]
|
||||
public void TestStringLineGroupWithSlices()
|
||||
{
|
||||
var text = new StringLineGroup(4)
|
||||
{
|
||||
var text = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("XABC", NewLine.LineFeed) { Start = 1},
|
||||
new StringSlice("YYE", NewLine.LineFeed) { Start = 2},
|
||||
new StringSlice("ZZZF") { Start = 3 }
|
||||
};
|
||||
new StringSlice("XABC", NewLine.LineFeed) { Start = 1},
|
||||
new StringSlice("YYE", NewLine.LineFeed) { Start = 2},
|
||||
new StringSlice("ZZZF") { Start = 3 }
|
||||
};
|
||||
|
||||
var chars = ToString(text.ToCharIterator());
|
||||
TextAssert.AreEqual("ABC\nE\nF", chars.ToString());
|
||||
}
|
||||
var chars = ToString(text.ToCharIterator());
|
||||
TextAssert.AreEqual("ABC\nE\nF", chars.ToString());
|
||||
}
|
||||
|
||||
|
||||
private static string ToString(StringLineGroup.Iterator text)
|
||||
private static string ToString(StringLineGroup.Iterator text)
|
||||
{
|
||||
var chars = new StringBuilder();
|
||||
while (text.CurrentChar != '\0')
|
||||
{
|
||||
var chars = new StringBuilder();
|
||||
while (text.CurrentChar != '\0')
|
||||
{
|
||||
chars.Append(text.CurrentChar);
|
||||
text.NextChar();
|
||||
}
|
||||
return chars.ToString();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupSaveAndRestore()
|
||||
{
|
||||
var text = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("ABCD", NewLine.LineFeed),
|
||||
new StringSlice("EF"),
|
||||
}.ToCharIterator();
|
||||
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
Assert.AreEqual(0, text.SliceIndex);
|
||||
|
||||
text.NextChar(); // B
|
||||
|
||||
text.NextChar(); // C
|
||||
text.NextChar(); // D
|
||||
text.NextChar(); // \n
|
||||
chars.Append(text.CurrentChar);
|
||||
text.NextChar();
|
||||
Assert.AreEqual('E', text.CurrentChar);
|
||||
Assert.AreEqual(1, text.SliceIndex);
|
||||
}
|
||||
return chars.ToString();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSkipWhitespaces()
|
||||
[Test]
|
||||
public void TestStringLineGroupSaveAndRestore()
|
||||
{
|
||||
var text = new StringLineGroup(4)
|
||||
{
|
||||
var text = new StringLineGroup(" ABC").ToCharIterator();
|
||||
Assert.False(text.TrimStart());
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
new StringSlice("ABCD", NewLine.LineFeed),
|
||||
new StringSlice("EF"),
|
||||
}.ToCharIterator();
|
||||
|
||||
text = new StringLineGroup(" ").ToCharIterator();
|
||||
Assert.True(text.TrimStart());
|
||||
Assert.AreEqual('\0', text.CurrentChar);
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
Assert.AreEqual(0, text.SliceIndex);
|
||||
|
||||
var slice = new StringSlice(" ABC");
|
||||
Assert.False(slice.TrimStart());
|
||||
text.NextChar(); // B
|
||||
|
||||
slice = new StringSlice(" ");
|
||||
Assert.True(slice.TrimStart());
|
||||
}
|
||||
text.NextChar(); // C
|
||||
text.NextChar(); // D
|
||||
text.NextChar(); // \n
|
||||
text.NextChar();
|
||||
Assert.AreEqual('E', text.CurrentChar);
|
||||
Assert.AreEqual(1, text.SliceIndex);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupWithModifiedStart()
|
||||
[Test]
|
||||
public void TestSkipWhitespaces()
|
||||
{
|
||||
var text = new StringLineGroup(" ABC").ToCharIterator();
|
||||
Assert.False(text.TrimStart());
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
|
||||
text = new StringLineGroup(" ").ToCharIterator();
|
||||
Assert.True(text.TrimStart());
|
||||
Assert.AreEqual('\0', text.CurrentChar);
|
||||
|
||||
var slice = new StringSlice(" ABC");
|
||||
Assert.False(slice.TrimStart());
|
||||
|
||||
slice = new StringSlice(" ");
|
||||
Assert.True(slice.TrimStart());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupWithModifiedStart()
|
||||
{
|
||||
var line1 = new StringSlice(" ABC", NewLine.LineFeed);
|
||||
line1.NextChar();
|
||||
line1.NextChar();
|
||||
|
||||
var line2 = new StringSlice(" DEF ");
|
||||
line2.Trim();
|
||||
|
||||
var text = new StringLineGroup(4) {line1, line2};
|
||||
|
||||
var result = ToString(text.ToCharIterator());
|
||||
TextAssert.AreEqual("ABC\nDEF", result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupWithTrim()
|
||||
{
|
||||
var line1 = new StringSlice(" ABC ", NewLine.LineFeed);
|
||||
line1.NextChar();
|
||||
line1.NextChar();
|
||||
|
||||
var line2 = new StringSlice(" DEF ");
|
||||
|
||||
var text = new StringLineGroup(4) { line1, line2}.ToCharIterator();
|
||||
text.TrimStart();
|
||||
|
||||
var result = ToString(text);
|
||||
TextAssert.AreEqual("ABC \n DEF ", result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupIteratorPeekChar()
|
||||
{
|
||||
var iterator = new StringLineGroup(4)
|
||||
{
|
||||
var line1 = new StringSlice(" ABC", NewLine.LineFeed);
|
||||
line1.NextChar();
|
||||
line1.NextChar();
|
||||
new StringSlice("ABC", NewLine.LineFeed),
|
||||
new StringSlice("E", NewLine.LineFeed),
|
||||
new StringSlice("F")
|
||||
}.ToCharIterator();
|
||||
|
||||
var line2 = new StringSlice(" DEF ");
|
||||
line2.Trim();
|
||||
Assert.AreEqual('A', iterator.CurrentChar);
|
||||
Assert.AreEqual('A', iterator.PeekChar(0));
|
||||
Assert.AreEqual('B', iterator.PeekChar());
|
||||
Assert.AreEqual('B', iterator.PeekChar(1));
|
||||
Assert.AreEqual('C', iterator.PeekChar(2));
|
||||
Assert.AreEqual('\n', iterator.PeekChar(3));
|
||||
Assert.AreEqual('E', iterator.PeekChar(4));
|
||||
Assert.AreEqual('\n', iterator.PeekChar(5));
|
||||
Assert.AreEqual('F', iterator.PeekChar(6));
|
||||
Assert.AreEqual('\0', iterator.PeekChar(7)); // There is no \n appended to the last line
|
||||
Assert.AreEqual('\0', iterator.PeekChar(8));
|
||||
Assert.AreEqual('\0', iterator.PeekChar(100));
|
||||
|
||||
var text = new StringLineGroup(4) {line1, line2};
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => iterator.PeekChar(-1));
|
||||
}
|
||||
|
||||
var result = ToString(text.ToCharIterator());
|
||||
TextAssert.AreEqual("ABC\nDEF", result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupWithTrim()
|
||||
[Test]
|
||||
public void TestIteratorSkipChar()
|
||||
{
|
||||
var lineGroup = new StringLineGroup(4)
|
||||
{
|
||||
var line1 = new StringSlice(" ABC ", NewLine.LineFeed);
|
||||
line1.NextChar();
|
||||
line1.NextChar();
|
||||
new StringSlice("ABC", NewLine.LineFeed),
|
||||
new StringSlice("E", NewLine.LineFeed)
|
||||
};
|
||||
|
||||
var line2 = new StringSlice(" DEF ");
|
||||
Test(lineGroup.ToCharIterator());
|
||||
|
||||
var text = new StringLineGroup(4) { line1, line2}.ToCharIterator();
|
||||
text.TrimStart();
|
||||
Test(new StringSlice("ABC\nE\n"));
|
||||
|
||||
var result = ToString(text);
|
||||
TextAssert.AreEqual("ABC \n DEF ", result);
|
||||
}
|
||||
Test(new StringSlice("Foo\nABC\nE\n", 4, 9));
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupIteratorPeekChar()
|
||||
static void Test<T>(T iterator) where T : ICharIterator
|
||||
{
|
||||
var iterator = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("ABC", NewLine.LineFeed),
|
||||
new StringSlice("E", NewLine.LineFeed),
|
||||
new StringSlice("F")
|
||||
}.ToCharIterator();
|
||||
|
||||
Assert.AreEqual('A', iterator.CurrentChar);
|
||||
Assert.AreEqual('A', iterator.PeekChar(0));
|
||||
Assert.AreEqual('B', iterator.PeekChar());
|
||||
Assert.AreEqual('B', iterator.PeekChar(1));
|
||||
Assert.AreEqual('C', iterator.PeekChar(2));
|
||||
Assert.AreEqual('\n', iterator.PeekChar(3));
|
||||
Assert.AreEqual('E', iterator.PeekChar(4));
|
||||
Assert.AreEqual('\n', iterator.PeekChar(5));
|
||||
Assert.AreEqual('F', iterator.PeekChar(6));
|
||||
Assert.AreEqual('\0', iterator.PeekChar(7)); // There is no \n appended to the last line
|
||||
Assert.AreEqual('\0', iterator.PeekChar(8));
|
||||
Assert.AreEqual('\0', iterator.PeekChar(100));
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => iterator.PeekChar(-1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIteratorSkipChar()
|
||||
{
|
||||
var lineGroup = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("ABC", NewLine.LineFeed),
|
||||
new StringSlice("E", NewLine.LineFeed)
|
||||
};
|
||||
|
||||
Test(lineGroup.ToCharIterator());
|
||||
|
||||
Test(new StringSlice("ABC\nE\n"));
|
||||
|
||||
Test(new StringSlice("Foo\nABC\nE\n", 4, 9));
|
||||
|
||||
static void Test<T>(T iterator) where T : ICharIterator
|
||||
{
|
||||
Assert.AreEqual('A', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('B', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('C', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('\n', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('E', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('\n', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('\0', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('\0', iterator.CurrentChar); iterator.SkipChar();
|
||||
}
|
||||
Assert.AreEqual('A', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('B', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('C', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('\n', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('E', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('\n', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('\0', iterator.CurrentChar); iterator.SkipChar();
|
||||
Assert.AreEqual('\0', iterator.CurrentChar); iterator.SkipChar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupCharIteratorAtCapacity()
|
||||
{
|
||||
string str = "ABCDEFGHI";
|
||||
var text = new StringLineGroup(1)
|
||||
{
|
||||
// Will store the following line at capacity
|
||||
new StringSlice(str, NewLine.CarriageReturnLineFeed) { Start = 0, End = 2 },
|
||||
};
|
||||
|
||||
var iterator = text.ToCharIterator();
|
||||
var chars = ToString(iterator);
|
||||
TextAssert.AreEqual("ABC\r\n", chars.ToString());
|
||||
TextAssert.AreEqual("ABC", text.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupCharIteratorForcingIncreaseCapacity()
|
||||
{
|
||||
string str = "ABCDEFGHI";
|
||||
var text = new StringLineGroup(1)
|
||||
{
|
||||
// Will store the following line at capacity
|
||||
new StringSlice(str, NewLine.CarriageReturnLineFeed) { Start = 0, End = 2 },
|
||||
|
||||
// Will force increase capacity to 2 and store the line at capacity
|
||||
new StringSlice(str, NewLine.CarriageReturnLineFeed) { Start = 3, End = 3 },
|
||||
};
|
||||
|
||||
var iterator = text.ToCharIterator();
|
||||
var chars = ToString(iterator);
|
||||
TextAssert.AreEqual("ABC\r\nD\r\n", chars.ToString());
|
||||
TextAssert.AreEqual("ABC\r\nD", text.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroup_EnumeratorReturnsRealLines()
|
||||
{
|
||||
string str = "A\r\n";
|
||||
var text = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice(str, NewLine.CarriageReturnLineFeed) { Start = 0, End = 0 }
|
||||
};
|
||||
|
||||
var enumerator = ((IEnumerable)text).GetEnumerator();
|
||||
Assert.True(enumerator.MoveNext());
|
||||
StringLine currentLine = (StringLine)enumerator.Current;
|
||||
TextAssert.AreEqual("A", currentLine.ToString());
|
||||
Assert.False(enumerator.MoveNext());
|
||||
|
||||
var nonBoxedEnumerator = text.GetEnumerator();
|
||||
|
||||
Assert.True(nonBoxedEnumerator.MoveNext());
|
||||
currentLine = (StringLine)nonBoxedEnumerator.Current;
|
||||
TextAssert.AreEqual("A", currentLine.ToString());
|
||||
Assert.False(nonBoxedEnumerator.MoveNext());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,102 +1,98 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Markdig.Helpers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestTransformedStringCache
|
||||
{
|
||||
public class TestTransformedStringCache
|
||||
[Test]
|
||||
public void GetRunsTransformationCallback()
|
||||
{
|
||||
[Test]
|
||||
public void GetRunsTransformationCallback()
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => "callback-" + s);
|
||||
var cache = new TransformedStringCache(static s => "callback-" + s);
|
||||
|
||||
Assert.AreEqual("callback-foo", cache.Get("foo"));
|
||||
Assert.AreEqual("callback-bar", cache.Get("bar"));
|
||||
Assert.AreEqual("callback-baz", cache.Get("baz"));
|
||||
Assert.AreEqual("callback-foo", cache.Get("foo"));
|
||||
Assert.AreEqual("callback-bar", cache.Get("bar"));
|
||||
Assert.AreEqual("callback-baz", cache.Get("baz"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CachesTransformedInstance()
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => "callback-" + s);
|
||||
|
||||
string transformedBar = cache.Get("bar");
|
||||
Assert.AreSame(transformedBar, cache.Get("bar"));
|
||||
|
||||
string transformedFoo = cache.Get("foo".AsSpan());
|
||||
Assert.AreSame(transformedFoo, cache.Get("foo"));
|
||||
|
||||
Assert.AreSame(cache.Get("baz"), cache.Get("baz".AsSpan()));
|
||||
|
||||
Assert.AreSame(transformedBar, cache.Get("bar"));
|
||||
Assert.AreSame(transformedFoo, cache.Get("foo"));
|
||||
Assert.AreSame(transformedBar, cache.Get("bar".AsSpan()));
|
||||
Assert.AreSame(transformedFoo, cache.Get("foo".AsSpan()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoesNotCacheEmptyInputs()
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => new string('a', 4));
|
||||
|
||||
string cached = cache.Get("");
|
||||
string cached2 = cache.Get("");
|
||||
string cached3 = cache.Get(ReadOnlySpan<char>.Empty);
|
||||
|
||||
Assert.AreEqual("aaaa", cached);
|
||||
Assert.AreEqual(cached, cached2);
|
||||
Assert.AreEqual(cached, cached3);
|
||||
|
||||
Assert.AreNotSame(cached, cached2);
|
||||
Assert.AreNotSame(cached, cached3);
|
||||
Assert.AreNotSame(cached2, cached3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(TransformedStringCache.InputLengthLimit, true)]
|
||||
[TestCase(TransformedStringCache.InputLengthLimit + 1, false)]
|
||||
public void DoesNotCacheLongInputs(int length, bool shouldBeCached)
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => "callback-" + s);
|
||||
|
||||
string input = new string('a', length);
|
||||
|
||||
string cached = cache.Get(input);
|
||||
string cached2 = cache.Get(input);
|
||||
|
||||
Assert.AreEqual("callback-" + input, cached);
|
||||
Assert.AreEqual(cached, cached2);
|
||||
|
||||
if (shouldBeCached)
|
||||
{
|
||||
Assert.AreSame(cached, cached2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CachesTransformedInstance()
|
||||
else
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => "callback-" + s);
|
||||
|
||||
string transformedBar = cache.Get("bar");
|
||||
Assert.AreSame(transformedBar, cache.Get("bar"));
|
||||
|
||||
string transformedFoo = cache.Get("foo".AsSpan());
|
||||
Assert.AreSame(transformedFoo, cache.Get("foo"));
|
||||
|
||||
Assert.AreSame(cache.Get("baz"), cache.Get("baz".AsSpan()));
|
||||
|
||||
Assert.AreSame(transformedBar, cache.Get("bar"));
|
||||
Assert.AreSame(transformedFoo, cache.Get("foo"));
|
||||
Assert.AreSame(transformedBar, cache.Get("bar".AsSpan()));
|
||||
Assert.AreSame(transformedFoo, cache.Get("foo".AsSpan()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoesNotCacheEmptyInputs()
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => new string('a', 4));
|
||||
|
||||
string cached = cache.Get("");
|
||||
string cached2 = cache.Get("");
|
||||
string cached3 = cache.Get(ReadOnlySpan<char>.Empty);
|
||||
|
||||
Assert.AreEqual("aaaa", cached);
|
||||
Assert.AreEqual(cached, cached2);
|
||||
Assert.AreEqual(cached, cached3);
|
||||
|
||||
Assert.AreNotSame(cached, cached2);
|
||||
Assert.AreNotSame(cached, cached3);
|
||||
Assert.AreNotSame(cached2, cached3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(TransformedStringCache.InputLengthLimit, true)]
|
||||
[TestCase(TransformedStringCache.InputLengthLimit + 1, false)]
|
||||
public void DoesNotCacheLongInputs(int length, bool shouldBeCached)
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => "callback-" + s);
|
||||
|
||||
string input = new string('a', length);
|
||||
|
||||
string cached = cache.Get(input);
|
||||
string cached2 = cache.Get(input);
|
||||
|
||||
Assert.AreEqual("callback-" + input, cached);
|
||||
Assert.AreEqual(cached, cached2);
|
||||
|
||||
if (shouldBeCached)
|
||||
{
|
||||
Assert.AreSame(cached, cached2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreNotSame(cached, cached2);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CachesAtMostNEntriesPerCharacter()
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => "callback-" + s);
|
||||
|
||||
int limit = TransformedStringCache.MaxEntriesPerCharacter;
|
||||
|
||||
string[] a = Enumerable.Range(1, limit + 1).Select(i => $"a{i}").ToArray();
|
||||
string[] cachedAs = a.Select(a => cache.Get(a)).ToArray();
|
||||
|
||||
for (int i = 0; i < limit; i++)
|
||||
{
|
||||
Assert.AreSame(cachedAs[i], cache.Get(a[i]));
|
||||
}
|
||||
|
||||
Assert.AreNotSame(cachedAs[limit], cache.Get(a[limit]));
|
||||
|
||||
Assert.AreSame(cache.Get("b1"), cache.Get("b1"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CachesAtMostNEntriesPerCharacter()
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => "callback-" + s);
|
||||
|
||||
int limit = TransformedStringCache.MaxEntriesPerCharacter;
|
||||
|
||||
string[] a = Enumerable.Range(1, limit + 1).Select(i => $"a{i}").ToArray();
|
||||
string[] cachedAs = a.Select(a => cache.Get(a)).ToArray();
|
||||
|
||||
for (int i = 0; i < limit; i++)
|
||||
{
|
||||
Assert.AreSame(cachedAs[i], cache.Get(a[i]));
|
||||
}
|
||||
|
||||
Assert.AreNotSame(cachedAs[limit], cache.Get(a[limit]));
|
||||
|
||||
Assert.AreSame(cache.Get("b1"), cache.Get("b1"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Markdig.Extensions.Yaml;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestYamlFrontMatterExtension
|
||||
{
|
||||
public class TestYamlFrontMatterExtension
|
||||
[TestCaseSource(nameof(TestCases))]
|
||||
public void ProperYamlFrontMatterRenderersAdded(IMarkdownObjectRenderer[] objectRenderers, bool hasYamlFrontMatterHtmlRenderer, bool hasYamlFrontMatterRoundtripRenderer)
|
||||
{
|
||||
[TestCaseSource(nameof(TestCases))]
|
||||
public void ProperYamlFrontMatterRenderersAdded(IMarkdownObjectRenderer[] objectRenderers, bool hasYamlFrontMatterHtmlRenderer, bool hasYamlFrontMatterRoundtripRenderer)
|
||||
var builder = new MarkdownPipelineBuilder();
|
||||
builder.Extensions.Add(new YamlFrontMatterExtension());
|
||||
var markdownRenderer = new DummyRenderer();
|
||||
markdownRenderer.ObjectRenderers.AddRange(objectRenderers);
|
||||
builder.Build().Setup(markdownRenderer);
|
||||
Assert.That(markdownRenderer.ObjectRenderers.Contains<YamlFrontMatterHtmlRenderer>(), Is.EqualTo(hasYamlFrontMatterHtmlRenderer));
|
||||
Assert.That(markdownRenderer.ObjectRenderers.Contains<YamlFrontMatterRoundtripRenderer>(), Is.EqualTo(hasYamlFrontMatterRoundtripRenderer));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AllowYamlFrontMatterInMiddleOfDocument()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.Use(new YamlFrontMatterExtension { AllowInMiddleOfDocument = true })
|
||||
.Build();
|
||||
|
||||
TestParser.TestSpec(
|
||||
"This is a text1\n---\nthis: is a frontmatter\n---\nThis is a text2",
|
||||
"<p>This is a text1</p>\n<p>This is a text2</p>",
|
||||
pipeline);
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> TestCases()
|
||||
{
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
var builder = new MarkdownPipelineBuilder();
|
||||
builder.Extensions.Add(new YamlFrontMatterExtension());
|
||||
var markdownRenderer = new DummyRenderer();
|
||||
markdownRenderer.ObjectRenderers.AddRange(objectRenderers);
|
||||
builder.Build().Setup(markdownRenderer);
|
||||
Assert.That(markdownRenderer.ObjectRenderers.Contains<YamlFrontMatterHtmlRenderer>(), Is.EqualTo(hasYamlFrontMatterHtmlRenderer));
|
||||
Assert.That(markdownRenderer.ObjectRenderers.Contains<YamlFrontMatterRoundtripRenderer>(), Is.EqualTo(hasYamlFrontMatterRoundtripRenderer));
|
||||
}, false, false) {TestName = "No ObjectRenderers"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Html.CodeBlockRenderer()
|
||||
}, true, false) {TestName = "Html CodeBlock"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Roundtrip.CodeBlockRenderer()
|
||||
}, false, true) {TestName = "Roundtrip CodeBlock"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Html.CodeBlockRenderer(),
|
||||
new Markdig.Renderers.Roundtrip.CodeBlockRenderer()
|
||||
}, true, true) {TestName = "Html/Roundtrip CodeBlock"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Html.CodeBlockRenderer(),
|
||||
new Markdig.Renderers.Roundtrip.CodeBlockRenderer(),
|
||||
new YamlFrontMatterHtmlRenderer()
|
||||
}, true, true) {TestName = "Html/Roundtrip CodeBlock, Yaml Html"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Html.CodeBlockRenderer(),
|
||||
new Markdig.Renderers.Roundtrip.CodeBlockRenderer(),
|
||||
new YamlFrontMatterRoundtripRenderer()
|
||||
}, true, true) { TestName = "Html/Roundtrip CodeBlock, Yaml Roundtrip" };
|
||||
}
|
||||
|
||||
private class DummyRenderer : IMarkdownRenderer
|
||||
{
|
||||
public DummyRenderer()
|
||||
{
|
||||
ObjectRenderers = new ObjectRendererCollection();
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> TestCases()
|
||||
#pragma warning disable CS0067 // ObjectWriteBefore/ObjectWriteAfter is never used
|
||||
public event Action<IMarkdownRenderer, MarkdownObject> ObjectWriteBefore;
|
||||
public event Action<IMarkdownRenderer, MarkdownObject> ObjectWriteAfter;
|
||||
#pragma warning restore CS0067
|
||||
|
||||
public ObjectRendererCollection ObjectRenderers { get; }
|
||||
public object Render(MarkdownObject markdownObject)
|
||||
{
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
}, false, false) {TestName = "No ObjectRenderers"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Html.CodeBlockRenderer()
|
||||
}, true, false) {TestName = "Html CodeBlock"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Roundtrip.CodeBlockRenderer()
|
||||
}, false, true) {TestName = "Roundtrip CodeBlock"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Html.CodeBlockRenderer(),
|
||||
new Markdig.Renderers.Roundtrip.CodeBlockRenderer()
|
||||
}, true, true) {TestName = "Html/Roundtrip CodeBlock"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Html.CodeBlockRenderer(),
|
||||
new Markdig.Renderers.Roundtrip.CodeBlockRenderer(),
|
||||
new YamlFrontMatterHtmlRenderer()
|
||||
}, true, true) {TestName = "Html/Roundtrip CodeBlock, Yaml Html"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Html.CodeBlockRenderer(),
|
||||
new Markdig.Renderers.Roundtrip.CodeBlockRenderer(),
|
||||
new YamlFrontMatterRoundtripRenderer()
|
||||
}, true, true) { TestName = "Html/Roundtrip CodeBlock, Yaml Roundtrip" };
|
||||
}
|
||||
|
||||
private class DummyRenderer : IMarkdownRenderer
|
||||
{
|
||||
public DummyRenderer()
|
||||
{
|
||||
ObjectRenderers = new ObjectRendererCollection();
|
||||
}
|
||||
|
||||
public event Action<IMarkdownRenderer, MarkdownObject> ObjectWriteBefore;
|
||||
public event Action<IMarkdownRenderer, MarkdownObject> ObjectWriteAfter;
|
||||
public ObjectRendererCollection ObjectRenderers { get; }
|
||||
public object Render(MarkdownObject markdownObject)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("---\nkey1: value1\nkey2: value2\n---\n\n# Content\n")]
|
||||
[TestCase("---\nkey1: value1\nkey2: value2\nkey3: value3\nkey4: value4\nkey5: value5\nkey6: value6\nkey7: value7\nkey8: value8\n---\n\n# Content\n")]
|
||||
public void FrontMatterBlockLinesCharIterator(string value)
|
||||
{
|
||||
var builder = new MarkdownPipelineBuilder();
|
||||
builder.Extensions.Add(new YamlFrontMatterExtension());
|
||||
var markdownDocument = Markdown.Parse(value, builder.Build());
|
||||
|
||||
var yamlBlocks = markdownDocument.Descendants<YamlFrontMatterBlock>();
|
||||
Assert.True(yamlBlocks.Any());
|
||||
|
||||
foreach (var yamlBlock in yamlBlocks)
|
||||
{
|
||||
var iterator = yamlBlock.Lines.ToCharIterator();
|
||||
while(iterator.CurrentChar != '\0')
|
||||
{
|
||||
iterator.NextChar();
|
||||
}
|
||||
}
|
||||
|
||||
// No exception parsing and iterating through YAML front matter block lines
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,119 +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.Globalization;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
namespace Markdig.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Pretty text assert from https://gist.github.com/Haacked/1610603
|
||||
/// Modified version to only print +-10 characters around the first diff
|
||||
/// </summary>
|
||||
public static class TextAssert
|
||||
{
|
||||
/// <summary>
|
||||
/// Pretty text assert from https://gist.github.com/Haacked/1610603
|
||||
/// Modified version to only print +-10 characters around the first diff
|
||||
/// </summary>
|
||||
public static class TextAssert
|
||||
public enum DiffStyle
|
||||
{
|
||||
public enum DiffStyle
|
||||
Full,
|
||||
Minimal
|
||||
}
|
||||
|
||||
public static void AreEqual(string expectedValue, string actualValue)
|
||||
{
|
||||
AreEqual(expectedValue, actualValue, DiffStyle.Full, Console.Out);
|
||||
}
|
||||
|
||||
public static void AreEqual(string expectedValue, string actualValue, DiffStyle diffStyle)
|
||||
{
|
||||
AreEqual(expectedValue, actualValue, diffStyle, Console.Out);
|
||||
}
|
||||
|
||||
public static void AreEqual(string expectedValue, string actualValue, DiffStyle diffStyle, TextWriter output)
|
||||
{
|
||||
if (actualValue == null || expectedValue == null)
|
||||
{
|
||||
Full,
|
||||
Minimal
|
||||
}
|
||||
|
||||
public static void AreEqual(string expectedValue, string actualValue)
|
||||
{
|
||||
AreEqual(expectedValue, actualValue, DiffStyle.Full, Console.Out);
|
||||
}
|
||||
|
||||
public static void AreEqual(string expectedValue, string actualValue, DiffStyle diffStyle)
|
||||
{
|
||||
AreEqual(expectedValue, actualValue, diffStyle, Console.Out);
|
||||
}
|
||||
|
||||
public static void AreEqual(string expectedValue, string actualValue, DiffStyle diffStyle, TextWriter output)
|
||||
{
|
||||
if (actualValue == null || expectedValue == null)
|
||||
{
|
||||
Assert.AreEqual(expectedValue, actualValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (actualValue.Equals(expectedValue, StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
output.WriteLine("Index Expected Actual");
|
||||
output.WriteLine("----------------------------");
|
||||
int maxLen = Math.Max(actualValue.Length, expectedValue.Length);
|
||||
int minLen = Math.Min(actualValue.Length, expectedValue.Length);
|
||||
|
||||
if (diffStyle != DiffStyle.Minimal)
|
||||
{
|
||||
int startDifferAt = 0;
|
||||
for (int i = 0; i < maxLen; i++)
|
||||
{
|
||||
if (i >= minLen || actualValue[i] != expectedValue[i])
|
||||
{
|
||||
startDifferAt = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var endDifferAt = Math.Min(startDifferAt + 10, maxLen);
|
||||
startDifferAt = Math.Max(startDifferAt - 10, 0);
|
||||
|
||||
bool isFirstDiff = true;
|
||||
for (int i = startDifferAt; i < endDifferAt; i++)
|
||||
{
|
||||
if (i >= minLen || actualValue[i] != expectedValue[i])
|
||||
{
|
||||
output.WriteLine("{0,-3} {1,-3} {2,-4} {3,-3} {4,-4} {5,-3}",
|
||||
i < minLen && actualValue[i] == expectedValue[i] ? " " : isFirstDiff ? ">>>": "***",
|
||||
// put a mark beside a differing row
|
||||
i, // the index
|
||||
i < expectedValue.Length ? ((int) expectedValue[i]).ToString() : "",
|
||||
// character decimal value
|
||||
i < expectedValue.Length ? expectedValue[i].ToSafeString() : "", // character safe string
|
||||
i < actualValue.Length ? ((int) actualValue[i]).ToString() : "", // character decimal value
|
||||
i < actualValue.Length ? actualValue[i].ToSafeString() : "" // character safe string
|
||||
);
|
||||
|
||||
isFirstDiff = false;
|
||||
}
|
||||
}
|
||||
//output.WriteLine();
|
||||
}
|
||||
|
||||
Assert.AreEqual(expectedValue, actualValue);
|
||||
return;
|
||||
}
|
||||
|
||||
private static string ToSafeString(this char c)
|
||||
if (actualValue.Equals(expectedValue, StringComparison.Ordinal))
|
||||
{
|
||||
if (char.IsControl(c) || char.IsWhiteSpace(c))
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
output.WriteLine("Index Expected Actual");
|
||||
output.WriteLine("----------------------------");
|
||||
int maxLen = Math.Max(actualValue.Length, expectedValue.Length);
|
||||
int minLen = Math.Min(actualValue.Length, expectedValue.Length);
|
||||
|
||||
if (diffStyle != DiffStyle.Minimal)
|
||||
{
|
||||
int startDifferAt = 0;
|
||||
for (int i = 0; i < maxLen; i++)
|
||||
{
|
||||
switch (c)
|
||||
if (i >= minLen || actualValue[i] != expectedValue[i])
|
||||
{
|
||||
case '\b':
|
||||
return @"\b";
|
||||
case '\r':
|
||||
return @"\r";
|
||||
case '\n':
|
||||
return @"\n";
|
||||
case '\t':
|
||||
return @"\t";
|
||||
case '\a':
|
||||
return @"\a";
|
||||
case '\v':
|
||||
return @"\v";
|
||||
case '\f':
|
||||
return @"\f";
|
||||
default:
|
||||
return $"\\u{(int) c:X};";
|
||||
startDifferAt = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return c.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
var endDifferAt = Math.Min(startDifferAt + 10, maxLen);
|
||||
startDifferAt = Math.Max(startDifferAt - 10, 0);
|
||||
|
||||
bool isFirstDiff = true;
|
||||
for (int i = startDifferAt; i < endDifferAt; i++)
|
||||
{
|
||||
if (i >= minLen || actualValue[i] != expectedValue[i])
|
||||
{
|
||||
output.WriteLine("{0,-3} {1,-3} {2,-4} {3,-3} {4,-4} {5,-3}",
|
||||
i < minLen && actualValue[i] == expectedValue[i] ? " " : isFirstDiff ? ">>>": "***",
|
||||
// put a mark beside a differing row
|
||||
i, // the index
|
||||
i < expectedValue.Length ? ((int) expectedValue[i]).ToString() : "",
|
||||
// character decimal value
|
||||
i < expectedValue.Length ? expectedValue[i].ToSafeString() : "", // character safe string
|
||||
i < actualValue.Length ? ((int) actualValue[i]).ToString() : "", // character decimal value
|
||||
i < actualValue.Length ? actualValue[i].ToSafeString() : "" // character safe string
|
||||
);
|
||||
|
||||
isFirstDiff = false;
|
||||
}
|
||||
}
|
||||
//output.WriteLine();
|
||||
}
|
||||
|
||||
Assert.AreEqual(expectedValue, actualValue);
|
||||
}
|
||||
|
||||
private static string ToSafeString(this char c)
|
||||
{
|
||||
if (char.IsControl(c) || char.IsWhiteSpace(c))
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\b':
|
||||
return @"\b";
|
||||
case '\r':
|
||||
return @"\r";
|
||||
case '\n':
|
||||
return @"\n";
|
||||
case '\t':
|
||||
return @"\t";
|
||||
case '\a':
|
||||
return @"\a";
|
||||
case '\v':
|
||||
return @"\v";
|
||||
case '\f':
|
||||
return @"\f";
|
||||
default:
|
||||
return $"\\u{(int) c:X};";
|
||||
}
|
||||
}
|
||||
return c.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +1,54 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Markdig.WebApp
|
||||
namespace Markdig.WebApp;
|
||||
|
||||
public class ApiController : Controller
|
||||
{
|
||||
public class ApiController : Controller
|
||||
[HttpGet()]
|
||||
[Route("")]
|
||||
public new string Empty()
|
||||
{
|
||||
[HttpGet()]
|
||||
[Route("")]
|
||||
public string Empty()
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// GET api/to_html?text=xxx&extension=advanced
|
||||
[Route("api/to_html")]
|
||||
[HttpGet()]
|
||||
public object Get([FromQuery] string text, [FromQuery] string extension)
|
||||
{
|
||||
try
|
||||
{
|
||||
return string.Empty;
|
||||
if (text == null)
|
||||
{
|
||||
text = string.Empty;
|
||||
}
|
||||
|
||||
if (text.Length > 1000)
|
||||
{
|
||||
text = text.Substring(0, 1000);
|
||||
}
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder().Configure(extension).Build();
|
||||
var result = Markdown.ToHtml(text, pipeline);
|
||||
|
||||
return new {name = "markdig", html = result, version = Markdown.Version};
|
||||
}
|
||||
|
||||
// GET api/to_html?text=xxx&extension=advanced
|
||||
[Route("api/to_html")]
|
||||
[HttpGet()]
|
||||
public object Get([FromQuery] string text, [FromQuery] string extension)
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (text == null)
|
||||
{
|
||||
text = string.Empty;
|
||||
}
|
||||
|
||||
if (text.Length > 1000)
|
||||
{
|
||||
text = text.Substring(0, 1000);
|
||||
}
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder().Configure(extension).Build();
|
||||
var result = Markdown.ToHtml(text, pipeline);
|
||||
|
||||
return new {name = "markdig", html = result, version = Markdown.Version};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new { name = "markdig", html = "exception: " + GetPrettyMessageFromException(ex), version = Markdown.Version };
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetPrettyMessageFromException(Exception exception)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
while (exception != null)
|
||||
{
|
||||
builder.Append(exception.Message);
|
||||
exception = exception.InnerException;
|
||||
}
|
||||
return builder.ToString();
|
||||
return new { name = "markdig", html = "exception: " + GetPrettyMessageFromException(ex), version = Markdown.Version };
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetPrettyMessageFromException(Exception exception)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
while (exception != null)
|
||||
{
|
||||
builder.Append(exception.Message);
|
||||
exception = exception.InnerException;
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<AssemblyName>Markdig.WebApp</AssemblyName>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<OutputType>Exe</OutputType>
|
||||
<PackageId>Markdig.WebApp</PackageId>
|
||||
<ApplicationInsightsResourceId>/subscriptions/b6745039-70e7-4641-994b-5457cb220e2a/resourcegroups/Default-ApplicationInsights-EastUS/providers/microsoft.insights/components/Markdig.WebApp</ApplicationInsightsResourceId>
|
||||
@@ -13,7 +14,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.20.0" />
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
namespace Markdig.WebApp;
|
||||
|
||||
namespace Markdig.WebApp
|
||||
public class Program
|
||||
{
|
||||
public class Program
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,60 +1,52 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
namespace Markdig.WebApp;
|
||||
|
||||
namespace Markdig.WebApp
|
||||
public class Startup
|
||||
{
|
||||
public class Startup
|
||||
public Startup(IWebHostEnvironment env)
|
||||
{
|
||||
public Startup(IWebHostEnvironment env)
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
|
||||
|
||||
if (env.IsEnvironment("Development"))
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
|
||||
|
||||
if (env.IsEnvironment("Development"))
|
||||
{
|
||||
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
|
||||
builder.AddApplicationInsightsSettings(developerMode: true);
|
||||
}
|
||||
|
||||
builder.AddEnvironmentVariables();
|
||||
Configuration = builder.Build();
|
||||
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
|
||||
builder.AddApplicationInsightsSettings(connectionString: null, developerMode: true);
|
||||
}
|
||||
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
builder.AddEnvironmentVariables();
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
public IConfigurationRoot Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddControllers();
|
||||
|
||||
// Add framework services.
|
||||
services.AddApplicationInsightsTelemetry(Configuration);
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
services.AddControllers();
|
||||
|
||||
// Add framework services.
|
||||
services.AddApplicationInsightsTelemetry(Configuration);
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
}
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,36 +7,35 @@ using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Extensions.Abbreviations
|
||||
namespace Markdig.Extensions.Abbreviations;
|
||||
|
||||
/// <summary>
|
||||
/// An abbreviation object stored at the document level. See extension methods in <see cref="AbbreviationHelper"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="LeafBlock" />
|
||||
[DebuggerDisplay("Abbr {Label} => {Text}")]
|
||||
public class Abbreviation : LeafBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// An abbreviation object stored at the document level. See extension methods in <see cref="AbbreviationHelper"/>.
|
||||
/// Initializes a new instance of the <see cref="Abbreviation"/> class.
|
||||
/// </summary>
|
||||
/// <seealso cref="LeafBlock" />
|
||||
[DebuggerDisplay("Abbr {Label} => {Text}")]
|
||||
public class Abbreviation : LeafBlock
|
||||
/// <param name="parser">The parser used to create this block.</param>
|
||||
public Abbreviation(BlockParser parser) : base(parser)
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Abbreviation"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parser">The parser used to create this block.</param>
|
||||
public Abbreviation(BlockParser parser) : base(parser)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the label.
|
||||
/// </summary>
|
||||
public string? Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The text associated to this label.
|
||||
/// </summary>
|
||||
public StringSlice Text;
|
||||
|
||||
/// <summary>
|
||||
/// The label span
|
||||
/// </summary>
|
||||
public SourceSpan LabelSpan;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the label.
|
||||
/// </summary>
|
||||
public string? Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The text associated to this label.
|
||||
/// </summary>
|
||||
public StringSlice Text;
|
||||
|
||||
/// <summary>
|
||||
/// The label span
|
||||
/// </summary>
|
||||
public SourceSpan LabelSpan;
|
||||
}
|
||||
@@ -4,26 +4,25 @@
|
||||
|
||||
using Markdig.Renderers;
|
||||
|
||||
namespace Markdig.Extensions.Abbreviations
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension to allow abbreviations.
|
||||
/// </summary>
|
||||
/// <seealso cref="IMarkdownExtension" />
|
||||
public class AbbreviationExtension : IMarkdownExtension
|
||||
{
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
pipeline.BlockParsers.AddIfNotAlready<AbbreviationParser>();
|
||||
}
|
||||
namespace Markdig.Extensions.Abbreviations;
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
/// <summary>
|
||||
/// Extension to allow abbreviations.
|
||||
/// </summary>
|
||||
/// <seealso cref="IMarkdownExtension" />
|
||||
public class AbbreviationExtension : IMarkdownExtension
|
||||
{
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
pipeline.BlockParsers.AddIfNotAlready<AbbreviationParser>();
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
if (renderer is HtmlRenderer htmlRenderer && !htmlRenderer.ObjectRenderers.Contains<HtmlAbbreviationRenderer>())
|
||||
{
|
||||
if (renderer is HtmlRenderer htmlRenderer && !htmlRenderer.ObjectRenderers.Contains<HtmlAbbreviationRenderer>())
|
||||
{
|
||||
// Must be inserted before CodeBlockRenderer
|
||||
htmlRenderer.ObjectRenderers.Insert(0, new HtmlAbbreviationRenderer());
|
||||
}
|
||||
// Must be inserted before CodeBlockRenderer
|
||||
htmlRenderer.ObjectRenderers.Insert(0, new HtmlAbbreviationRenderer());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,42 +2,40 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Extensions.Abbreviations
|
||||
namespace Markdig.Extensions.Abbreviations;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="Abbreviation"/>.
|
||||
/// </summary>
|
||||
public static class AbbreviationHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="Abbreviation"/>.
|
||||
/// </summary>
|
||||
public static class AbbreviationHelper
|
||||
private static readonly object DocumentKey = typeof (Abbreviation);
|
||||
|
||||
public static bool HasAbbreviations(this MarkdownDocument document)
|
||||
{
|
||||
private static readonly object DocumentKey = typeof (Abbreviation);
|
||||
return document.GetAbbreviations() != null;
|
||||
}
|
||||
|
||||
public static bool HasAbbreviations(this MarkdownDocument document)
|
||||
public static void AddAbbreviation(this MarkdownDocument document, string label, Abbreviation abbr)
|
||||
{
|
||||
if (document is null) ThrowHelper.ArgumentNullException(nameof(document));
|
||||
if (label is null) ThrowHelper.ArgumentNullException_label();
|
||||
if (abbr is null) ThrowHelper.ArgumentNullException(nameof(abbr));
|
||||
|
||||
var map = document.GetAbbreviations();
|
||||
if (map is null)
|
||||
{
|
||||
return document.GetAbbreviations() != null;
|
||||
map = new Dictionary<string, Abbreviation>();
|
||||
document.SetData(DocumentKey, map);
|
||||
}
|
||||
map[label] = abbr;
|
||||
}
|
||||
|
||||
public static void AddAbbreviation(this MarkdownDocument document, string label, Abbreviation abbr)
|
||||
{
|
||||
if (document is null) ThrowHelper.ArgumentNullException(nameof(document));
|
||||
if (label is null) ThrowHelper.ArgumentNullException_label();
|
||||
if (abbr is null) ThrowHelper.ArgumentNullException(nameof(abbr));
|
||||
|
||||
var map = document.GetAbbreviations();
|
||||
if (map is null)
|
||||
{
|
||||
map = new Dictionary<string, Abbreviation>();
|
||||
document.SetData(DocumentKey, map);
|
||||
}
|
||||
map[label] = abbr;
|
||||
}
|
||||
|
||||
public static Dictionary<string, Abbreviation>? GetAbbreviations(this MarkdownDocument document)
|
||||
{
|
||||
return document.GetData(DocumentKey) as Dictionary<string, Abbreviation>;
|
||||
}
|
||||
public static Dictionary<string, Abbreviation>? GetAbbreviations(this MarkdownDocument document)
|
||||
{
|
||||
return document.GetData(DocumentKey) as Dictionary<string, Abbreviation>;
|
||||
}
|
||||
}
|
||||
@@ -5,24 +5,23 @@
|
||||
using System.Diagnostics;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.Abbreviations
|
||||
namespace Markdig.Extensions.Abbreviations;
|
||||
|
||||
/// <summary>
|
||||
/// The inline abbreviation.
|
||||
/// </summary>
|
||||
/// <seealso cref="LeafInline" />
|
||||
[DebuggerDisplay("{Abbreviation}")]
|
||||
public class AbbreviationInline : LeafInline
|
||||
{
|
||||
/// <summary>
|
||||
/// The inline abbreviation.
|
||||
/// Initializes a new instance of the <see cref="AbbreviationInline"/> class.
|
||||
/// </summary>
|
||||
/// <seealso cref="LeafInline" />
|
||||
[DebuggerDisplay("{Abbreviation}")]
|
||||
public class AbbreviationInline : LeafInline
|
||||
/// <param name="abbreviation">The abbreviation.</param>
|
||||
public AbbreviationInline(Abbreviation abbreviation)
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbbreviationInline"/> class.
|
||||
/// </summary>
|
||||
/// <param name="abbreviation">The abbreviation.</param>
|
||||
public AbbreviationInline(Abbreviation abbreviation)
|
||||
{
|
||||
Abbreviation = abbreviation;
|
||||
}
|
||||
|
||||
public Abbreviation Abbreviation { get; set; }
|
||||
Abbreviation = abbreviation;
|
||||
}
|
||||
|
||||
public Abbreviation Abbreviation { get; set; }
|
||||
}
|
||||
@@ -2,225 +2,220 @@
|
||||
// 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;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.Abbreviations
|
||||
namespace Markdig.Extensions.Abbreviations;
|
||||
|
||||
/// <summary>
|
||||
/// A block parser for abbreviations.
|
||||
/// </summary>
|
||||
/// <seealso cref="BlockParser" />
|
||||
public class AbbreviationParser : BlockParser
|
||||
{
|
||||
/// <summary>
|
||||
/// A block parser for abbreviations.
|
||||
/// Initializes a new instance of the <see cref="AbbreviationParser"/> class.
|
||||
/// </summary>
|
||||
/// <seealso cref="BlockParser" />
|
||||
public class AbbreviationParser : BlockParser
|
||||
public AbbreviationParser()
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbbreviationParser"/> class.
|
||||
/// </summary>
|
||||
public AbbreviationParser()
|
||||
OpeningCharacters = ['*'];
|
||||
}
|
||||
|
||||
public override BlockState TryOpen(BlockProcessor processor)
|
||||
{
|
||||
if (processor.IsCodeIndent)
|
||||
{
|
||||
OpeningCharacters = new[] { '*' };
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
public override BlockState TryOpen(BlockProcessor processor)
|
||||
// A link must be of the form *[Some Text]: An abbreviation
|
||||
var slice = processor.Line;
|
||||
var startPosition = slice.Start;
|
||||
var c = slice.NextChar();
|
||||
if (c != '[')
|
||||
{
|
||||
if (processor.IsCodeIndent)
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
// A link must be of the form *[Some Text]: An abbreviation
|
||||
var slice = processor.Line;
|
||||
var startPosition = slice.Start;
|
||||
var c = slice.NextChar();
|
||||
if (c != '[')
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
if (!LinkHelper.TryParseLabel(ref slice, out string? label, out SourceSpan labelSpan))
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
c = slice.CurrentChar;
|
||||
if (c != ':')
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
slice.SkipChar();
|
||||
|
||||
slice.Trim();
|
||||
|
||||
var abbr = new Abbreviation(this)
|
||||
{
|
||||
Label = label,
|
||||
Text = slice,
|
||||
Span = new SourceSpan(startPosition, slice.End),
|
||||
Line = processor.LineIndex,
|
||||
Column = processor.Column,
|
||||
LabelSpan = labelSpan,
|
||||
};
|
||||
if (!processor.Document.HasAbbreviations())
|
||||
{
|
||||
processor.Document.ProcessInlinesBegin += DocumentOnProcessInlinesBegin;
|
||||
}
|
||||
processor.Document.AddAbbreviation(abbr.Label, abbr);
|
||||
|
||||
return BlockState.BreakDiscard;
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
private void DocumentOnProcessInlinesBegin(InlineProcessor inlineProcessor, Inline? inline)
|
||||
if (!LinkHelper.TryParseLabel(ref slice, out string? label, out SourceSpan labelSpan))
|
||||
{
|
||||
inlineProcessor.Document.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
var abbreviations = inlineProcessor.Document.GetAbbreviations();
|
||||
// Should not happen, but another extension could decide to remove them, so...
|
||||
if (abbreviations is null)
|
||||
c = slice.CurrentChar;
|
||||
if (c != ':')
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
slice.SkipChar();
|
||||
|
||||
slice.Trim();
|
||||
|
||||
var abbr = new Abbreviation(this)
|
||||
{
|
||||
Label = label,
|
||||
Text = slice,
|
||||
Span = new SourceSpan(startPosition, slice.End),
|
||||
Line = processor.LineIndex,
|
||||
Column = processor.Column,
|
||||
LabelSpan = labelSpan,
|
||||
};
|
||||
if (!processor.Document.HasAbbreviations())
|
||||
{
|
||||
processor.Document.ProcessInlinesBegin += DocumentOnProcessInlinesBegin;
|
||||
}
|
||||
processor.Document.AddAbbreviation(abbr.Label, abbr);
|
||||
|
||||
return BlockState.BreakDiscard;
|
||||
}
|
||||
|
||||
private void DocumentOnProcessInlinesBegin(InlineProcessor inlineProcessor, Inline? inline)
|
||||
{
|
||||
inlineProcessor.Document.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;
|
||||
|
||||
var abbreviations = inlineProcessor.Document.GetAbbreviations();
|
||||
// Should not happen, but another extension could decide to remove them, so...
|
||||
if (abbreviations is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a text matcher from the abbreviations labels
|
||||
var prefixTree = new CompactPrefixTree<Abbreviation>(abbreviations);
|
||||
|
||||
inlineProcessor.LiteralInlineParser.PostMatch += (InlineProcessor processor, ref StringSlice slice) =>
|
||||
{
|
||||
var literal = (LiteralInline)processor.Inline!;
|
||||
var originalLiteral = literal;
|
||||
var originalSpanEnd = literal.Span.End;
|
||||
|
||||
ContainerInline? container = null;
|
||||
|
||||
// This is slow, but we don't have much the choice
|
||||
var content = literal.Content;
|
||||
var text = content.Text;
|
||||
|
||||
for (int i = content.Start; i <= content.End; i++)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Build a text matcher from the abbreviations labels
|
||||
var prefixTree = new CompactPrefixTree<Abbreviation>(abbreviations);
|
||||
|
||||
inlineProcessor.LiteralInlineParser.PostMatch += (InlineProcessor processor, ref StringSlice slice) =>
|
||||
{
|
||||
var literal = (LiteralInline)processor.Inline!;
|
||||
var originalLiteral = literal;
|
||||
|
||||
ContainerInline? container = null;
|
||||
|
||||
// This is slow, but we don't have much the choice
|
||||
var content = literal.Content;
|
||||
var text = content.Text;
|
||||
|
||||
for (int i = content.Start; i <= content.End; i++)
|
||||
// Abbreviation must be a whole word == start at the start of a line or after a whitespace
|
||||
if (i != 0)
|
||||
{
|
||||
// Abbreviation must be a whole word == start at the start of a line or after a whitespace
|
||||
if (i != 0)
|
||||
for (i = i - 1; i <= content.End; i++)
|
||||
{
|
||||
for (i = i - 1; i <= content.End; i++)
|
||||
if (text[i].IsWhitespace())
|
||||
{
|
||||
if (text[i].IsWhitespace())
|
||||
{
|
||||
i++;
|
||||
goto ValidAbbreviationStart;
|
||||
}
|
||||
i++;
|
||||
goto ValidAbbreviationStart;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ValidAbbreviationStart:;
|
||||
|
||||
if (prefixTree.TryMatchLongest(text.AsSpan(i, content.End - i + 1), out KeyValuePair<string, Abbreviation> abbreviationMatch))
|
||||
{
|
||||
var match = abbreviationMatch.Key;
|
||||
if (!IsValidAbbreviationEnding(match, content, i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var indexAfterMatch = i + match.Length;
|
||||
|
||||
// If we don't have a container, create a new one
|
||||
if (container is null)
|
||||
{
|
||||
container = literal.Parent ??
|
||||
new ContainerInline
|
||||
{
|
||||
Span = originalLiteral.Span,
|
||||
Line = originalLiteral.Line,
|
||||
Column = originalLiteral.Column,
|
||||
};
|
||||
}
|
||||
|
||||
var abbrInline = new AbbreviationInline(abbreviationMatch.Value)
|
||||
{
|
||||
Span =
|
||||
{
|
||||
Start = processor.GetSourcePosition(i, out int line, out int column),
|
||||
},
|
||||
Line = line,
|
||||
Column = column
|
||||
};
|
||||
abbrInline.Span.End = abbrInline.Span.Start + match.Length - 1;
|
||||
|
||||
// Append the previous literal
|
||||
if (i > content.Start && literal.Parent is null)
|
||||
{
|
||||
container.AppendChild(literal);
|
||||
}
|
||||
|
||||
literal.Span.End = abbrInline.Span.Start - 1;
|
||||
// Truncate it before the abbreviation
|
||||
literal.Content.End = i - 1;
|
||||
|
||||
|
||||
// Append the abbreviation
|
||||
container.AppendChild(abbrInline);
|
||||
|
||||
// If this is the end of the string, clear the literal and exit
|
||||
if (content.End == indexAfterMatch - 1)
|
||||
{
|
||||
literal = null;
|
||||
break;
|
||||
}
|
||||
|
||||
// Process the remaining literal
|
||||
literal = new LiteralInline()
|
||||
{
|
||||
Span = new SourceSpan(abbrInline.Span.End + 1, literal.Span.End),
|
||||
Line = line,
|
||||
Column = column + match.Length,
|
||||
};
|
||||
content.Start = indexAfterMatch;
|
||||
literal.Content = content;
|
||||
|
||||
i = indexAfterMatch - 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (container != null)
|
||||
ValidAbbreviationStart:;
|
||||
|
||||
if (prefixTree.TryMatchLongest(text.AsSpan(i, content.End - i + 1), out KeyValuePair<string, Abbreviation> abbreviationMatch))
|
||||
{
|
||||
if (literal != null)
|
||||
var match = abbreviationMatch.Key;
|
||||
if (!IsValidAbbreviationEnding(match, content, i))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var indexAfterMatch = i + match.Length;
|
||||
|
||||
// If we don't have a container, create a new one
|
||||
if (container is null)
|
||||
{
|
||||
container = literal.Parent ??
|
||||
new ContainerInline
|
||||
{
|
||||
Span = originalLiteral.Span,
|
||||
Line = originalLiteral.Line,
|
||||
Column = originalLiteral.Column,
|
||||
};
|
||||
}
|
||||
|
||||
var abbrInline = new AbbreviationInline(abbreviationMatch.Value)
|
||||
{
|
||||
Span =
|
||||
{
|
||||
Start = processor.GetSourcePosition(i, out int line, out int column),
|
||||
},
|
||||
Line = line,
|
||||
Column = column
|
||||
};
|
||||
abbrInline.Span.End = abbrInline.Span.Start + match.Length - 1;
|
||||
|
||||
// Append the previous literal
|
||||
if (i > content.Start && literal.Parent is null)
|
||||
{
|
||||
container.AppendChild(literal);
|
||||
}
|
||||
processor.Inline = container;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsValidAbbreviationEnding(string match, StringSlice content, int matchIndex)
|
||||
{
|
||||
// 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;
|
||||
int index = matchIndex + match.Length;
|
||||
while (index <= contentNew.End)
|
||||
{
|
||||
var c = contentNew.PeekCharAbsolute(index);
|
||||
if (!(c == '\0' || c.IsWhitespace() || c.IsAsciiPunctuation()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
literal.Span.End = abbrInline.Span.Start - 1;
|
||||
// Truncate it before the abbreviation
|
||||
literal.Content.End = i - 1;
|
||||
|
||||
if (c.IsAlphaNumeric())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c.IsWhitespace())
|
||||
{
|
||||
break;
|
||||
// Append the abbreviation
|
||||
container.AppendChild(abbrInline);
|
||||
|
||||
// If this is the end of the string, clear the literal and exit
|
||||
if (content.End == indexAfterMatch - 1)
|
||||
{
|
||||
literal = null;
|
||||
break;
|
||||
}
|
||||
|
||||
// Process the remaining literal
|
||||
literal = new LiteralInline()
|
||||
{
|
||||
Span = new SourceSpan(abbrInline.Span.End + 1, originalSpanEnd),
|
||||
Line = line,
|
||||
Column = column + match.Length,
|
||||
};
|
||||
content.Start = indexAfterMatch;
|
||||
literal.Content = content;
|
||||
|
||||
i = indexAfterMatch - 1;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return true;
|
||||
|
||||
if (container != null)
|
||||
{
|
||||
if (literal != null)
|
||||
{
|
||||
container.AppendChild(literal);
|
||||
}
|
||||
processor.Inline = container;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsValidAbbreviationEnding(string match, StringSlice content, int matchIndex)
|
||||
{
|
||||
// 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;
|
||||
int index = matchIndex + match.Length;
|
||||
while (index <= contentNew.End)
|
||||
{
|
||||
var c = contentNew.PeekCharAbsolute(index);
|
||||
|
||||
if (c.IsWhitespace())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!c.IsAsciiPunctuationOrZero())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -5,27 +5,26 @@
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
|
||||
namespace Markdig.Extensions.Abbreviations
|
||||
namespace Markdig.Extensions.Abbreviations;
|
||||
|
||||
/// <summary>
|
||||
/// A HTML renderer for a <see cref="AbbreviationInline"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="HtmlObjectRenderer{CustomContainer}" />
|
||||
public class HtmlAbbreviationRenderer : HtmlObjectRenderer<AbbreviationInline>
|
||||
{
|
||||
/// <summary>
|
||||
/// A HTML renderer for a <see cref="AbbreviationInline"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="HtmlObjectRenderer{CustomContainer}" />
|
||||
public class HtmlAbbreviationRenderer : HtmlObjectRenderer<AbbreviationInline>
|
||||
protected override void Write(HtmlRenderer renderer, AbbreviationInline obj)
|
||||
{
|
||||
protected override void Write(HtmlRenderer renderer, AbbreviationInline obj)
|
||||
// <abbr title="Hyper Text Markup Language">HTML</abbr>
|
||||
var abbr = obj.Abbreviation;
|
||||
if (renderer.EnableHtmlForInline)
|
||||
{
|
||||
// <abbr title="Hyper Text Markup Language">HTML</abbr>
|
||||
var abbr = obj.Abbreviation;
|
||||
if (renderer.EnableHtmlForInline)
|
||||
{
|
||||
renderer.Write("<abbr").WriteAttributes(obj).Write(" title=\"").WriteEscape(ref abbr.Text).Write("\">");
|
||||
}
|
||||
renderer.Write(abbr.Label);
|
||||
if (renderer.EnableHtmlForInline)
|
||||
{
|
||||
renderer.Write("</abbr>");
|
||||
}
|
||||
renderer.Write("<abbr").WriteAttributes(obj).Write(" title=\"").WriteEscape(ref abbr.Text).Write("\">");
|
||||
}
|
||||
renderer.Write(abbr.Label);
|
||||
if (renderer.EnableHtmlForInline)
|
||||
{
|
||||
renderer.Write("</abbr>");
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/Markdig/Extensions/Alerts/AlertBlock.cs
Normal file
33
src/Markdig/Extensions/Alerts/AlertBlock.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// 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.Syntax;
|
||||
|
||||
namespace Markdig.Extensions.Alerts;
|
||||
|
||||
/// <summary>
|
||||
/// A block representing an alert quote block.
|
||||
/// </summary>
|
||||
public class AlertBlock : QuoteBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of this block.
|
||||
/// </summary>
|
||||
/// <param name="kind"></param>
|
||||
public AlertBlock(StringSlice kind) : base(null)
|
||||
{
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the kind of the alert block (e.g `NOTE`, `TIP`, `IMPORTANT`, `WARNING`, `CAUTION`).
|
||||
/// </summary>
|
||||
public StringSlice Kind { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the trivia space after the kind.
|
||||
/// </summary>
|
||||
public StringSlice TriviaSpaceAfterKind { get; set; }
|
||||
}
|
||||
86
src/Markdig/Extensions/Alerts/AlertBlockRenderer.cs
Normal file
86
src/Markdig/Extensions/Alerts/AlertBlockRenderer.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
// 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.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
|
||||
namespace Markdig.Extensions.Alerts;
|
||||
|
||||
/// <summary>
|
||||
/// A HTML renderer for a <see cref="AlertBlock"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="HtmlObjectRenderer{AlertBlock}" />
|
||||
public class AlertBlockRenderer : HtmlObjectRenderer<AlertBlock>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of this renderer.
|
||||
/// </summary>
|
||||
public AlertBlockRenderer()
|
||||
{
|
||||
RenderKind = DefaultRenderKind;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets of sets a delegate to render the kind of the alert.
|
||||
/// </summary>
|
||||
public Action<HtmlRenderer, StringSlice> RenderKind { get; set; }
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Write(HtmlRenderer renderer, AlertBlock obj)
|
||||
{
|
||||
renderer.EnsureLine();
|
||||
if (renderer.EnableHtmlForBlock)
|
||||
{
|
||||
renderer.Write("<div");
|
||||
renderer.WriteAttributes(obj);
|
||||
renderer.WriteLine('>');
|
||||
}
|
||||
|
||||
RenderKind(renderer, obj.Kind);
|
||||
|
||||
var savedImplicitParagraph = renderer.ImplicitParagraph;
|
||||
renderer.ImplicitParagraph = false;
|
||||
renderer.WriteChildren(obj);
|
||||
renderer.ImplicitParagraph = savedImplicitParagraph;
|
||||
if (renderer.EnableHtmlForBlock)
|
||||
{
|
||||
renderer.WriteLine("</div>");
|
||||
}
|
||||
|
||||
renderer.EnsureLine();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Renders the kind of the alert.
|
||||
/// </summary>
|
||||
/// <param name="renderer">The HTML renderer.</param>
|
||||
/// <param name="kind">The kind of the alert to render</param>
|
||||
public static void DefaultRenderKind(HtmlRenderer renderer, StringSlice kind)
|
||||
{
|
||||
if (kind.Length >= 16)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Span<char> upperKind = stackalloc char[kind.Length];
|
||||
kind.AsSpan().ToUpperInvariant(upperKind);
|
||||
string? html = upperKind switch
|
||||
{
|
||||
"NOTE" => "<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Note</p>",
|
||||
"TIP" => "<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z\"></path></svg>Tip</p>",
|
||||
"IMPORTANT" => "<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"></path></svg>Important</p>",
|
||||
"WARNING" => "<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"></path></svg>Warning</p>",
|
||||
"CAUTION" => "<p class=\"markdown-alert-title\"><svg viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"><path d=\"M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z\"></path></svg>Caution</p>",
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (html is not null)
|
||||
{
|
||||
renderer.WriteLine(html);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/Markdig/Extensions/Alerts/AlertExtension.cs
Normal file
44
src/Markdig/Extensions/Alerts/AlertExtension.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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.Inlines;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
|
||||
namespace Markdig.Extensions.Alerts;
|
||||
|
||||
/// <summary>
|
||||
/// Extension for adding alerts to a Markdown pipeline.
|
||||
/// </summary>
|
||||
public class AlertExtension : IMarkdownExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the delegate to render the kind of the alert.
|
||||
/// </summary>
|
||||
public Action<HtmlRenderer, StringSlice>? RenderKind { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
var inlineParser = pipeline.InlineParsers.Find<AlertInlineParser>();
|
||||
if (inlineParser == null)
|
||||
{
|
||||
pipeline.InlineParsers.InsertBefore<LinkInlineParser>(new AlertInlineParser());
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var blockRenderer = renderer.ObjectRenderers.FindExact<AlertBlockRenderer>();
|
||||
if (blockRenderer == null)
|
||||
{
|
||||
renderer.ObjectRenderers.InsertBefore<QuoteBlockRenderer>(new AlertBlockRenderer()
|
||||
{
|
||||
RenderKind = RenderKind ?? AlertBlockRenderer.DefaultRenderKind
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
134
src/Markdig/Extensions/Alerts/AlertInlineParser.cs
Normal file
134
src/Markdig/Extensions/Alerts/AlertInlineParser.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
// 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.Alerts;
|
||||
|
||||
/// <summary>
|
||||
/// An inline parser for an alert inline (e.g. `[!NOTE]`).
|
||||
/// </summary>
|
||||
/// <seealso cref="InlineParser" />
|
||||
public class AlertInlineParser : InlineParser
|
||||
{
|
||||
private static readonly TransformedStringCache s_alertTypeClassCache = new(
|
||||
type => $"markdown-alert-{type.ToLowerInvariant()}");
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AlertInlineParser"/> class.
|
||||
/// </summary>
|
||||
public AlertInlineParser()
|
||||
{
|
||||
OpeningCharacters = ['['];
|
||||
}
|
||||
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
if (slice.PeekChar() != '!')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We expect the alert to be the first child of a quote block. Example:
|
||||
// > [!NOTE]
|
||||
// > This is a note
|
||||
if (processor.Block is not ParagraphBlock paragraphBlock ||
|
||||
paragraphBlock.Parent is not QuoteBlock quoteBlock ||
|
||||
paragraphBlock.Inline?.FirstChild != null ||
|
||||
quoteBlock is AlertBlock ||
|
||||
quoteBlock.Parent is not MarkdownDocument)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
StringSlice saved = slice;
|
||||
|
||||
slice.SkipChar(); // Skip [
|
||||
char c = slice.NextChar(); // Skip !
|
||||
|
||||
int start = slice.Start;
|
||||
int end = start;
|
||||
while (c.IsAlpha())
|
||||
{
|
||||
end = slice.Start;
|
||||
c = slice.NextChar();
|
||||
}
|
||||
|
||||
// We need at least one character
|
||||
if (c != ']' || start == end)
|
||||
{
|
||||
slice = saved;
|
||||
return false;
|
||||
}
|
||||
|
||||
var alertType = new StringSlice(slice.Text, start, end);
|
||||
c = slice.NextChar(); // Skip ]
|
||||
|
||||
start = slice.Start;
|
||||
while (true)
|
||||
{
|
||||
if (c == '\0' || c == '\n' || c == '\r')
|
||||
{
|
||||
end = slice.Start;
|
||||
if (c == '\r')
|
||||
{
|
||||
c = slice.NextChar(); // Skip \r
|
||||
if (c == '\0' || c == '\n')
|
||||
{
|
||||
end = slice.Start;
|
||||
if (c == '\n')
|
||||
{
|
||||
slice.SkipChar(); // Skip \n
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
slice.SkipChar(); // Skip \n
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (!c.IsSpaceOrTab())
|
||||
{
|
||||
slice = saved;
|
||||
return false;
|
||||
}
|
||||
|
||||
c = slice.NextChar();
|
||||
}
|
||||
|
||||
var alertBlock = new AlertBlock(alertType)
|
||||
{
|
||||
Span = quoteBlock.Span,
|
||||
TriviaSpaceAfterKind = new StringSlice(slice.Text, start, end),
|
||||
Line = quoteBlock.Line,
|
||||
Column = quoteBlock.Column,
|
||||
};
|
||||
|
||||
HtmlAttributes attributes = alertBlock.GetAttributes();
|
||||
attributes.AddClass("markdown-alert");
|
||||
attributes.AddClass(s_alertTypeClassCache.Get(alertType.AsSpan()));
|
||||
|
||||
// Replace the quote block with the alert block
|
||||
var parentQuoteBlock = quoteBlock.Parent!;
|
||||
var indexOfQuoteBlock = parentQuoteBlock.IndexOf(quoteBlock);
|
||||
parentQuoteBlock[indexOfQuoteBlock] = alertBlock;
|
||||
|
||||
while (quoteBlock.Count > 0)
|
||||
{
|
||||
var block = quoteBlock[0];
|
||||
quoteBlock.RemoveAt(0);
|
||||
alertBlock.Add(block);
|
||||
}
|
||||
|
||||
// Workaround to replace the parent container
|
||||
// Experimental API, so we are keeping it internal for now until we are sure it's the way we want to go
|
||||
processor.ReplaceParentContainer(quoteBlock, alertBlock);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,6 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Renderers;
|
||||
@@ -11,206 +9,214 @@ using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.AutoIdentifiers
|
||||
namespace Markdig.Extensions.AutoIdentifiers;
|
||||
|
||||
/// <summary>
|
||||
/// The auto-identifier extension
|
||||
/// </summary>
|
||||
/// <seealso cref="IMarkdownExtension" />
|
||||
public class AutoIdentifierExtension : IMarkdownExtension
|
||||
{
|
||||
private const string AutoIdentifierKey = "AutoIdentifier";
|
||||
|
||||
private static readonly StripRendererCache _rendererCache = new();
|
||||
|
||||
private readonly AutoIdentifierOptions _options;
|
||||
private readonly ProcessInlineDelegate _processInlinesBegin;
|
||||
private readonly ProcessInlineDelegate _processInlinesEnd;
|
||||
|
||||
/// <summary>
|
||||
/// The auto-identifier extension
|
||||
/// Initializes a new instance of the <see cref="AutoIdentifierExtension"/> class.
|
||||
/// </summary>
|
||||
/// <seealso cref="IMarkdownExtension" />
|
||||
public class AutoIdentifierExtension : IMarkdownExtension
|
||||
/// <param name="options">The options.</param>
|
||||
public AutoIdentifierExtension(AutoIdentifierOptions options)
|
||||
{
|
||||
private const string AutoIdentifierKey = "AutoIdentifier";
|
||||
private readonly AutoIdentifierOptions options;
|
||||
private readonly StripRendererCache rendererCache = new StripRendererCache();
|
||||
_options = options;
|
||||
_processInlinesBegin = DocumentOnProcessInlinesBegin;
|
||||
_processInlinesEnd = HeadingBlock_ProcessInlinesEnd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AutoIdentifierExtension"/> class.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
public AutoIdentifierExtension(AutoIdentifierOptions options)
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
var headingBlockParser = pipeline.BlockParsers.Find<HeadingBlockParser>();
|
||||
if (headingBlockParser != null)
|
||||
{
|
||||
this.options = options;
|
||||
// Install a hook on the HeadingBlockParser when a HeadingBlock is actually processed
|
||||
headingBlockParser.Closed -= HeadingBlockParser_Closed;
|
||||
headingBlockParser.Closed += HeadingBlockParser_Closed;
|
||||
}
|
||||
var paragraphBlockParser = pipeline.BlockParsers.FindExact<ParagraphBlockParser>();
|
||||
if (paragraphBlockParser != null)
|
||||
{
|
||||
// Install a hook on the ParagraphBlockParser when a HeadingBlock is actually processed as a Setex heading
|
||||
paragraphBlockParser.Closed -= HeadingBlockParser_Closed;
|
||||
paragraphBlockParser.Closed += HeadingBlockParser_Closed;
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process on a new <see cref="HeadingBlock"/>
|
||||
/// </summary>
|
||||
/// <param name="processor">The processor.</param>
|
||||
/// <param name="block">The heading block.</param>
|
||||
private void HeadingBlockParser_Closed(BlockProcessor processor, Block block)
|
||||
{
|
||||
// We may have a ParagraphBlock here as we have a hook on the ParagraphBlockParser
|
||||
if (!(block is HeadingBlock headingBlock))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
// If the AutoLink options is set, we register a LinkReferenceDefinition at the document level
|
||||
if ((_options & AutoIdentifierOptions.AutoLink) != 0)
|
||||
{
|
||||
var headingBlockParser = pipeline.BlockParsers.Find<HeadingBlockParser>();
|
||||
if (headingBlockParser != null)
|
||||
var headingLine = headingBlock.Lines.Lines[0];
|
||||
|
||||
var text = headingLine.ToString();
|
||||
|
||||
var linkRef = new HeadingLinkReferenceDefinition(headingBlock)
|
||||
{
|
||||
// Install a hook on the HeadingBlockParser when a HeadingBlock is actually processed
|
||||
headingBlockParser.Closed -= HeadingBlockParser_Closed;
|
||||
headingBlockParser.Closed += HeadingBlockParser_Closed;
|
||||
}
|
||||
var paragraphBlockParser = pipeline.BlockParsers.FindExact<ParagraphBlockParser>();
|
||||
if (paragraphBlockParser != null)
|
||||
{
|
||||
// Install a hook on the ParagraphBlockParser when a HeadingBlock is actually processed as a Setex heading
|
||||
paragraphBlockParser.Closed -= HeadingBlockParser_Closed;
|
||||
paragraphBlockParser.Closed += HeadingBlockParser_Closed;
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process on a new <see cref="HeadingBlock"/>
|
||||
/// </summary>
|
||||
/// <param name="processor">The processor.</param>
|
||||
/// <param name="block">The heading block.</param>
|
||||
private void HeadingBlockParser_Closed(BlockProcessor processor, Block block)
|
||||
{
|
||||
// We may have a ParagraphBlock here as we have a hook on the ParagraphBlockParser
|
||||
if (!(block is HeadingBlock headingBlock))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the AutoLink options is set, we register a LinkReferenceDefinition at the document level
|
||||
if ((options & AutoIdentifierOptions.AutoLink) != 0)
|
||||
{
|
||||
var headingLine = headingBlock.Lines.Lines[0];
|
||||
|
||||
var text = headingLine.ToString();
|
||||
|
||||
var linkRef = new HeadingLinkReferenceDefinition(headingBlock)
|
||||
{
|
||||
CreateLinkInline = CreateLinkInlineForHeading
|
||||
};
|
||||
|
||||
var doc = processor.Document;
|
||||
var dictionary = doc.GetData(this) as Dictionary<string, HeadingLinkReferenceDefinition>;
|
||||
if (dictionary is null)
|
||||
{
|
||||
dictionary = new Dictionary<string, HeadingLinkReferenceDefinition>();
|
||||
doc.SetData(this, dictionary);
|
||||
doc.ProcessInlinesBegin += DocumentOnProcessInlinesBegin;
|
||||
}
|
||||
dictionary[text] = linkRef;
|
||||
}
|
||||
|
||||
// Then we register after inline have been processed to actually generate the proper #id
|
||||
headingBlock.ProcessInlinesEnd += HeadingBlock_ProcessInlinesEnd;
|
||||
}
|
||||
|
||||
private void DocumentOnProcessInlinesBegin(InlineProcessor processor, Inline? inline)
|
||||
{
|
||||
var doc = processor.Document;
|
||||
doc.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;
|
||||
var dictionary = (Dictionary<string, HeadingLinkReferenceDefinition>)doc.GetData(this)!;
|
||||
foreach (var keyPair in dictionary)
|
||||
{
|
||||
// Here we make sure that auto-identifiers will not override an existing link definition
|
||||
// defined in the document
|
||||
// If it is the case, we skip the auto identifier for the Heading
|
||||
if (!doc.TryGetLinkReferenceDefinition(keyPair.Key, out var linkDef))
|
||||
{
|
||||
doc.SetLinkReferenceDefinition(keyPair.Key, keyPair.Value, true);
|
||||
}
|
||||
}
|
||||
// Once we are done, we don't need to keep the intermediate dictionary around
|
||||
doc.RemoveData(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback when there is a reference to found to a heading.
|
||||
/// Note that reference are only working if they are declared after.
|
||||
/// </summary>
|
||||
private Inline CreateLinkInlineForHeading(InlineProcessor inlineState, LinkReferenceDefinition linkRef, Inline? child)
|
||||
{
|
||||
var headingRef = (HeadingLinkReferenceDefinition) linkRef;
|
||||
return new LinkInline()
|
||||
{
|
||||
// Use GetDynamicUrl to allow late binding of the Url (as a link may occur before the heading is declared and
|
||||
// the inlines of the heading are actually processed by HeadingBlock_ProcessInlinesEnd)
|
||||
GetDynamicUrl = () => HtmlHelper.Unescape("#" + headingRef.Heading.GetAttributes().Id),
|
||||
Title = HtmlHelper.Unescape(linkRef.Title),
|
||||
CreateLinkInline = CreateLinkInlineForHeading
|
||||
};
|
||||
|
||||
var doc = processor.Document;
|
||||
var dictionary = doc.GetData(this) as Dictionary<string, HeadingLinkReferenceDefinition>;
|
||||
if (dictionary is null)
|
||||
{
|
||||
dictionary = new Dictionary<string, HeadingLinkReferenceDefinition>();
|
||||
doc.SetData(this, dictionary);
|
||||
doc.ProcessInlinesBegin += _processInlinesBegin;
|
||||
}
|
||||
dictionary[text] = linkRef;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the inlines of the heading to create a unique identifier
|
||||
/// </summary>
|
||||
/// <param name="processor">The processor.</param>
|
||||
/// <param name="inline">The inline.</param>
|
||||
private void HeadingBlock_ProcessInlinesEnd(InlineProcessor processor, Inline? inline)
|
||||
// Then we register after inline have been processed to actually generate the proper #id
|
||||
headingBlock.ProcessInlinesEnd += _processInlinesEnd;
|
||||
}
|
||||
|
||||
private void DocumentOnProcessInlinesBegin(InlineProcessor processor, Inline? inline)
|
||||
{
|
||||
var doc = processor.Document;
|
||||
doc.ProcessInlinesBegin -= _processInlinesBegin;
|
||||
var dictionary = (Dictionary<string, HeadingLinkReferenceDefinition>)doc.GetData(this)!;
|
||||
foreach (var keyPair in dictionary)
|
||||
{
|
||||
var identifiers = processor.Document.GetData(AutoIdentifierKey) as HashSet<string>;
|
||||
if (identifiers is null)
|
||||
// Here we make sure that auto-identifiers will not override an existing link definition
|
||||
// defined in the document
|
||||
// If it is the case, we skip the auto identifier for the Heading
|
||||
if (!doc.TryGetLinkReferenceDefinition(keyPair.Key, out var linkDef))
|
||||
{
|
||||
identifiers = new HashSet<string>();
|
||||
processor.Document.SetData(AutoIdentifierKey, identifiers);
|
||||
doc.SetLinkReferenceDefinition(keyPair.Key, keyPair.Value, true);
|
||||
}
|
||||
}
|
||||
// Once we are done, we don't need to keep the intermediate dictionary around
|
||||
doc.RemoveData(this);
|
||||
}
|
||||
|
||||
var headingBlock = (HeadingBlock) processor.Block!;
|
||||
if (headingBlock.Inline is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/// <summary>
|
||||
/// Callback when there is a reference to found to a heading.
|
||||
/// Note that reference are only working if they are declared after.
|
||||
/// </summary>
|
||||
private static Inline CreateLinkInlineForHeading(InlineProcessor inlineState, LinkReferenceDefinition linkRef, Inline? child)
|
||||
{
|
||||
var headingRef = (HeadingLinkReferenceDefinition) linkRef;
|
||||
return new LinkInline()
|
||||
{
|
||||
// Use GetDynamicUrl to allow late binding of the Url (as a link may occur before the heading is declared and
|
||||
// the inlines of the heading are actually processed by HeadingBlock_ProcessInlinesEnd)
|
||||
GetDynamicUrl = () => HtmlHelper.Unescape("#" + headingRef.Heading.GetAttributes().Id),
|
||||
Title = HtmlHelper.Unescape(linkRef.Title),
|
||||
};
|
||||
}
|
||||
|
||||
// If id is already set, don't try to modify it
|
||||
var attributes = processor.Block!.GetAttributes();
|
||||
if (attributes.Id != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Use internally a HtmlRenderer to strip links from a heading
|
||||
var stripRenderer = rendererCache.Get();
|
||||
|
||||
stripRenderer.Render(headingBlock.Inline);
|
||||
var headingText = stripRenderer.Writer.ToString()!;
|
||||
rendererCache.Release(stripRenderer);
|
||||
|
||||
// Urilize the link
|
||||
headingText = (options & AutoIdentifierOptions.GitHub) != 0
|
||||
? LinkHelper.UrilizeAsGfm(headingText)
|
||||
: LinkHelper.Urilize(headingText, (options & AutoIdentifierOptions.AllowOnlyAscii) != 0);
|
||||
|
||||
// If the heading is empty, use the word "section" instead
|
||||
var baseHeadingId = string.IsNullOrEmpty(headingText) ? "section" : headingText;
|
||||
|
||||
// Add a trailing -1, -2, -3...etc. in case of collision
|
||||
var headingId = baseHeadingId;
|
||||
if (!identifiers.Add(headingId))
|
||||
{
|
||||
var headingBuffer = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
headingBuffer.Append(baseHeadingId);
|
||||
headingBuffer.Append('-');
|
||||
uint index = 0;
|
||||
do
|
||||
{
|
||||
index++;
|
||||
headingBuffer.Append(index);
|
||||
headingId = headingBuffer.AsSpan().ToString();
|
||||
headingBuffer.Length = baseHeadingId.Length + 1;
|
||||
}
|
||||
while (!identifiers.Add(headingId));
|
||||
headingBuffer.Dispose();
|
||||
}
|
||||
|
||||
attributes.Id = headingId;
|
||||
/// <summary>
|
||||
/// Process the inlines of the heading to create a unique identifier
|
||||
/// </summary>
|
||||
/// <param name="processor">The processor.</param>
|
||||
/// <param name="inline">The inline.</param>
|
||||
private void HeadingBlock_ProcessInlinesEnd(InlineProcessor processor, Inline? inline)
|
||||
{
|
||||
var identifiers = processor.Document.GetData(AutoIdentifierKey) as HashSet<string>;
|
||||
if (identifiers is null)
|
||||
{
|
||||
identifiers = new HashSet<string>();
|
||||
processor.Document.SetData(AutoIdentifierKey, identifiers);
|
||||
}
|
||||
|
||||
private sealed class StripRendererCache : ObjectCache<HtmlRenderer>
|
||||
var headingBlock = (HeadingBlock) processor.Block!;
|
||||
if (headingBlock.Inline is null)
|
||||
{
|
||||
protected override HtmlRenderer NewInstance()
|
||||
{
|
||||
var headingWriter = new StringWriter();
|
||||
var stripRenderer = new HtmlRenderer(headingWriter)
|
||||
{
|
||||
// Set to false both to avoid having any HTML tags in the output
|
||||
EnableHtmlForInline = false,
|
||||
EnableHtmlEscape = false
|
||||
};
|
||||
return stripRenderer;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
protected override void Reset(HtmlRenderer instance)
|
||||
// If id is already set, don't try to modify it
|
||||
var attributes = processor.Block!.GetAttributes();
|
||||
if (attributes.Id != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Use internally a HtmlRenderer to strip links from a heading
|
||||
var stripRenderer = _rendererCache.Get();
|
||||
|
||||
stripRenderer.Render(headingBlock.Inline);
|
||||
ReadOnlySpan<char> rawHeadingText = ((FastStringWriter)stripRenderer.Writer).AsSpan();
|
||||
|
||||
// Urilize the link
|
||||
string headingText = (_options & AutoIdentifierOptions.GitHub) != 0
|
||||
? LinkHelper.UrilizeAsGfm(rawHeadingText)
|
||||
: LinkHelper.Urilize(rawHeadingText, (_options & AutoIdentifierOptions.AllowOnlyAscii) != 0);
|
||||
|
||||
_rendererCache.Release(stripRenderer);
|
||||
|
||||
// If the heading is empty, use the word "section" instead
|
||||
var baseHeadingId = string.IsNullOrEmpty(headingText) ? "section" : headingText;
|
||||
|
||||
// Add a trailing -1, -2, -3...etc. in case of collision
|
||||
var headingId = baseHeadingId;
|
||||
if (!identifiers.Add(headingId))
|
||||
{
|
||||
var headingBuffer = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
headingBuffer.Append(baseHeadingId);
|
||||
headingBuffer.Append('-');
|
||||
uint index = 0;
|
||||
do
|
||||
{
|
||||
instance.Reset();
|
||||
index++;
|
||||
headingBuffer.Append(index);
|
||||
headingId = headingBuffer.AsSpan().ToString();
|
||||
headingBuffer.Length = baseHeadingId.Length + 1;
|
||||
}
|
||||
while (!identifiers.Add(headingId));
|
||||
headingBuffer.Dispose();
|
||||
}
|
||||
|
||||
attributes.Id = headingId;
|
||||
}
|
||||
|
||||
private sealed class StripRendererCache : ObjectCache<HtmlRenderer>
|
||||
{
|
||||
protected override HtmlRenderer NewInstance()
|
||||
{
|
||||
var headingWriter = new FastStringWriter();
|
||||
var stripRenderer = new HtmlRenderer(headingWriter)
|
||||
{
|
||||
// Set to false both to avoid having any HTML tags in the output
|
||||
EnableHtmlForInline = false,
|
||||
EnableHtmlEscape = false
|
||||
};
|
||||
return stripRenderer;
|
||||
}
|
||||
|
||||
protected override void Reset(HtmlRenderer instance)
|
||||
{
|
||||
instance.ResetInternal();
|
||||
|
||||
((FastStringWriter)instance.Writer).Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,39 +2,36 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
namespace Markdig.Extensions.AutoIdentifiers;
|
||||
|
||||
namespace Markdig.Extensions.AutoIdentifiers
|
||||
/// <summary>
|
||||
/// Options for the <see cref="AutoIdentifierExtension"/>.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AutoIdentifierOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for the <see cref="AutoIdentifierExtension"/>.
|
||||
/// No options: does not apply any additional formatting and/or transformations.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AutoIdentifierOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// No options: does not apply any additional formatting and/or transformations.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Default (<see cref="AutoLink"/>)
|
||||
/// </summary>
|
||||
Default = AutoLink | AllowOnlyAscii,
|
||||
/// <summary>
|
||||
/// Default (<see cref="AutoLink"/>)
|
||||
/// </summary>
|
||||
Default = AutoLink | AllowOnlyAscii,
|
||||
|
||||
/// <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>
|
||||
AutoLink = 1,
|
||||
/// <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>
|
||||
AutoLink = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Allows only ASCII characters in the url (HTML 5 allows to have UTF8 characters). Default is <c>true</c>
|
||||
/// </summary>
|
||||
AllowOnlyAscii = 2,
|
||||
/// <summary>
|
||||
/// Allows only ASCII characters in the url (HTML 5 allows to have UTF8 characters). Default is <c>true</c>
|
||||
/// </summary>
|
||||
AllowOnlyAscii = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Renders auto identifiers like GitHub.
|
||||
/// </summary>
|
||||
GitHub = 4,
|
||||
}
|
||||
/// <summary>
|
||||
/// Renders auto identifiers like GitHub.
|
||||
/// </summary>
|
||||
GitHub = 4,
|
||||
}
|
||||
|
||||
@@ -4,22 +4,21 @@
|
||||
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Extensions.AutoIdentifiers
|
||||
{
|
||||
/// <summary>
|
||||
/// A link reference definition to a <see cref="HeadingBlock"/> stored at the <see cref="MarkdownDocument"/> level.
|
||||
/// </summary>
|
||||
/// <seealso cref="LinkReferenceDefinition" />
|
||||
public class HeadingLinkReferenceDefinition : LinkReferenceDefinition
|
||||
{
|
||||
public HeadingLinkReferenceDefinition(HeadingBlock headling)
|
||||
{
|
||||
Heading = headling;
|
||||
}
|
||||
namespace Markdig.Extensions.AutoIdentifiers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the heading related to this link reference definition.
|
||||
/// </summary>
|
||||
public HeadingBlock Heading { get; set; }
|
||||
/// <summary>
|
||||
/// A link reference definition to a <see cref="HeadingBlock"/> stored at the <see cref="MarkdownDocument"/> level.
|
||||
/// </summary>
|
||||
/// <seealso cref="LinkReferenceDefinition" />
|
||||
public class HeadingLinkReferenceDefinition : LinkReferenceDefinition
|
||||
{
|
||||
public HeadingLinkReferenceDefinition(HeadingBlock headling)
|
||||
{
|
||||
Heading = headling;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the heading related to this link reference definition.
|
||||
/// </summary>
|
||||
public HeadingBlock Heading { get; set; }
|
||||
}
|
||||
@@ -5,32 +5,26 @@
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.AutoLinks
|
||||
namespace Markdig.Extensions.AutoLinks;
|
||||
|
||||
/// <summary>
|
||||
/// Extension to automatically create <see cref="LinkInline"/> when a link url http: or mailto: is found.
|
||||
/// </summary>
|
||||
/// <seealso cref="IMarkdownExtension" />
|
||||
public class AutoLinkExtension(AutoLinkOptions? options) : IMarkdownExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension to automatically create <see cref="LinkInline"/> when a link url http: or mailto: is found.
|
||||
/// </summary>
|
||||
/// <seealso cref="IMarkdownExtension" />
|
||||
public class AutoLinkExtension : IMarkdownExtension
|
||||
public readonly AutoLinkOptions Options = options ?? new AutoLinkOptions();
|
||||
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
public readonly AutoLinkOptions Options;
|
||||
|
||||
public AutoLinkExtension(AutoLinkOptions? options)
|
||||
{
|
||||
Options = options ?? new AutoLinkOptions();
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
if (!pipeline.InlineParsers.Contains<AutoLinkParser>())
|
||||
{
|
||||
// Insert the parser before any other parsers
|
||||
pipeline.InlineParsers.Insert(0, new AutoLinkParser(Options));
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
if (!pipeline.InlineParsers.Contains<AutoLinkParser>())
|
||||
{
|
||||
// Insert the parser before any other parsers
|
||||
pipeline.InlineParsers.Insert(0, new AutoLinkParser(Options));
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -2,25 +2,29 @@
|
||||
// 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.AutoLinks
|
||||
namespace Markdig.Extensions.AutoLinks;
|
||||
|
||||
public class AutoLinkOptions
|
||||
{
|
||||
public class AutoLinkOptions
|
||||
public AutoLinkOptions()
|
||||
{
|
||||
public AutoLinkOptions()
|
||||
{
|
||||
ValidPreviousCharacters = "*_~(";
|
||||
}
|
||||
|
||||
public string ValidPreviousCharacters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the link open in a new window when clicked (false by default)
|
||||
/// </summary>
|
||||
public bool OpenInNewWindow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should a www link be prefixed with https:// instead of http:// (false by default)
|
||||
/// </summary>
|
||||
public bool UseHttpsForWWWLinks { get; set; }
|
||||
ValidPreviousCharacters = "*_~(";
|
||||
}
|
||||
|
||||
public string ValidPreviousCharacters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the link open in a new window when clicked (false by default)
|
||||
/// </summary>
|
||||
public bool OpenInNewWindow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should a www link be prefixed with https:// instead of http:// (false by default)
|
||||
/// </summary>
|
||||
public bool UseHttpsForWWWLinks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should auto-linking allow a domain with no period, e.g. https://localhost (false by default)
|
||||
/// </summary>
|
||||
public bool AllowDomainWithoutPeriod { get; set; }
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user