mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-04 05:44:50 +00:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ef1d735d5 | ||
|
|
47c6c49f5c | ||
|
|
a19f78342f | ||
|
|
edb4c6c3cb | ||
|
|
4b6d7c78f5 | ||
|
|
0386cfd617 | ||
|
|
5c52a7249d | ||
|
|
14fb550704 | ||
|
|
741d09a4e8 | ||
|
|
34d9fa2bcc | ||
|
|
8ee50a265d | ||
|
|
eb002db3c7 | ||
|
|
a15203a061 | ||
|
|
b0a2f19ed7 | ||
|
|
a9d78e04a1 | ||
|
|
a8b6e2c2e4 | ||
|
|
665f5f5e51 | ||
|
|
86a72c3582 | ||
|
|
784b999d6c | ||
|
|
be3a893d3d | ||
|
|
2679c84788 | ||
|
|
3d005d6444 | ||
|
|
09593ff3da | ||
|
|
f0269fc61f | ||
|
|
2d97628cfd | ||
|
|
ee97d32ef0 | ||
|
|
9ca5cdcb31 | ||
|
|
5f453fbe92 | ||
|
|
8300a9cca2 | ||
|
|
f7d763230d | ||
|
|
cce1b99edc | ||
|
|
2dca2119fa | ||
|
|
faf0a20816 | ||
|
|
68196c1ce6 | ||
|
|
fdf125d05d | ||
|
|
46133715ab | ||
|
|
4151ac2287 | ||
|
|
305f722899 | ||
|
|
abc8aa25b7 | ||
|
|
e6bb83ecdf | ||
|
|
e6ea8ad274 | ||
|
|
1f3cf5962f | ||
|
|
83b3427805 |
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -25,9 +25,6 @@ jobs:
|
||||
with:
|
||||
dotnet-version: 3.0.100
|
||||
|
||||
- name: Add coveralls.net
|
||||
run: dotnet tool install coveralls.net --version 1.0.0 --tool-path tools
|
||||
|
||||
- name: Build (Release)
|
||||
run: dotnet build src -c Release
|
||||
|
||||
@@ -40,14 +37,15 @@ jobs:
|
||||
- name: Build & Test (Debug)
|
||||
run: dotnet test src -c Debug
|
||||
|
||||
- name: Coveralls
|
||||
run: dotnet test src -c Release -f netcoreapp2.1 /p:Include=\"[${{env.PROJECT_NAME}}]*\" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=./coverage.info
|
||||
- name: Coverlet
|
||||
run: dotnet test src -c Release -f netcoreapp2.1 /p:Include=\"[${{env.PROJECT_NAME}}]*\" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
|
||||
|
||||
- name: Coveralls Upload
|
||||
uses: coverallsapp/github-action@v1.0.1
|
||||
if: github.event_name == 'push'
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path-to-lcov: src/${{env.PROJECT_NAME}}.Tests/coverage.info
|
||||
path-to-lcov: src/${{env.PROJECT_NAME}}.Tests/coverage.netcoreapp2.1.info
|
||||
|
||||
- name: Pack
|
||||
run: dotnet pack src -c Release
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 0.20.0 (18 Apr 2020)
|
||||
- Markdig is now compatible only with `NETStandard 2.0`, `NETStandard 2.1`, `NETCoreApp 2.1` and `NETCoreApp 3.1`.
|
||||
- Many performance improvements from [PR #416](https://github.com/lunet-io/markdig/pull/416)
|
||||
[PR #417](https://github.com/lunet-io/markdig/pull/417)
|
||||
[PR #418](https://github.com/lunet-io/markdig/pull/418)
|
||||
[PR #421](https://github.com/lunet-io/markdig/pull/421)
|
||||
[PR #422](https://github.com/lunet-io/markdig/pull/422)
|
||||
[PR #410](https://github.com/lunet-io/markdig/pull/410)
|
||||
|
||||
## 0.18.3 (8 Mar 2020)
|
||||
- Publish NuGet Symbol packages
|
||||
|
||||
|
||||
@@ -50,7 +50,9 @@ You can **try Markdig online** and compare it to other implementations on [babel
|
||||
- [**Diagrams**](src/Markdig.Tests/Specs/DiagramsSpecs.md) extension whenever a fenced code block contains a special keyword, it will be converted to a div block with the content as-is (currently, supports [`mermaid`](https://knsv.github.io/mermaid/) and [`nomnoml`](https://github.com/skanaar/nomnoml) diagrams)
|
||||
- [**YAML 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)
|
||||
- Compatible with .NET 3.5, 4.0+ and .NET Core (`netstandard1.1+`)
|
||||
- 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`.
|
||||
|
||||
If you are looking for support for an old .NET Framework 3.5 or 4.0, you can download Markdig `0.18.3`.
|
||||
|
||||
### Third Party Extensions
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net451;netcoreapp2.1</TargetFrameworks>
|
||||
<TargetFrameworks>netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
|
||||
<OutputType>Library</OutputType>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.msbuild" Version="2.6.2">
|
||||
<PackageReference Include="coverlet.msbuild" Version="2.8.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
// Generated: 2020-04-18 06:41:26
|
||||
|
||||
// --------------------------------
|
||||
// Grid Tables
|
||||
@@ -62,8 +62,8 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:50%">
|
||||
// <col style="width:50%">
|
||||
// <col style="width:50%" />
|
||||
// <col style="width:50%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>This is</td>
|
||||
@@ -73,7 +73,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table |", "<table>\n<col style=\"width:50%\">\n<col style=\"width:50%\">\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table |", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// The following is not a valid row separator
|
||||
@@ -112,9 +112,9 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>Col1
|
||||
@@ -135,7 +135,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---------+---------+---------+\n| Col1 | Col2 | Col3 |\n| Col1a | Col2a | Col3a |\n| Col1b | Col3b |\n| Col1c |", "<table>\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<tbody>\n<tr>\n<td>Col1\nCol1a</td>\n<td>Col2\nCol2a</td>\n<td>Col3\nCol3a</td>\n</tr>\n<tr>\n<td colspan=\"2\">Col1b</td>\n<td>Col3b</td>\n</tr>\n<tr>\n<td colspan=\"3\">Col1c</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---------+---------+---------+\n| Col1 | Col2 | Col3 |\n| Col1a | Col2a | Col3a |\n| Col1b | Col3b |\n| Col1c |", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td>Col1\nCol1a</td>\n<td>Col2\nCol2a</td>\n<td>Col3\nCol3a</td>\n</tr>\n<tr>\n<td colspan=\"2\">Col1b</td>\n<td>Col3b</td>\n</tr>\n<tr>\n<td colspan=\"3\">Col1c</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// A row header is separated using `+========+` instead of `+---------+`:
|
||||
@@ -152,8 +152,8 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:50%">
|
||||
// <col style="width:50%">
|
||||
// <col style="width:50%" />
|
||||
// <col style="width:50%" />
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>This is</th>
|
||||
@@ -163,7 +163,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table |\n+=========+=========+", "<table>\n<col style=\"width:50%\">\n<col style=\"width:50%\">\n<thead>\n<tr>\n<th>This is</th>\n<th>a table</th>\n</tr>\n</thead>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table |\n+=========+=========+", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<thead>\n<tr>\n<th>This is</th>\n<th>a table</th>\n</tr>\n</thead>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// The last column separator `|` may be omitted:
|
||||
@@ -179,8 +179,8 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:50%">
|
||||
// <col style="width:50%">
|
||||
// <col style="width:50%" />
|
||||
// <col style="width:50%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>This is</td>
|
||||
@@ -190,7 +190,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table with a longer text in the second column", "<table>\n<col style=\"width:50%\">\n<col style=\"width:50%\">\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table with a longer text in the second column</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table with a longer text in the second column", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table with a longer text in the second column</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// The respective width of the columns are calculated from the ratio between the total size of the first table row without counting the `+`: `+----+--------+----+` would be divided between:
|
||||
@@ -215,9 +215,9 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:25%">
|
||||
// <col style="width:50%">
|
||||
// <col style="width:25%">
|
||||
// <col style="width:25%" />
|
||||
// <col style="width:50%" />
|
||||
// <col style="width:25%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>A</td>
|
||||
@@ -228,7 +228,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+----+--------+----+\n| A | B C D | E |\n+----+--------+----+", "<table>\n<col style=\"width:25%\">\n<col style=\"width:50%\">\n<col style=\"width:25%\">\n<tbody>\n<tr>\n<td>A</td>\n<td>B C D</td>\n<td>E</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+----+--------+----+\n| A | B C D | E |\n+----+--------+----+", "<table>\n<col style=\"width:25%\" />\n<col style=\"width:50%\" />\n<col style=\"width:25%\" />\n<tbody>\n<tr>\n<td>A</td>\n<td>B C D</td>\n<td>E</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// Alignment might be specified on the first row using the character `:`:
|
||||
@@ -245,9 +245,9 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>A</td>
|
||||
@@ -258,7 +258,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+-----+:---:+-----+\n| A | B | C |\n+-----+-----+-----+", "<table>\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<tbody>\n<tr>\n<td>A</td>\n<td style=\"text-align: center;\">B</td>\n<td>C</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+-----+:---:+-----+\n| A | B | C |\n+-----+-----+-----+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td>A</td>\n<td style=\"text-align: center;\">B</td>\n<td>C</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// A grid table may have cells spanning both columns and rows:
|
||||
@@ -279,9 +279,9 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td colspan="2">AAAAA</td>
|
||||
@@ -302,7 +302,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+---+---+ B +\n| D | E | B |\n+ D +---+---+\n| D | CCCCC |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<tbody>\n<tr>\n<td colspan=\"2\">AAAAA</td>\n<td rowspan=\"2\">B\nB\nB</td>\n</tr>\n<tr>\n<td rowspan=\"2\">D\nD\nD</td>\n<td>E</td>\n</tr>\n<tr>\n<td colspan=\"2\">CCCCC</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+---+---+ B +\n| D | E | B |\n+ D +---+---+\n| D | CCCCC |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td colspan=\"2\">AAAAA</td>\n<td rowspan=\"2\">B\nB\nB</td>\n</tr>\n<tr>\n<td rowspan=\"2\">D\nD\nD</td>\n<td>E</td>\n</tr>\n<tr>\n<td colspan=\"2\">CCCCC</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// A grid table may have cells with both colspan and rowspan:
|
||||
@@ -323,9 +323,9 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td colspan="2" rowspan="2">AAAAA
|
||||
@@ -345,7 +345,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+ AAAAA +---+\n| AAAAA | C |\n+---+---+---+\n| D | E | F |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<tbody>\n<tr>\n<td colspan=\"2\" rowspan=\"2\">AAAAA\nAAAAA\nAAAAA</td>\n<td>B</td>\n</tr>\n<tr>\n<td>C</td>\n</tr>\n<tr>\n<td>D</td>\n<td>E</td>\n<td>F</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+ AAAAA +---+\n| AAAAA | C |\n+---+---+---+\n| D | E | F |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td colspan=\"2\" rowspan=\"2\">AAAAA\nAAAAA\nAAAAA</td>\n<td>B</td>\n</tr>\n<tr>\n<td>C</td>\n</tr>\n<tr>\n<td>D</td>\n<td>E</td>\n<td>F</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// A grid table may not have irregularly shaped cells:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using NUnit.Framework;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Markdig.Helpers;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
@@ -33,9 +33,9 @@ namespace Markdig.Tests
|
||||
|
||||
foreach (LiteralInline literalInline in syntaxTree.Descendants<LiteralInline>())
|
||||
{
|
||||
Assert.AreSame(ArrayHelper<ListBlock>.Empty, literalInline.Descendants<ListBlock>());
|
||||
Assert.AreSame(ArrayHelper<ParagraphBlock>.Empty, literalInline.Descendants<ParagraphBlock>());
|
||||
Assert.AreSame(ArrayHelper<ContainerInline>.Empty, literalInline.Descendants<ContainerInline>());
|
||||
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>())
|
||||
@@ -50,13 +50,13 @@ namespace Markdig.Tests
|
||||
|
||||
if (containerInline.FirstChild is null)
|
||||
{
|
||||
Assert.AreSame(ArrayHelper<LiteralInline>.Empty, containerInline.Descendants<LiteralInline>());
|
||||
Assert.AreSame(ArrayHelper<LiteralInline>.Empty, containerInline.FindDescendants<LiteralInline>());
|
||||
Assert.AreSame(ArrayHelper<LiteralInline>.Empty, (containerInline as MarkdownObject).Descendants<LiteralInline>());
|
||||
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>());
|
||||
}
|
||||
|
||||
Assert.AreSame(ArrayHelper<ListBlock>.Empty, containerInline.Descendants<ListBlock>());
|
||||
Assert.AreSame(ArrayHelper<ParagraphBlock>.Empty, containerInline.Descendants<ParagraphBlock>());
|
||||
Assert.AreSame(Array.Empty<ListBlock>(), containerInline.Descendants<ListBlock>());
|
||||
Assert.AreSame(Array.Empty<ParagraphBlock>(), containerInline.Descendants<ParagraphBlock>());
|
||||
}
|
||||
|
||||
foreach (ParagraphBlock paragraphBlock in syntaxTree.Descendants<ParagraphBlock>())
|
||||
@@ -65,7 +65,7 @@ namespace Markdig.Tests
|
||||
(paragraphBlock as MarkdownObject).Descendants<LiteralInline>(),
|
||||
paragraphBlock.Descendants<LiteralInline>());
|
||||
|
||||
Assert.AreSame(ArrayHelper<ParagraphBlock>.Empty, paragraphBlock.Descendants<ParagraphBlock>());
|
||||
Assert.AreSame(Array.Empty<ParagraphBlock>(), paragraphBlock.Descendants<ParagraphBlock>());
|
||||
}
|
||||
|
||||
foreach (ContainerBlock containerBlock in syntaxTree.Descendants<ContainerBlock>())
|
||||
@@ -80,9 +80,9 @@ namespace Markdig.Tests
|
||||
|
||||
if (containerBlock.Count == 0)
|
||||
{
|
||||
Assert.AreSame(ArrayHelper<LiteralInline>.Empty, containerBlock.Descendants<LiteralInline>());
|
||||
Assert.AreSame(ArrayHelper<LiteralInline>.Empty, (containerBlock as Block).Descendants<LiteralInline>());
|
||||
Assert.AreSame(ArrayHelper<LiteralInline>.Empty, (containerBlock as MarkdownObject).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>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<!--Add support for sourcelink-->
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.*" PrivateAssets="All"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Extensions.Abbreviations
|
||||
@@ -21,9 +22,9 @@ namespace Markdig.Extensions.Abbreviations
|
||||
|
||||
public static void AddAbbreviation(this MarkdownDocument document, string label, Abbreviation abbr)
|
||||
{
|
||||
if (document == null) throw new ArgumentNullException(nameof(document));
|
||||
if (label == null) throw new ArgumentNullException(nameof(label));
|
||||
if (abbr == null) throw new ArgumentNullException(nameof(abbr));
|
||||
if (document == null) ThrowHelper.ArgumentNullException(nameof(document));
|
||||
if (label == null) ThrowHelper.ArgumentNullException_label();
|
||||
if (abbr == null) ThrowHelper.ArgumentNullException(nameof(abbr));
|
||||
|
||||
var map = document.GetAbbreviations();
|
||||
if (map == null)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
@@ -115,7 +116,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
|
||||
ValidAbbreviationStart:;
|
||||
|
||||
if (prefixTree.TryMatchLongest(text, i, content.End - i + 1, out KeyValuePair<string, Abbreviation> abbreviationMatch))
|
||||
if (prefixTree.TryMatchLongest(text.AsSpan(i, content.End - i + 1), out KeyValuePair<string, Abbreviation> abbreviationMatch))
|
||||
{
|
||||
var match = abbreviationMatch.Key;
|
||||
if (!IsValidAbbreviationEnding(match, content, i))
|
||||
|
||||
@@ -1747,10 +1747,10 @@ namespace Markdig.Extensions.Emoji
|
||||
public EmojiMapping(IDictionary<string, string> shortcodeToUnicode, IDictionary<string, string> smileyToShortcode)
|
||||
{
|
||||
if (shortcodeToUnicode == null)
|
||||
throw new ArgumentNullException(nameof(shortcodeToUnicode));
|
||||
ThrowHelper.ArgumentNullException(nameof(shortcodeToUnicode));
|
||||
|
||||
if (smileyToShortcode == null)
|
||||
throw new ArgumentNullException(nameof(smileyToShortcode));
|
||||
ThrowHelper.ArgumentNullException(nameof(smileyToShortcode));
|
||||
|
||||
// Build emojis and smileys CompactPrefixTree
|
||||
|
||||
@@ -1768,7 +1768,7 @@ namespace Markdig.Extensions.Emoji
|
||||
foreach (var shortcode in shortcodeToUnicode)
|
||||
{
|
||||
if (string.IsNullOrEmpty(shortcode.Key) || string.IsNullOrEmpty(shortcode.Value))
|
||||
throw new ArgumentException("The dictionaries cannot contain null or empty keys/values", nameof(shortcodeToUnicode));
|
||||
ThrowHelper.ArgumentException("The dictionaries cannot contain null or empty keys/values", nameof(shortcodeToUnicode));
|
||||
|
||||
firstChars.Add(shortcode.Key[0]);
|
||||
PrefixTree.Add(shortcode);
|
||||
@@ -1777,15 +1777,15 @@ namespace Markdig.Extensions.Emoji
|
||||
foreach (var smiley in smileyToShortcode)
|
||||
{
|
||||
if (string.IsNullOrEmpty(smiley.Key) || string.IsNullOrEmpty(smiley.Value))
|
||||
throw new ArgumentException("The dictionaries cannot contain null or empty keys/values", nameof(smileyToShortcode));
|
||||
ThrowHelper.ArgumentException("The dictionaries cannot contain null or empty keys/values", nameof(smileyToShortcode));
|
||||
|
||||
if (!shortcodeToUnicode.TryGetValue(smiley.Value, out string unicode))
|
||||
throw new ArgumentException(string.Format("Invalid smiley target: {0} is not present in the emoji shortcodes dictionary", smiley.Value));
|
||||
ThrowHelper.ArgumentException(string.Format("Invalid smiley target: {0} is not present in the emoji shortcodes dictionary", smiley.Value));
|
||||
|
||||
firstChars.Add(smiley.Key[0]);
|
||||
|
||||
if (!PrefixTree.TryAdd(smiley.Key, unicode))
|
||||
throw new ArgumentException(string.Format("Smiley {0} is already present in the emoji mapping", smiley.Key));
|
||||
ThrowHelper.ArgumentException(string.Format("Smiley {0} is already present in the emoji mapping", smiley.Key));
|
||||
}
|
||||
|
||||
OpeningCharacters = new List<char>(firstChars).ToArray();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
@@ -34,7 +35,7 @@ namespace Markdig.Extensions.Emoji
|
||||
}
|
||||
|
||||
// Try to match an emoji shortcode or smiley
|
||||
if (!_emojiMapping.PrefixTree.TryMatchLongest(slice.Text, slice.Start, slice.Length, out KeyValuePair<string, string> match))
|
||||
if (!_emojiMapping.PrefixTree.TryMatchLongest(slice.Text.AsSpan(slice.Start, slice.Length), out KeyValuePair<string, string> match))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
@@ -45,16 +45,16 @@ namespace Markdig.Extensions.ListExtras
|
||||
if ((isRomanLow || isRomanUp) && (pendingBulletType == '\0' || pendingBulletType == 'i' || pendingBulletType == 'I'))
|
||||
{
|
||||
int startChar = state.Start;
|
||||
int endChar = 0;
|
||||
// With a roman, we can have multiple characters
|
||||
// Note that we don't validate roman numbers
|
||||
while (isRomanLow ? CharHelper.IsRomanLetterLowerPartial(c) : CharHelper.IsRomanLetterUpperPartial(c))
|
||||
do
|
||||
{
|
||||
endChar = state.Start;
|
||||
c = state.NextChar();
|
||||
}
|
||||
while (isRomanLow ? CharHelper.IsRomanLetterLowerPartial(c) : CharHelper.IsRomanLetterUpperPartial(c));
|
||||
|
||||
result.OrderedStart = CharHelper.RomanToArabic(state.Line.Text.Substring(startChar, endChar - startChar + 1)).ToString();
|
||||
int orderValue = CharHelper.RomanToArabic(state.Line.Text.AsSpan(startChar, state.Start - startChar));
|
||||
result.OrderedStart = CharHelper.SmallNumberToString(orderValue);
|
||||
result.BulletType = isRomanLow ? 'i' : 'I';
|
||||
result.DefaultOrderedStart = isRomanLow ? "i" : "I";
|
||||
}
|
||||
@@ -62,7 +62,7 @@ namespace Markdig.Extensions.ListExtras
|
||||
{
|
||||
// otherwise we expect a regular alpha lettered list with a single character.
|
||||
var isUpper = c.IsAlphaUpper();
|
||||
result.OrderedStart = (Char.ToUpperInvariant(c) - 64).ToString();
|
||||
result.OrderedStart = CharHelper.SmallNumberToString((c | 0x20) - 'a' + 1);
|
||||
result.BulletType = isUpper ? 'A' : 'a';
|
||||
result.DefaultOrderedStart = isUpper ? "A" : "a";
|
||||
state.NextChar();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -40,9 +41,10 @@ namespace Markdig.Extensions.MediaLinks
|
||||
public static IHostProvider Create(string hostPrefix, Func<Uri, string> handler, bool allowFullScreen = true, string iframeClass = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hostPrefix))
|
||||
throw new ArgumentException("hostPrefix is null or empty.", nameof(hostPrefix));
|
||||
ThrowHelper.ArgumentException("hostPrefix is null or empty.", nameof(hostPrefix));
|
||||
if (handler == null)
|
||||
throw new ArgumentNullException(nameof(handler));
|
||||
ThrowHelper.ArgumentNullException(nameof(handler));
|
||||
|
||||
return new DelegateProvider { HostPrefix = hostPrefix, Delegate = handler, AllowFullScreen = allowFullScreen, Class = iframeClass };
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// 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 Markdig.Helpers;
|
||||
using Markdig.Renderers;
|
||||
|
||||
namespace Markdig.Extensions.SelfPipeline
|
||||
@@ -29,7 +30,7 @@ namespace Markdig.Extensions.SelfPipeline
|
||||
tag = string.IsNullOrEmpty(tag) ? DefaultTag : tag;
|
||||
if (tag.IndexOfAny(new []{'<', '>'}) >= 0)
|
||||
{
|
||||
throw new ArgumentException("Tag cannot contain `<` or `>` characters", nameof(tag));
|
||||
ThrowHelper.ArgumentException("Tag cannot contain `<` or `>` characters", nameof(tag));
|
||||
}
|
||||
|
||||
if (defaultExtensions != null)
|
||||
@@ -57,7 +58,7 @@ namespace Markdig.Extensions.SelfPipeline
|
||||
// Make sure that this pipeline has only one extension (itself)
|
||||
if (pipeline.Extensions.Count > 1)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
ThrowHelper.InvalidOperationException(
|
||||
"The SelfPipeline extension cannot be configured with other extensions");
|
||||
}
|
||||
}
|
||||
@@ -74,7 +75,7 @@ namespace Markdig.Extensions.SelfPipeline
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
public MarkdownPipeline CreatePipelineFromInput(string inputText)
|
||||
{
|
||||
if (inputText == null) throw new ArgumentNullException(nameof(inputText));
|
||||
if (inputText == null) ThrowHelper.ArgumentNullException(nameof(inputText));
|
||||
|
||||
var builder = new MarkdownPipelineBuilder();
|
||||
string defaultConfig = DefaultExtensions;
|
||||
|
||||
@@ -30,8 +30,7 @@ namespace Markdig.Extensions.Tables
|
||||
/// <param name="options">The options.</param>
|
||||
public PipeTableParser(LineBreakInlineParser lineBreakParser, PipeTableOptions options = null)
|
||||
{
|
||||
if (lineBreakParser == null) throw new ArgumentNullException(nameof(lineBreakParser));
|
||||
this.lineBreakParser = lineBreakParser;
|
||||
this.lineBreakParser = lineBreakParser ?? throw new ArgumentNullException(nameof(lineBreakParser));
|
||||
OpeningCharacters = new[] { '|', '\n' };
|
||||
Options = options ?? new PipeTableOptions();
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for defining Empty arrays.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of an element of the array</typeparam>
|
||||
public static class ArrayHelper<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty array.
|
||||
/// </summary>
|
||||
public static readonly T[] Empty = new T[0];
|
||||
}
|
||||
}
|
||||
@@ -2,36 +2,6 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
|
||||
// The IsHighSurrogate, IsLowSurrogate and ConvertToUtf32 methods are copied from
|
||||
// .Net Core source code which is under MIT license. They are copied here because
|
||||
// they don't exist in `portable40-net40+sl5+win8+wp8+wpa81`, We probably should remove them
|
||||
// once we dropped support for that target platform and use the official .Net methods.
|
||||
|
||||
//The MIT License(MIT)
|
||||
|
||||
//Copyright(c) .NET Foundation and Contributors
|
||||
|
||||
//All rights reserved.
|
||||
|
||||
//Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
//of this software and associated documentation files (the "Software"), to deal
|
||||
//in the Software without restriction, including without limitation the rights
|
||||
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
//copies of the Software, and to permit persons to whom the Software is
|
||||
//furnished to do so, subject to the following conditions:
|
||||
|
||||
//The above copyright notice and this permission notice shall be included in all
|
||||
//copies or substantial portions of the Software.
|
||||
|
||||
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
//SOFTWARE.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Collections.Generic;
|
||||
@@ -47,20 +17,20 @@ namespace Markdig.Helpers
|
||||
{
|
||||
public const int TabSize = 4;
|
||||
|
||||
public const char ZeroSafeChar = '\uFFFD';
|
||||
public const char ReplacementChar = '\uFFFD';
|
||||
|
||||
public const string ZeroSafeString = "\uFFFD";
|
||||
public const string ReplacementCharString = "\uFFFD";
|
||||
|
||||
private const char HighSurrogateStart = '\ud800';
|
||||
private const char HighSurrogateEnd = '\udbff';
|
||||
private const char LowSurrogateStart = '\udc00';
|
||||
private const char LowSurrogateEnd = '\udfff';
|
||||
|
||||
// The starting codepoint for Unicode plane 1. Plane 1 contains 0x010000 ~ 0x01ffff.
|
||||
private const int UnicodePlane01Start = 0x10000;
|
||||
|
||||
// We don't support LCDM
|
||||
private static readonly Dictionary<char, int> romanMap = new Dictionary<char, int> { { 'I', 1 }, { 'V', 5 }, { 'X', 10 } };
|
||||
private static readonly Dictionary<char, int> romanMap = new Dictionary<char, int>(6) {
|
||||
{ 'i', 1 }, { 'v', 5 }, { 'x', 10 },
|
||||
{ 'I', 1 }, { 'V', 5 }, { 'X', 10 }
|
||||
};
|
||||
|
||||
private static readonly char[] punctuationExceptions = { '−', '-', '†', '‡' };
|
||||
|
||||
@@ -104,36 +74,35 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsRomanLetterPartial(char c)
|
||||
{
|
||||
// We don't support LCDM
|
||||
return IsRomanLetterLowerPartial(c) || IsRomanLetterUpperPartial(c);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsRomanLetterLowerPartial(char c)
|
||||
{
|
||||
// We don't support LCDM
|
||||
return c == 'i' || c == 'v' || c == 'x';
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsRomanLetterUpperPartial(char c)
|
||||
{
|
||||
// We don't support LCDM
|
||||
return c == 'I' || c == 'V' || c == 'X';
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
public static int RomanToArabic(string text)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int RomanToArabic(ReadOnlySpan<char> text)
|
||||
{
|
||||
int result = 0;
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
var character = char.ToUpperInvariant(text[i]);
|
||||
var candidate = romanMap[character];
|
||||
if (i + 1 < text.Length && candidate < romanMap[char.ToUpperInvariant(text[i + 1])])
|
||||
var candidate = romanMap[text[i]];
|
||||
if ((uint)(i + 1) < text.Length && candidate < romanMap[text[i + 1]])
|
||||
{
|
||||
result -= candidate;
|
||||
}
|
||||
@@ -145,7 +114,7 @@ namespace Markdig.Helpers
|
||||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int AddTab(int column)
|
||||
{
|
||||
// return ((column + TabSize) / TabSize) * TabSize;
|
||||
@@ -153,18 +122,18 @@ namespace Markdig.Helpers
|
||||
return TabSize + (column & ~(TabSize - 1));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsAcrossTab(int column)
|
||||
{
|
||||
return (column & (TabSize - 1)) != 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Contains(this char[] charList, char c)
|
||||
{
|
||||
for (int i = 0; i < charList.Length; i++)
|
||||
foreach (char ch in charList)
|
||||
{
|
||||
if (charList[i] == c)
|
||||
if (ch == c)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -172,7 +141,7 @@ namespace Markdig.Helpers
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsWhitespace(this char c)
|
||||
{
|
||||
// 2.1 Characters and lines
|
||||
@@ -180,20 +149,20 @@ namespace Markdig.Helpers
|
||||
return c <= ' ' && (c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r');
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsControl(this char c)
|
||||
{
|
||||
return c < ' ' || char.IsControl(c);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsEscapableSymbol(this char c)
|
||||
{
|
||||
// char.IsSymbol also works with Unicode symbols that cannot be escaped based on the specification.
|
||||
return (c > ' ' && c < '0') || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || (c > 'z' && c < 127) || c == '•';
|
||||
}
|
||||
|
||||
//[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsWhiteSpaceOrZero(this char c)
|
||||
{
|
||||
return IsZero(c) || IsWhitespace(c);
|
||||
@@ -253,19 +222,19 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsNewLine(this char c)
|
||||
{
|
||||
return c == '\n';
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsZero(this char c)
|
||||
{
|
||||
return c == '\0';
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsSpace(this char c)
|
||||
{
|
||||
// 2.1 Characters and lines
|
||||
@@ -273,7 +242,7 @@ namespace Markdig.Helpers
|
||||
return c == ' ';
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsTab(this char c)
|
||||
{
|
||||
// 2.1 Characters and lines
|
||||
@@ -281,13 +250,13 @@ namespace Markdig.Helpers
|
||||
return c == '\t';
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsSpaceOrTab(this char c)
|
||||
{
|
||||
return IsSpace(c) || IsTab(c);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static char EscapeInsecure(this char c)
|
||||
{
|
||||
// 2.3 Insecure characters
|
||||
@@ -295,25 +264,25 @@ namespace Markdig.Helpers
|
||||
return c == '\0' ? '\ufffd' : c;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsAlphaUpper(this char c)
|
||||
{
|
||||
return (uint)(c - 'A') <= ('Z' - 'A');
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsAlpha(this char c)
|
||||
{
|
||||
return (uint)((c - 'A') & ~0x20) <= ('Z' - 'A');
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsAlphaNumeric(this char c)
|
||||
{
|
||||
return IsAlpha(c) || IsDigit(c);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsDigit(this char c)
|
||||
{
|
||||
return (uint)(c - '0') <= ('9' - '0');
|
||||
@@ -362,40 +331,31 @@ namespace Markdig.Helpers
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsEmailUsernameSpecialChar(char c)
|
||||
{
|
||||
return ".!#$%&'*+/=?^_`{|}~-+.~".IndexOf(c) >= 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsHighSurrogate(char c)
|
||||
{
|
||||
return IsInInclusiveRange(c, HighSurrogateStart, HighSurrogateEnd);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsLowSurrogate(char c)
|
||||
{
|
||||
return IsInInclusiveRange(c, LowSurrogateStart, LowSurrogateEnd);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsInInclusiveRange(char c, char min, char max)
|
||||
=> (uint) (c - min) <= (uint) (max - min);
|
||||
=> (uint)(c - min) <= (uint)(max - min);
|
||||
|
||||
public static int ConvertToUtf32(char highSurrogate, char lowSurrogate)
|
||||
{
|
||||
if (!IsHighSurrogate(highSurrogate))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(highSurrogate), "Invalid high surrogate");
|
||||
}
|
||||
if (!IsLowSurrogate(lowSurrogate))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(lowSurrogate), "Invalid low surrogate");
|
||||
}
|
||||
return (((highSurrogate - HighSurrogateStart) * 0x400) + (lowSurrogate - LowSurrogateStart) + UnicodePlane01Start);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static bool IsInInclusiveRange(int value, uint min, uint max)
|
||||
=> ((uint)value - min) <= (max - min);
|
||||
|
||||
public static IEnumerable<int> ToUtf32(StringSlice text)
|
||||
{
|
||||
@@ -785,6 +745,24 @@ namespace Markdig.Helpers
|
||||
c == 0x002128 || c == 0x002395 ||
|
||||
c == 0x01D4A2 || c == 0x01D4BB ||
|
||||
c == 0x01D546;
|
||||
}
|
||||
|
||||
// Used by ListExtraItemParser to format numbers from 1 - 26
|
||||
private static readonly string[] smallNumberStringCache = {
|
||||
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
|
||||
"20", "21", "22", "23", "24", "25", "26",
|
||||
};
|
||||
|
||||
internal static string SmallNumberToString(int number)
|
||||
{
|
||||
string[] cache = smallNumberStringCache;
|
||||
if ((uint)number < (uint)cache.Length)
|
||||
{
|
||||
return cache[number];
|
||||
}
|
||||
|
||||
return number.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,8 @@ namespace Markdig.Helpers
|
||||
public class CharacterMap<T> where T : class
|
||||
{
|
||||
private readonly T[] asciiMap;
|
||||
private readonly Dictionary<char, T> nonAsciiMap;
|
||||
private readonly BitVector128 isOpeningCharacter;
|
||||
private readonly Dictionary<uint, T> nonAsciiMap;
|
||||
private readonly BoolVector128 isOpeningCharacter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CharacterMap{T}"/> class.
|
||||
@@ -27,56 +27,43 @@ namespace Markdig.Helpers
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
public CharacterMap(IEnumerable<KeyValuePair<char, T>> maps)
|
||||
{
|
||||
if (maps == null) throw new ArgumentNullException(nameof(maps));
|
||||
if (maps == null) ThrowHelper.ArgumentNullException(nameof(maps));
|
||||
var charSet = new HashSet<char>();
|
||||
int maxChar = 0;
|
||||
|
||||
foreach (var map in maps)
|
||||
{
|
||||
var openingChar = map.Key;
|
||||
|
||||
charSet.Add(openingChar);
|
||||
|
||||
if (openingChar < 128 && openingChar > maxChar)
|
||||
if (openingChar < 128)
|
||||
{
|
||||
maxChar = openingChar;
|
||||
maxChar = Math.Max(maxChar, openingChar);
|
||||
}
|
||||
else if (openingChar >= 128 && nonAsciiMap == null)
|
||||
else
|
||||
{
|
||||
// Initialize only if with have an actual non-ASCII opening character
|
||||
nonAsciiMap = new Dictionary<char, T>();
|
||||
nonAsciiMap ??= new Dictionary<uint, T>();
|
||||
}
|
||||
}
|
||||
|
||||
OpeningCharacters = charSet.ToArray();
|
||||
Array.Sort(OpeningCharacters);
|
||||
|
||||
asciiMap = new T[maxChar + 1];
|
||||
var isOpeningCharacter = new BitVector128();
|
||||
|
||||
foreach (var state in maps)
|
||||
{
|
||||
var openingChar = state.Key;
|
||||
T stateByChar;
|
||||
char openingChar = state.Key;
|
||||
if (openingChar < 128)
|
||||
{
|
||||
stateByChar = asciiMap[openingChar];
|
||||
|
||||
if (stateByChar == null)
|
||||
{
|
||||
asciiMap[openingChar] = state.Value;
|
||||
}
|
||||
asciiMap[openingChar] ??= state.Value;
|
||||
isOpeningCharacter.Set(openingChar);
|
||||
}
|
||||
else
|
||||
else if (!nonAsciiMap.ContainsKey(openingChar))
|
||||
{
|
||||
if (!nonAsciiMap.TryGetValue(openingChar, out stateByChar))
|
||||
{
|
||||
nonAsciiMap[openingChar] = state.Value;
|
||||
}
|
||||
nonAsciiMap[openingChar] = state.Value;
|
||||
}
|
||||
}
|
||||
|
||||
this.isOpeningCharacter = isOpeningCharacter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -89,24 +76,26 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
/// <param name="openingChar">The opening character.</param>
|
||||
/// <returns>A list of parsers valid for the specified opening character or null if no parsers registered.</returns>
|
||||
public T this[char openingChar]
|
||||
public T this[uint openingChar]
|
||||
{
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
T map = null;
|
||||
if (openingChar < asciiMap.Length)
|
||||
T[] asciiMap = this.asciiMap;
|
||||
if (openingChar < (uint)asciiMap.Length)
|
||||
{
|
||||
map = asciiMap[openingChar];
|
||||
return asciiMap[openingChar];
|
||||
}
|
||||
else if (nonAsciiMap != null)
|
||||
else
|
||||
{
|
||||
nonAsciiMap.TryGetValue(openingChar, out map);
|
||||
T map = null;
|
||||
nonAsciiMap?.TryGetValue(openingChar, out map);
|
||||
return map;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Searches for an opening character from a registered parser in the specified string.
|
||||
/// </summary>
|
||||
@@ -116,57 +105,111 @@ namespace Markdig.Helpers
|
||||
/// <returns>Index position within the string of the first opening character found in the specified text; if not found, returns -1</returns>
|
||||
public int IndexOfOpeningCharacter(string text, int start, int end)
|
||||
{
|
||||
var openingChars = isOpeningCharacter;
|
||||
|
||||
unsafe
|
||||
if (nonAsciiMap is null)
|
||||
{
|
||||
fixed (char* pText = text)
|
||||
#if NETCOREAPP3_1
|
||||
ref char textRef = ref Unsafe.AsRef(in text.GetPinnableReference());
|
||||
for (; start <= end; start++)
|
||||
{
|
||||
if (nonAsciiMap == null)
|
||||
if (IntPtr.Size == 4)
|
||||
{
|
||||
for (int i = start; i <= end; i++)
|
||||
uint c = Unsafe.Add(ref textRef, start);
|
||||
if (c < 128 && isOpeningCharacter[c])
|
||||
{
|
||||
var c = pText[i];
|
||||
if (c < 128 && openingChars[c])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong c = Unsafe.Add(ref textRef, start);
|
||||
if (c < 128 && isOpeningCharacter[c])
|
||||
{
|
||||
return start;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
unsafe
|
||||
{
|
||||
fixed (char* pText = text)
|
||||
{
|
||||
for (int i = start; i <= end; i++)
|
||||
{
|
||||
var c = pText[i];
|
||||
if (c < 128 ? openingChars[c] : nonAsciiMap.ContainsKey(c))
|
||||
char c = pText[i];
|
||||
if (c < 128 && isOpeningCharacter[c])
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IndexOfOpeningCharacterNonAscii(text, start, end);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
internal unsafe struct BitVector128
|
||||
private int IndexOfOpeningCharacterNonAscii(string text, int start, int end)
|
||||
{
|
||||
fixed uint values[4];
|
||||
#if NETCOREAPP3_1
|
||||
ref char textRef = ref Unsafe.AsRef(in text.GetPinnableReference());
|
||||
for (int i = start; i <= end; i++)
|
||||
{
|
||||
char c = Unsafe.Add(ref textRef, i);
|
||||
if (c < 128 ? isOpeningCharacter[c] : nonAsciiMap.ContainsKey(c))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
#else
|
||||
unsafe
|
||||
{
|
||||
fixed (char* pText = text)
|
||||
{
|
||||
for (int i = start; i <= end; i++)
|
||||
{
|
||||
char c = pText[i];
|
||||
if (c < 128 ? isOpeningCharacter[c] : nonAsciiMap.ContainsKey(c))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void Set(char c)
|
||||
internal unsafe struct BoolVector128
|
||||
{
|
||||
private fixed bool values[128];
|
||||
|
||||
public void Set(char c)
|
||||
{
|
||||
Debug.Assert(c < 128);
|
||||
values[c] = true;
|
||||
}
|
||||
|
||||
public readonly bool this[uint c]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
Debug.Assert(c < 128);
|
||||
values[c >> 5] |= (uint)1 << c;
|
||||
return values[c];
|
||||
}
|
||||
|
||||
public readonly bool this[char c]
|
||||
}
|
||||
public readonly bool this[ulong c]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
Debug.Assert(c < 128);
|
||||
return (values[c >> 5] & (uint)1 << c) != 0;
|
||||
}
|
||||
Debug.Assert(c < 128 && IntPtr.Size == 8);
|
||||
return values[c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,7 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The value associated with the key</typeparam>
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal sealed class CompactPrefixTree<TValue>
|
||||
//#if !LEGACY
|
||||
// : IReadOnlyDictionary<string, TValue>, IReadOnlyList<KeyValuePair<string, TValue>>
|
||||
//#endif
|
||||
internal sealed class CompactPrefixTree<TValue> : IReadOnlyDictionary<string, TValue>, IReadOnlyList<KeyValuePair<string, TValue>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Used internally to control behavior of insertion
|
||||
@@ -120,7 +117,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnsureTreeCapacity(int min)
|
||||
{
|
||||
if (_tree.Length < min)
|
||||
@@ -169,7 +166,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnsureCapacity(int min)
|
||||
{
|
||||
// Expansion logic as in System.Collections.Generic.List<T>
|
||||
@@ -227,7 +224,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnsureChildrenCapacity(int min)
|
||||
{
|
||||
if (_children.Length < min)
|
||||
@@ -254,7 +251,7 @@ namespace Markdig.Helpers
|
||||
|
||||
// Inspired by Markdig's CharacterMap
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool TryGetRoot(char rootChar, out int rootNodeIndex)
|
||||
{
|
||||
if (rootChar < 128)
|
||||
@@ -338,7 +335,7 @@ namespace Markdig.Helpers
|
||||
/// <returns>The key/value pair of the element at the specified index</returns>
|
||||
public KeyValuePair<string, TValue> this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if ((uint)index >= (uint)Count) ThrowHelper.ThrowIndexOutOfRangeException();
|
||||
@@ -355,7 +352,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
get
|
||||
{
|
||||
if (TryMatchExact(key, out KeyValuePair<string, TValue> match))
|
||||
if (TryMatchExact(key.AsSpan(), out KeyValuePair<string, TValue> match))
|
||||
return match.Value;
|
||||
throw new KeyNotFoundException(key);
|
||||
}
|
||||
@@ -367,7 +364,6 @@ namespace Markdig.Helpers
|
||||
}
|
||||
} // Get, Set
|
||||
|
||||
#if NETCORE
|
||||
/// <summary>
|
||||
/// Gets the value associated with the specified key
|
||||
/// </summary>
|
||||
@@ -382,7 +378,6 @@ namespace Markdig.Helpers
|
||||
throw new KeyNotFoundException(key.ToString());
|
||||
}
|
||||
} // Get only
|
||||
#endif
|
||||
|
||||
#endregion this[] accessors
|
||||
|
||||
@@ -713,50 +708,6 @@ namespace Markdig.Helpers
|
||||
|
||||
#region TryMatch longest
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the longest prefix of text, starting at offset, that is contained in this <see cref="CompactPrefixTree{TValue}"/>
|
||||
/// </summary>
|
||||
/// <param name="text">The text in which to search for the prefix</param>
|
||||
/// <param name="offset">Index of the character at which to start searching</param>
|
||||
/// <param name="match">The found prefix and the corresponding value</param>
|
||||
/// <returns>True if a match was found, false otherwise</returns>
|
||||
public bool TryMatchLongest(string text, int offset, out KeyValuePair<string, TValue> match)
|
||||
{
|
||||
#if NETCORE
|
||||
return TryMatchLongest(text.AsSpan(offset), out match);
|
||||
#else
|
||||
return TryMatchLongest(text, offset, text.Length - offset, out match);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the longest prefix of text, that is contained in this <see cref="CompactPrefixTree{TValue}"/>
|
||||
/// </summary>
|
||||
/// <param name="text">The text in which to search for the prefix</param>
|
||||
/// <param name="match">The found prefix and the corresponding value</param>
|
||||
/// <returns>True if a match was found, false otherwise</returns>
|
||||
public bool TryMatchLongest(string text, out KeyValuePair<string, TValue> match)
|
||||
{
|
||||
if (text == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
|
||||
#if NETCORE
|
||||
return TryMatchLongest(text.AsSpan(), out match);
|
||||
#else
|
||||
return TryMatchLongest(text, 0, text.Length, out match);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the longest prefix of text, starting at offset, that is contained in this <see cref="CompactPrefixTree{TValue}"/>
|
||||
/// </summary>
|
||||
/// <param name="text">The text in which to search for the prefix</param>
|
||||
/// <param name="offset">The offset in text at which to start looking for the prefix</param>
|
||||
/// <param name="length">The longest prefix allowed to match</param>
|
||||
/// <param name="match">The found prefix and the corresponding value</param>
|
||||
/// <returns>True if a match was found, false otherwise</returns>
|
||||
#if NETCORE
|
||||
public bool TryMatchLongest(string text, int offset, int length, out KeyValuePair<string, TValue> match)
|
||||
=> TryMatchLongest(text.AsSpan(offset, length), out match);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the longest prefix of text, that is contained in this <see cref="CompactPrefixTree{TValue}"/>
|
||||
/// </summary>
|
||||
@@ -768,20 +719,6 @@ namespace Markdig.Helpers
|
||||
match = default;
|
||||
if (text.Length == 0 || !TryGetRoot(text[0], out int nodeIndex))
|
||||
return false;
|
||||
#else
|
||||
public bool TryMatchLongest(string text, int offset, int length, out KeyValuePair<string, TValue> match)
|
||||
{
|
||||
int limit = offset + length;
|
||||
if (text == null)
|
||||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
|
||||
|
||||
if (offset < 0 || length < 0 || text.Length < limit)
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.offsetLength, ExceptionReason.InvalidOffsetLength);
|
||||
|
||||
match = default;
|
||||
if (length == 0 || !TryGetRoot(text[offset], out int nodeIndex))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
int matchIndex = -1;
|
||||
int depth = 1;
|
||||
@@ -790,11 +727,7 @@ namespace Markdig.Helpers
|
||||
if (node.ChildChar == 0) goto LeafNodeFound;
|
||||
if (node.MatchIndex != -1) matchIndex = node.MatchIndex;
|
||||
|
||||
#if NETCORE
|
||||
for (int i = 1; i < text.Length; i++)
|
||||
#else
|
||||
for (int i = offset + 1; i < limit; i++)
|
||||
#endif
|
||||
{
|
||||
char c = text[i];
|
||||
|
||||
@@ -826,7 +759,6 @@ namespace Markdig.Helpers
|
||||
|
||||
LeafNodeFound:;
|
||||
ref KeyValuePair<string, TValue> possibleMatch = ref _matches[node.MatchIndex];
|
||||
#if NETCORE
|
||||
if (possibleMatch.Key.Length <= text.Length)
|
||||
{
|
||||
// Check that the rest of the strings match
|
||||
@@ -835,18 +767,6 @@ namespace Markdig.Helpers
|
||||
matchIndex = node.MatchIndex;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (possibleMatch.Key.Length <= length)
|
||||
{
|
||||
// Check that the rest of the strings match
|
||||
for (int i = offset + depth, j = depth; j < possibleMatch.Key.Length; i++, j++)
|
||||
{
|
||||
if (text[i] != possibleMatch.Key[j])
|
||||
goto Return;
|
||||
}
|
||||
matchIndex = node.MatchIndex;
|
||||
}
|
||||
#endif
|
||||
|
||||
Return:;
|
||||
if (matchIndex != -1)
|
||||
@@ -861,50 +781,6 @@ namespace Markdig.Helpers
|
||||
|
||||
#region TryMatch exact
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find a suffix of text, starting at offset, that is contained in this <see cref="CompactPrefixTree{TValue}"/> and is exactly (text.Length - offset) characters long
|
||||
/// </summary>
|
||||
/// <param name="text">The text in which to search for the prefix</param>
|
||||
/// <param name="offset">Index of the character at which to start searching</param>
|
||||
/// <param name="match">The found prefix and the corresponding value</param>
|
||||
/// <returns>True if a match was found, false otherwise</returns>
|
||||
public bool TryMatchExact(string text, int offset, out KeyValuePair<string, TValue> match)
|
||||
{
|
||||
#if NETCORE
|
||||
return TryMatchExact(text.AsSpan(offset), out match);
|
||||
#else
|
||||
return TryMatchExact(text, offset, text.Length - offset, out match);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find a prefix of text, that is contained in this <see cref="CompactPrefixTree{TValue}"/> and is exactly text.Length characters long
|
||||
/// </summary>
|
||||
/// <param name="text">The text in which to search for the prefix</param>
|
||||
/// <param name="match">The found prefix and the corresponding value</param>
|
||||
/// <returns>True if a match was found, false otherwise</returns>
|
||||
public bool TryMatchExact(string text, out KeyValuePair<string, TValue> match)
|
||||
{
|
||||
if (text == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
|
||||
#if NETCORE
|
||||
return TryMatchExact(text.AsSpan(), out match);
|
||||
#else
|
||||
return TryMatchExact(text, 0, text.Length, out match);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find a prefix of text, starting at offset, that is contained in this <see cref="CompactPrefixTree{TValue}"/> and is exactly length characters long
|
||||
/// </summary>
|
||||
/// <param name="text">The text in which to search for the prefix</param>
|
||||
/// <param name="offset">The offset in text at which to start looking for the prefix</param>
|
||||
/// <param name="length">The longest prefix allowed to match</param>
|
||||
/// <param name="match">The found prefix and the corresponding value</param>
|
||||
/// <returns>True if a match was found, false otherwise</returns>
|
||||
#if NETCORE
|
||||
public bool TryMatchExact(string text, int offset, int length, out KeyValuePair<string, TValue> match)
|
||||
=> TryMatchExact(text.AsSpan(offset, length), out match);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find a prefix of text, that is contained in this <see cref="CompactPrefixTree{TValue}"/> and is exactly text.Length characters long
|
||||
/// </summary>
|
||||
@@ -916,39 +792,18 @@ namespace Markdig.Helpers
|
||||
match = default;
|
||||
if (text.Length == 0 || !TryGetRoot(text[0], out int nodeIndex))
|
||||
return false;
|
||||
#else
|
||||
public bool TryMatchExact(string text, int offset, int length, out KeyValuePair<string, TValue> match)
|
||||
{
|
||||
int limit = offset + length;
|
||||
if (text == null)
|
||||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
|
||||
|
||||
if (offset < 0 || length < 0 || text.Length < limit)
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.offsetLength, ExceptionReason.InvalidOffsetLength);
|
||||
|
||||
match = default;
|
||||
if (length == 0 || !TryGetRoot(text[offset], out int nodeIndex))
|
||||
return false;
|
||||
#endif
|
||||
int depth = 1;
|
||||
|
||||
ref Node node = ref _tree[nodeIndex];
|
||||
if (node.ChildChar == 0) goto LeafNodeFound;
|
||||
#if NETCORE
|
||||
if (node.MatchIndex != -1 && text.Length == 1)
|
||||
#else
|
||||
if (node.MatchIndex != -1 && length == 1)
|
||||
#endif
|
||||
{
|
||||
match = _matches[node.MatchIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
#if NETCORE
|
||||
for (int i = 1; i < text.Length; i++)
|
||||
#else
|
||||
for (int i = offset + 1; i < limit; i++)
|
||||
#endif
|
||||
{
|
||||
char c = text[i];
|
||||
|
||||
@@ -977,81 +832,20 @@ namespace Markdig.Helpers
|
||||
|
||||
if (node.MatchIndex == -1) return false;
|
||||
match = _matches[node.MatchIndex];
|
||||
#if NETCORE
|
||||
Debug.Assert(match.Key.Length == text.Length);
|
||||
#else
|
||||
Debug.Assert(match.Key.Length == length);
|
||||
#endif
|
||||
return true;
|
||||
|
||||
LeafNodeFound:;
|
||||
match = _matches[node.MatchIndex];
|
||||
#if NETCORE
|
||||
|
||||
return match.Key.Length == text.Length &&
|
||||
text.Slice(depth).Equals(match.Key.AsSpan(depth), StringComparison.Ordinal);
|
||||
#else
|
||||
if (match.Key.Length == length)
|
||||
{
|
||||
// Check that the rest of the strings match
|
||||
for (int i = offset + depth, j = depth; j < match.Key.Length; i++, j++)
|
||||
{
|
||||
if (text[i] != match.Key[j])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion TryMatch exact
|
||||
|
||||
#region TryMatch shortest
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the shortest prefix of text, starting at offset, that is contained in this <see cref="CompactPrefixTree{TValue}"/>
|
||||
/// </summary>
|
||||
/// <param name="text">The text in which to search for the prefix</param>
|
||||
/// <param name="offset">Index of the character at which to start searching</param>
|
||||
/// <param name="match">The found prefix and the corresponding value</param>
|
||||
/// <returns>True if a match was found, false otherwise</returns>
|
||||
public bool TryMatchShortest(string text, int offset, out KeyValuePair<string, TValue> match)
|
||||
{
|
||||
#if NETCORE
|
||||
return TryMatchShortest(text.AsSpan(offset), out match);
|
||||
#else
|
||||
return TryMatchShortest(text, offset, text.Length - offset, out match);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the shortest prefix of text, that is contained in this <see cref="CompactPrefixTree{TValue}"/>
|
||||
/// </summary>
|
||||
/// <param name="text">The text in which to search for the prefix</param>
|
||||
/// <param name="match">The found prefix and the corresponding value</param>
|
||||
/// <returns>True if a match was found, false otherwise</returns>
|
||||
public bool TryMatchShortest(string text, out KeyValuePair<string, TValue> match)
|
||||
{
|
||||
if (text == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
|
||||
#if NETCORE
|
||||
return TryMatchShortest(text.AsSpan(), out match);
|
||||
#else
|
||||
return TryMatchShortest(text, 0, text.Length, out match);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the shortest prefix of text, starting at offset, that is contained in this <see cref="CompactPrefixTree{TValue}"/>
|
||||
/// </summary>
|
||||
/// <param name="text">The text in which to search for the prefix</param>
|
||||
/// <param name="offset">The offset in text at which to start looking for the prefix</param>
|
||||
/// <param name="length">The longest prefix allowed to match</param>
|
||||
/// <param name="match">The found prefix and the corresponding value</param>
|
||||
/// <returns>True if a match was found, false otherwise</returns>
|
||||
#if NETCORE
|
||||
public bool TryMatchShortest(string text, int offset, int length, out KeyValuePair<string, TValue> match)
|
||||
=> TryMatchShortest(text.AsSpan(offset, length), out match);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find the shortest prefix of text, that is contained in this <see cref="CompactPrefixTree{TValue}"/>
|
||||
/// </summary>
|
||||
@@ -1063,20 +857,7 @@ namespace Markdig.Helpers
|
||||
match = default;
|
||||
if (text.Length == 0 || !TryGetRoot(text[0], out int nodeIndex))
|
||||
return false;
|
||||
#else
|
||||
public bool TryMatchShortest(string text, int offset, int length, out KeyValuePair<string, TValue> match)
|
||||
{
|
||||
int limit = offset + length;
|
||||
if (text == null)
|
||||
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
|
||||
|
||||
if (offset < 0 || length < 0 || text.Length < limit)
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.offsetLength, ExceptionReason.InvalidOffsetLength);
|
||||
|
||||
match = default;
|
||||
if (length == 0 || !TryGetRoot(text[offset], out int nodeIndex))
|
||||
return false;
|
||||
#endif
|
||||
ref Node node = ref _tree[nodeIndex];
|
||||
if (node.MatchIndex != -1)
|
||||
{
|
||||
@@ -1084,11 +865,7 @@ namespace Markdig.Helpers
|
||||
return true;
|
||||
}
|
||||
|
||||
#if NETCORE
|
||||
for (int i = 1; i < text.Length; i++)
|
||||
#else
|
||||
for (int i = offset + 1; i < limit; i++)
|
||||
#endif
|
||||
{
|
||||
char c = text[i];
|
||||
|
||||
@@ -1131,7 +908,7 @@ namespace Markdig.Helpers
|
||||
/// <param name="key">The key to locate in this <see cref="CompactPrefixTree{TValue}"/></param>
|
||||
/// <returns>True if the key is contained in this PrefixTree, false otherwise.</returns>
|
||||
public bool ContainsKey(string key)
|
||||
=> TryMatchExact(key, out _);
|
||||
=> TryMatchExact(key.AsSpan(), out _);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value associated with the specified key
|
||||
@@ -1141,7 +918,7 @@ namespace Markdig.Helpers
|
||||
/// <returns>True if the key is contained in this PrefixTree, false otherwise.</returns>
|
||||
public bool TryGetValue(string key, out TValue value)
|
||||
{
|
||||
bool ret = TryMatchExact(key, out KeyValuePair<string, TValue> match);
|
||||
bool ret = TryMatchExact(key.AsSpan(), out KeyValuePair<string, TValue> match);
|
||||
value = match.Value;
|
||||
return ret;
|
||||
}
|
||||
@@ -1175,9 +952,8 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerator<KeyValuePair<string, TValue>> GetEnumerator() => new Enumerator(_matches);
|
||||
//#if !LEGACY
|
||||
//IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_matches);
|
||||
//#endif
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_matches);
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the elements of a <see cref="CompactPrefixTree{TValue}"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
@@ -46,11 +47,10 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity without <c>&</c> and <c>;</c> symbols, for example, <c>copy</c>.</param>
|
||||
/// <returns>The unicode character set or <c>null</c> if the entity was not recognized.</returns>
|
||||
public static string DecodeEntity(string entity)
|
||||
public static string DecodeEntity(ReadOnlySpan<char> entity)
|
||||
{
|
||||
string result;
|
||||
if (EntityMap.TryGetValue(entity, out result))
|
||||
return result;
|
||||
if (EntityMap.TryMatchExact(entity, out KeyValuePair<string, string> result))
|
||||
return result.Value;
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -61,25 +61,49 @@ namespace Markdig.Helpers
|
||||
/// <returns>The unicode character set or <c>null</c> if the entity was not recognized.</returns>
|
||||
public static string DecodeEntity(int utf32)
|
||||
{
|
||||
if (utf32 < 0 || utf32 > 1114111 || (utf32 >= 55296 && utf32 <= 57343))
|
||||
return null;
|
||||
if (!CharHelper.IsInInclusiveRange(utf32, 1, 1114111) || CharHelper.IsInInclusiveRange(utf32, 55296, 57343))
|
||||
return CharHelper.ReplacementCharString;
|
||||
|
||||
if (utf32 < 65536)
|
||||
return char.ToString((char)utf32);
|
||||
|
||||
utf32 -= 65536;
|
||||
return new string(new char[]
|
||||
return new string(
|
||||
#if NETCORE
|
||||
stackalloc
|
||||
#else
|
||||
new
|
||||
#endif
|
||||
char[]
|
||||
{
|
||||
(char)(utf32 / 1024 + 55296),
|
||||
(char)(utf32 % 1024 + 56320)
|
||||
(char)((uint)utf32 / 1024 + 55296),
|
||||
(char)((uint)utf32 % 1024 + 56320)
|
||||
});
|
||||
}
|
||||
|
||||
#region [ EntityMap ]
|
||||
public static void DecodeEntity(int utf32, StringBuilder sb)
|
||||
{
|
||||
if (!CharHelper.IsInInclusiveRange(utf32, 1, 1114111) || CharHelper.IsInInclusiveRange(utf32, 55296, 57343))
|
||||
{
|
||||
sb.Append(CharHelper.ReplacementChar);
|
||||
}
|
||||
else if (utf32 < 65536)
|
||||
{
|
||||
sb.Append((char)utf32);
|
||||
}
|
||||
else
|
||||
{
|
||||
utf32 -= 65536;
|
||||
sb.Append((char)((uint)utf32 / 1024 + 55296));
|
||||
sb.Append((char)((uint)utf32 % 1024 + 56320));
|
||||
}
|
||||
}
|
||||
|
||||
#region [ EntityMap ]
|
||||
/// <summary>
|
||||
/// Source: http://www.w3.org/html/wg/drafts/html/master/syntax.html#named-character-references
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, string> EntityMap = new Dictionary<string, string>(2125, StringComparer.Ordinal)
|
||||
private static readonly CompactPrefixTree<string> EntityMap = new CompactPrefixTree<string>(2125, 3385, 3510)
|
||||
{
|
||||
{ "Aacute", "\u00C1" },
|
||||
{ "aacute", "\u00E1" },
|
||||
@@ -2207,6 +2231,6 @@ namespace Markdig.Helpers
|
||||
{ "zwj", "\u200D" },
|
||||
{ "zwnj", "\u200C" }
|
||||
};
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#if NET35
|
||||
namespace System.Diagnostics.CodeAnalysis
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)]
|
||||
internal class ExcludeFromCodeCoverageAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -51,7 +51,7 @@ namespace Markdig.Helpers
|
||||
|
||||
public static bool TryParseHtmlTag(ref StringSlice text, StringBuilder builder)
|
||||
{
|
||||
if (builder == null) throw new ArgumentNullException(nameof(builder));
|
||||
if (builder == null) ThrowHelper.ArgumentNullException(nameof(builder));
|
||||
var c = text.CurrentChar;
|
||||
if (c != '<')
|
||||
{
|
||||
@@ -450,11 +450,7 @@ namespace Markdig.Helpers
|
||||
|
||||
while ((searchPos = text.IndexOfAny(search, searchPos)) != -1)
|
||||
{
|
||||
if (sb == null)
|
||||
{
|
||||
sb = StringBuilderCache.Local();
|
||||
sb.Length = 0;
|
||||
}
|
||||
sb ??= StringBuilderCache.Local();
|
||||
c = text[searchPos];
|
||||
if (removeBackSlash && c == '\\')
|
||||
{
|
||||
@@ -472,10 +468,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
else if (c == '&')
|
||||
{
|
||||
int entityNameStart;
|
||||
int entityNameLength;
|
||||
int numericEntity;
|
||||
var match = ScanEntity(new StringSlice(text, searchPos, text.Length - 1), out numericEntity, out entityNameStart, out entityNameLength);
|
||||
var match = ScanEntity(new StringSlice(text, searchPos, text.Length - 1), out int numericEntity, out int entityNameStart, out int entityNameLength);
|
||||
if (match == 0)
|
||||
{
|
||||
searchPos++;
|
||||
@@ -486,8 +479,7 @@ namespace Markdig.Helpers
|
||||
|
||||
if (entityNameLength > 0)
|
||||
{
|
||||
var namedEntity = new StringSlice(text, entityNameStart, entityNameStart + entityNameLength - 1);
|
||||
var decoded = EntityHelper.DecodeEntity(namedEntity.ToString());
|
||||
var decoded = EntityHelper.DecodeEntity(text.AsSpan(entityNameStart, entityNameLength));
|
||||
if (decoded != null)
|
||||
{
|
||||
sb.Append(text, lastPos, searchPos - match - lastPos);
|
||||
@@ -498,36 +490,18 @@ namespace Markdig.Helpers
|
||||
else if (numericEntity >= 0)
|
||||
{
|
||||
sb.Append(text, lastPos, searchPos - match - lastPos);
|
||||
if (numericEntity == 0)
|
||||
{
|
||||
sb.Append('\0'.EscapeInsecure());
|
||||
}
|
||||
else
|
||||
{
|
||||
var decoded = EntityHelper.DecodeEntity(numericEntity);
|
||||
if (decoded != null)
|
||||
{
|
||||
sb.Append(decoded);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append('\uFFFD');
|
||||
}
|
||||
}
|
||||
|
||||
EntityHelper.DecodeEntity(numericEntity, sb);
|
||||
lastPos = searchPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sb == null)
|
||||
if (sb == null || lastPos == 0)
|
||||
return text;
|
||||
|
||||
sb.Append(text, lastPos, text.Length - lastPos);
|
||||
var result = sb.ToString();
|
||||
sb.Length = 0;
|
||||
return result;
|
||||
return sb.GetStringAndReset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -21,7 +21,10 @@ namespace Markdig.Helpers
|
||||
/// <exception cref="ArgumentOutOfRangeException">bufferSize cannot be <= 0</exception>
|
||||
public LineReader(string text)
|
||||
{
|
||||
_text = text ?? throw new ArgumentNullException(nameof(text));
|
||||
if (text is null)
|
||||
ThrowHelper.ArgumentNullException_text();
|
||||
|
||||
_text = text;
|
||||
SourcePosition = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace Markdig.Helpers
|
||||
return headingBuffer.GetStringAndReset();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsReservedPunctuation(char c)
|
||||
{
|
||||
return c == '_' || c == '-' || c == '.';
|
||||
@@ -425,7 +425,6 @@ namespace Markdig.Helpers
|
||||
{
|
||||
bool isValid = false;
|
||||
var buffer = StringBuilderCache.Local();
|
||||
buffer.Length = 0;
|
||||
|
||||
// a sequence of zero or more characters between straight double-quote characters ("), including a " character only if it is backslash-escaped, or
|
||||
// a sequence of zero or more characters between straight single-quote characters ('), including a ' character only if it is backslash-escaped, or
|
||||
@@ -517,7 +516,6 @@ namespace Markdig.Helpers
|
||||
{
|
||||
bool isValid = false;
|
||||
var buffer = StringBuilderCache.Local();
|
||||
buffer.Length = 0;
|
||||
|
||||
var c = text.CurrentChar;
|
||||
|
||||
@@ -652,14 +650,14 @@ namespace Markdig.Helpers
|
||||
return isValid;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsTrailingUrlStopCharacter(char c)
|
||||
{
|
||||
// Trailing punctuation (specifically, ?, !, ., ,, :, *, _, and ~) will not be considered part of the autolink, though they may be included in the interior of the link:
|
||||
return c == '?' || c == '!' || c == '.' || c == ',' || c == ':' || c == '*' || c == '*' || c == '_' || c == '~';
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool IsEndOfUri(char c, bool isAutoLink)
|
||||
{
|
||||
return c == '\0' || c.IsSpaceOrTab() || c.IsControl() || (isAutoLink && c == '<'); // TODO: specs unclear. space is strict or relaxed? (includes tabs?)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal helper to allow to declare a method using AggressiveInlining without being .NET 4.0+
|
||||
/// </summary>
|
||||
internal static class MethodImplOptionPortable
|
||||
{
|
||||
public const MethodImplOptions AggressiveInlining = (MethodImplOptions)256;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
@@ -57,7 +57,7 @@ namespace Markdig.Helpers
|
||||
/// <exception cref="System.ArgumentNullException">if instance is null</exception>
|
||||
public void Release(T instance)
|
||||
{
|
||||
if (instance == null) throw new ArgumentNullException(nameof(instance));
|
||||
if (instance == null) ThrowHelper.ArgumentNullException(nameof(instance));
|
||||
Reset(instance);
|
||||
lock (builders)
|
||||
{
|
||||
|
||||
@@ -22,85 +22,85 @@ namespace Markdig.Helpers
|
||||
{
|
||||
}
|
||||
|
||||
public bool InsertBefore<TElement>(T element) where TElement : T
|
||||
public bool InsertBefore<TItem>(T item) where TItem : T
|
||||
{
|
||||
if (element == null) throw new ArgumentNullException(nameof(element));
|
||||
if (item == null) ThrowHelper.ArgumentNullException_item();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (this[i] is TElement)
|
||||
if (this[i] is TItem)
|
||||
{
|
||||
Insert(i, element);
|
||||
Insert(i, item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public TElement Find<TElement>() where TElement : T
|
||||
public TItem Find<TItem>() where TItem : T
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (this[i] is TElement)
|
||||
if (this[i] is TItem)
|
||||
{
|
||||
return (TElement)this[i];
|
||||
return (TItem)this[i];
|
||||
}
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
public bool TryFind<TElement>(out TElement element) where TElement : T
|
||||
public bool TryFind<TItem>(out TItem item) where TItem : T
|
||||
{
|
||||
element = Find<TElement>();
|
||||
return element != null;
|
||||
item = Find<TItem>();
|
||||
return item != null;
|
||||
}
|
||||
|
||||
public TElement FindExact<TElement>() where TElement : T
|
||||
public TItem FindExact<TItem>() where TItem : T
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (this[i].GetType() == typeof(TElement))
|
||||
if (this[i].GetType() == typeof(TItem))
|
||||
{
|
||||
return (TElement)this[i];
|
||||
return (TItem)this[i];
|
||||
}
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
public void AddIfNotAlready<TElement>() where TElement : class, T, new()
|
||||
public void AddIfNotAlready<TItem>() where TItem : class, T, new()
|
||||
{
|
||||
if (!Contains<TElement>())
|
||||
if (!Contains<TItem>())
|
||||
{
|
||||
Add(new TElement());
|
||||
Add(new TItem());
|
||||
}
|
||||
}
|
||||
|
||||
public void AddIfNotAlready<TElement>(TElement telement) where TElement : T
|
||||
public void AddIfNotAlready<TItem>(TItem item) where TItem : T
|
||||
{
|
||||
if (!Contains<TElement>())
|
||||
if (!Contains<TItem>())
|
||||
{
|
||||
Add(telement);
|
||||
Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public bool InsertAfter<TElement>(T element) where TElement : T
|
||||
public bool InsertAfter<TItem>(T item) where TItem : T
|
||||
{
|
||||
if (element == null) throw new ArgumentNullException(nameof(element));
|
||||
if (item == null) ThrowHelper.ArgumentNullException_item();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (this[i] is TElement)
|
||||
if (this[i] is TItem)
|
||||
{
|
||||
Insert(i + 1, element);
|
||||
Insert(i + 1, item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool Contains<TElement>() where TElement : T
|
||||
public bool Contains<TItem>() where TItem : T
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (this[i] is TElement)
|
||||
if (this[i] is TItem)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -109,16 +109,16 @@ namespace Markdig.Helpers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces <typeparamref name="TElement"/> with <paramref name="replacement"/>.
|
||||
/// Replaces <typeparamref name="TItem"/> with <paramref name="replacement"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TElement">Element type to find in the list</typeparam>
|
||||
/// <param name="replacement">Object to replace this element with</param>
|
||||
/// <typeparam name="TItem">Item type to find in the list</typeparam>
|
||||
/// <param name="replacement">Object to replace this item with</param>
|
||||
/// <returns><c>true</c> if a replacement was made; otherwise <c>false</c>.</returns>
|
||||
public bool Replace<TElement>(T replacement) where TElement : T
|
||||
public bool Replace<TItem>(T replacement) where TItem : T
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
if (this[i] is TElement)
|
||||
if (this[i] is TItem)
|
||||
{
|
||||
RemoveAt(i);
|
||||
Insert(i, replacement);
|
||||
@@ -129,28 +129,28 @@ namespace Markdig.Helpers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces <typeparamref name="TElement"/> with <paramref name="newElement"/> or adds <paramref name="newElement"/>.
|
||||
/// Replaces <typeparamref name="TItem"/> with <paramref name="newItem"/> or adds <paramref name="newItem"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TElement">Element type to find in the list</typeparam>
|
||||
/// <param name="newElement">Object to add/replace the found element with</param>
|
||||
/// <typeparam name="TItem">Item type to find in the list</typeparam>
|
||||
/// <param name="newItem">Object to add/replace the found item with</param>
|
||||
/// <returns><c>true</c> if a replacement was made; otherwise <c>false</c>.</returns>
|
||||
public bool ReplaceOrAdd<TElement>(T newElement) where TElement : T
|
||||
public bool ReplaceOrAdd<TItem>(T newItem) where TItem : T
|
||||
{
|
||||
if (Replace<TElement>(newElement))
|
||||
if (Replace<TItem>(newItem))
|
||||
return true;
|
||||
|
||||
Add(newElement);
|
||||
Add(newItem);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the first occurrence of <typeparamref name="TElement"/>
|
||||
/// Removes the first occurrence of <typeparamref name="TItem"/>
|
||||
/// </summary>
|
||||
public bool TryRemove<TElement>() where TElement : T
|
||||
public bool TryRemove<TItem>() where TItem : T
|
||||
{
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (this[i] is TElement)
|
||||
if (this[i] is TItem)
|
||||
{
|
||||
RemoveAt(i);
|
||||
return true;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
@@ -6,11 +6,7 @@ using System.Text;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// An implementation of <see cref="ObjectCache{T}"/> for <see cref="StringBuilder"/>
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Helpers.ObjectCache{StringBuilder}" />
|
||||
public class StringBuilderCache : DefaultObjectCache<StringBuilder>
|
||||
public static class StringBuilderCache
|
||||
{
|
||||
/// <summary>
|
||||
/// A StringBuilder that can be used locally in a method body only.
|
||||
@@ -31,13 +27,5 @@ namespace Markdig.Helpers
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
protected override void Reset(StringBuilder instance)
|
||||
{
|
||||
if (instance.Length > 0)
|
||||
{
|
||||
instance.Length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace Markdig.Helpers
|
||||
/// <param name="capacity"></param>
|
||||
public StringLineGroup(int capacity, bool willRelease = false)
|
||||
{
|
||||
if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
if (capacity <= 0) ThrowHelper.ArgumentOutOfRangeException(nameof(capacity));
|
||||
Lines = _pool.Rent(willRelease ? Math.Max(8, capacity) : capacity);
|
||||
Count = 0;
|
||||
}
|
||||
@@ -38,7 +38,7 @@ namespace Markdig.Helpers
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
public StringLineGroup(string text)
|
||||
{
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
if (text == null) ThrowHelper.ArgumentNullException_text();
|
||||
Lines = new StringLine[1];
|
||||
Count = 0;
|
||||
Add(new StringSlice(text));
|
||||
@@ -82,7 +82,7 @@ namespace Markdig.Helpers
|
||||
/// Adds the specified line to this instance.
|
||||
/// </summary>
|
||||
/// <param name="line">The line.</param>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(ref StringLine line)
|
||||
{
|
||||
if (Count == Lines.Length) IncreaseCapacity();
|
||||
@@ -93,7 +93,7 @@ namespace Markdig.Helpers
|
||||
/// Adds the specified slice to this instance.
|
||||
/// </summary>
|
||||
/// <param name="slice">The slice.</param>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(StringSlice slice)
|
||||
{
|
||||
if (Count == Lines.Length) IncreaseCapacity();
|
||||
@@ -283,7 +283,7 @@ namespace Markdig.Helpers
|
||||
|
||||
public readonly char PeekChar(int offset = 1)
|
||||
{
|
||||
if (offset < 0) throw new ArgumentOutOfRangeException("Negative offset are not supported for StringLineGroup", nameof(offset));
|
||||
if (offset < 0) ThrowHelper.ArgumentOutOfRangeException("Negative offset are not supported for StringLineGroup", nameof(offset));
|
||||
|
||||
if (Start + offset > End)
|
||||
{
|
||||
|
||||
@@ -38,7 +38,10 @@ namespace Markdig.Helpers
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
public StringSlice(string text, int start, int end)
|
||||
{
|
||||
Text = text ?? throw new ArgumentNullException(nameof(text));
|
||||
if (text is null)
|
||||
ThrowHelper.ArgumentNullException_text();
|
||||
|
||||
Text = text;
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
@@ -51,12 +54,12 @@ namespace Markdig.Helpers
|
||||
/// <summary>
|
||||
/// Gets or sets the start position within <see cref="Text"/>.
|
||||
/// </summary>
|
||||
public int Start { get; set; }
|
||||
public int Start { readonly get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the end position (inclusive) within <see cref="Text"/>.
|
||||
/// </summary>
|
||||
public int End { get; set; }
|
||||
public int End { readonly get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length.
|
||||
@@ -80,7 +83,7 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
public readonly bool IsEmpty
|
||||
{
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => Start > End;
|
||||
}
|
||||
|
||||
@@ -91,7 +94,7 @@ namespace Markdig.Helpers
|
||||
/// <returns>A character in the slice at the specified index (not from <see cref="Start"/> but from the begining of the slice)</returns>
|
||||
public readonly char this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => Text[index];
|
||||
}
|
||||
|
||||
@@ -102,7 +105,7 @@ namespace Markdig.Helpers
|
||||
/// <returns>
|
||||
/// The next character. `\0` is end of the iteration.
|
||||
/// </returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public char NextChar()
|
||||
{
|
||||
int start = Start;
|
||||
@@ -121,7 +124,7 @@ namespace Markdig.Helpers
|
||||
/// inside the range <see cref="Start"/> and <see cref="End"/>, returns `\0` if outside this range.
|
||||
/// </summary>
|
||||
/// <returns>The character at offset, returns `\0` if none.</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly char PeekChar()
|
||||
{
|
||||
int index = Start + 1;
|
||||
@@ -134,7 +137,7 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns>The character at offset, returns `\0` if none.</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly char PeekChar(int offset)
|
||||
{
|
||||
var index = Start + offset;
|
||||
@@ -145,7 +148,7 @@ namespace Markdig.Helpers
|
||||
/// Peeks a character at the specified offset from the current beginning of the string, without taking into account <see cref="Start"/> and <see cref="End"/>
|
||||
/// </summary>
|
||||
/// <returns>The character at offset, returns `\0` if none.</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly char PeekCharAbsolute(int index)
|
||||
{
|
||||
string text = Text;
|
||||
@@ -158,7 +161,7 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <returns>The character at offset, returns `\0` if none.</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly char PeekCharExtra(int offset)
|
||||
{
|
||||
var index = Start + offset;
|
||||
@@ -275,13 +278,7 @@ namespace Markdig.Helpers
|
||||
if (length <= 0)
|
||||
return -1;
|
||||
|
||||
#if NETCORE
|
||||
var span = Text.AsSpan(offset, length);
|
||||
int index = ignoreCase ? span.IndexOf(text, StringComparison.OrdinalIgnoreCase) : span.IndexOf(text);
|
||||
return index == -1 ? index : index + offset;
|
||||
#else
|
||||
return Text.IndexOf(text, offset, length, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -296,12 +293,7 @@ namespace Markdig.Helpers
|
||||
if (length <= 0)
|
||||
return -1;
|
||||
|
||||
#if NETCORE
|
||||
int index = Text.AsSpan(start, length).IndexOf(c);
|
||||
return index == -1 ? index : index + start;
|
||||
#else
|
||||
return Text.IndexOf(c, start, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -312,16 +304,15 @@ namespace Markdig.Helpers
|
||||
/// </returns>
|
||||
public bool TrimStart()
|
||||
{
|
||||
// Strip leading spaces
|
||||
for (; Start <= End; Start++)
|
||||
{
|
||||
if (Start < Text.Length
|
||||
&& !Text[Start].IsWhitespace())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return IsEmpty;
|
||||
string text = Text;
|
||||
int end = End;
|
||||
int i = Start;
|
||||
|
||||
while (i <= end && (uint)i < (uint)text.Length && text[i].IsWhitespace())
|
||||
i++;
|
||||
|
||||
Start = i;
|
||||
return i > end;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -330,16 +321,15 @@ namespace Markdig.Helpers
|
||||
/// <param name="spaceCount">The number of spaces trimmed.</param>
|
||||
public void TrimStart(out int spaceCount)
|
||||
{
|
||||
spaceCount = 0;
|
||||
// Strip leading spaces
|
||||
for (; Start <= End; Start++)
|
||||
{
|
||||
if (!Text[Start].IsWhitespace())
|
||||
{
|
||||
break;
|
||||
}
|
||||
spaceCount++;
|
||||
}
|
||||
string text = Text;
|
||||
int end = End;
|
||||
int i = Start;
|
||||
|
||||
while (i <= end && (uint)i < (uint)text.Length && text[i].IsWhitespace())
|
||||
i++;
|
||||
|
||||
spaceCount = i - Start;
|
||||
Start = i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -348,15 +338,15 @@ namespace Markdig.Helpers
|
||||
/// <returns></returns>
|
||||
public bool TrimEnd()
|
||||
{
|
||||
for (; Start <= End; End--)
|
||||
{
|
||||
if (End < Text.Length
|
||||
&& !Text[End].IsWhitespace())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return IsEmpty;
|
||||
string text = Text;
|
||||
int start = Start;
|
||||
int i = End;
|
||||
|
||||
while (start <= i && (uint)i < (uint)text.Length && text[i].IsWhitespace())
|
||||
i--;
|
||||
|
||||
End = i;
|
||||
return start > i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -364,8 +354,18 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
public void Trim()
|
||||
{
|
||||
TrimStart();
|
||||
TrimEnd();
|
||||
string text = Text;
|
||||
int start = Start;
|
||||
int end = End;
|
||||
|
||||
while (start <= end && (uint)start < (uint)text.Length && text[start].IsWhitespace())
|
||||
start++;
|
||||
|
||||
while (start <= end && (uint)end < (uint)text.Length && text[end].IsWhitespace())
|
||||
end--;
|
||||
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -393,9 +393,12 @@ namespace Markdig.Helpers
|
||||
/// <returns><c>true</c> if this slice is empty or made only of whitespaces; <c>false</c> otherwise</returns>
|
||||
public readonly bool IsEmptyOrWhitespace()
|
||||
{
|
||||
for (int i = Start; i <= End; i++)
|
||||
string text = Text;
|
||||
int end = End;
|
||||
|
||||
for (int i = Start; i <= end && (uint)i < (uint)text.Length; i++)
|
||||
{
|
||||
if (!Text[i].IsWhitespace())
|
||||
if (!text[i].IsWhitespace())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -10,19 +10,39 @@ namespace Markdig.Helpers
|
||||
[ExcludeFromCodeCoverage]
|
||||
internal static class ThrowHelper
|
||||
{
|
||||
public static void ArgumentNullException(string paramName) => throw new ArgumentNullException(paramName);
|
||||
public static void ArgumentNullException_item() => throw new ArgumentNullException("item");
|
||||
public static void ArgumentNullException_text() => throw new ArgumentNullException("text");
|
||||
public static void ArgumentNullException_label() => throw new ArgumentNullException("label");
|
||||
public static void ArgumentNullException_key() => throw new ArgumentNullException("key");
|
||||
public static void ArgumentNullException_name() => throw new ArgumentNullException("name");
|
||||
public static void ArgumentNullException_markdown() => throw new ArgumentNullException("markdown");
|
||||
public static void ArgumentNullException_writer() => throw new ArgumentNullException("writer");
|
||||
public static void ArgumentNullException_leafBlock() => throw new ArgumentNullException("leafBlock");
|
||||
public static void ArgumentNullException_markdownObject() => throw new ArgumentNullException("markdownObject");
|
||||
|
||||
public static void ArgumentException(string message) => throw new ArgumentException(message);
|
||||
public static void ArgumentException(string message, string paramName) => throw new ArgumentException(message, paramName);
|
||||
|
||||
public static void ArgumentOutOfRangeException(string paramName) => throw new ArgumentOutOfRangeException(paramName);
|
||||
public static void ArgumentOutOfRangeException(string message, string paramName) => throw new ArgumentOutOfRangeException(message, paramName);
|
||||
public static void ArgumentOutOfRangeException_index() => throw new ArgumentOutOfRangeException("index");
|
||||
|
||||
public static void InvalidOperationException(string message) => throw new InvalidOperationException(message);
|
||||
|
||||
public static void ThrowArgumentNullException(ExceptionArgument argument)
|
||||
{
|
||||
throw new ArgumentNullException(GetArgumentName(argument));
|
||||
throw new ArgumentNullException(argument.ToString());
|
||||
}
|
||||
|
||||
public static void ThrowArgumentException(ExceptionArgument argument, ExceptionReason reason)
|
||||
{
|
||||
throw new ArgumentException(GetArgumentName(argument), GetExceptionReason(reason));
|
||||
throw new ArgumentException(argument.ToString(), GetExceptionReason(reason));
|
||||
}
|
||||
|
||||
public static void ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionReason reason)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(GetArgumentName(argument), GetExceptionReason(reason));
|
||||
throw new ArgumentOutOfRangeException(argument.ToString(), GetExceptionReason(reason));
|
||||
}
|
||||
|
||||
public static void ThrowIndexOutOfRangeException()
|
||||
@@ -30,25 +50,6 @@ namespace Markdig.Helpers
|
||||
throw new IndexOutOfRangeException();
|
||||
}
|
||||
|
||||
private static string GetArgumentName(ExceptionArgument argument)
|
||||
{
|
||||
switch (argument)
|
||||
{
|
||||
case ExceptionArgument.key:
|
||||
case ExceptionArgument.input:
|
||||
case ExceptionArgument.value:
|
||||
case ExceptionArgument.length:
|
||||
case ExceptionArgument.text:
|
||||
return argument.ToString();
|
||||
|
||||
case ExceptionArgument.offsetLength:
|
||||
return "offset and length";
|
||||
|
||||
default:
|
||||
Debug.Assert(false, "The enum value is not defined, please check the ExceptionArgument Enum.");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
private static string GetExceptionReason(ExceptionReason reason)
|
||||
{
|
||||
switch (reason)
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<Description>A fast, powerful, CommonMark compliant, extensible Markdown processor for .NET with 20+ builtin extensions (pipetables, footnotes, definition lists... etc.)</Description>
|
||||
<Copyright>Alexandre Mutel</Copyright>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<VersionPrefix>0.18.3</VersionPrefix>
|
||||
<VersionPrefix>0.20.0</VersionPrefix>
|
||||
<Authors>Alexandre Mutel</Authors>
|
||||
<TargetFrameworks>net35;net40;netstandard2.0;netcoreapp2.1</TargetFrameworks>
|
||||
<TargetFrameworks>netstandard2.0;netstandard2.1;netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
|
||||
<PackageTags>Markdown CommonMark md html md2html</PackageTags>
|
||||
<PackageReleaseNotes>https://github.com/lunet-io/markdig/blob/master/changelog.md</PackageReleaseNotes>
|
||||
<PackageLicenseExpression>BSD-2-Clause</PackageLicenseExpression>
|
||||
@@ -14,19 +14,35 @@
|
||||
<PackageProjectUrl>https://github.com/lunet-io/markdig</PackageProjectUrl>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<LangVersion>8.0</LangVersion>
|
||||
<!--Add support for sourcelink-->
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
|
||||
<DefineConstants>$(DefineConstants);NETCORE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.1' ">
|
||||
<DefineConstants>$(DefineConstants);NETCORE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net35' OR '$(TargetFramework)' == 'net40'">
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" />
|
||||
<PackageReference Include="jnm2.ReferenceAssemblies.net35" Version="1.0.1" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
|
||||
<DefineConstants>$(DefineConstants);NETCORE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.*" PrivateAssets="All"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Markdig.Extensions.SelfPipeline;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Normalize;
|
||||
@@ -68,8 +69,8 @@ namespace Markdig
|
||||
/// <exception cref="System.ArgumentNullException">if markdown variable is null</exception>
|
||||
public static string ToHtml(string markdown, MarkdownPipeline pipeline = null)
|
||||
{
|
||||
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
|
||||
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
|
||||
if (markdown == null) ThrowHelper.ArgumentNullException_markdown();
|
||||
pipeline ??= new MarkdownPipelineBuilder().Build();
|
||||
pipeline = CheckForSelfPipeline(pipeline, markdown);
|
||||
|
||||
var renderer = pipeline.GetCacheableHtmlRenderer();
|
||||
@@ -94,9 +95,9 @@ namespace Markdig
|
||||
/// <exception cref="System.ArgumentNullException">if reader or writer variable are null</exception>
|
||||
public static MarkdownDocument ToHtml(string markdown, TextWriter writer, MarkdownPipeline pipeline = null, MarkdownParserContext context = null)
|
||||
{
|
||||
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
|
||||
if (writer == null) throw new ArgumentNullException(nameof(writer));
|
||||
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
|
||||
if (markdown == null) ThrowHelper.ArgumentNullException_markdown();
|
||||
if (writer == null) ThrowHelper.ArgumentNullException_writer();
|
||||
pipeline ??= new MarkdownPipelineBuilder().Build();
|
||||
pipeline = CheckForSelfPipeline(pipeline, markdown);
|
||||
|
||||
// We override the renderer with our own writer
|
||||
@@ -120,9 +121,9 @@ namespace Markdig
|
||||
/// <exception cref="System.ArgumentNullException">if markdown or writer variable are null</exception>
|
||||
public static object Convert(string markdown, IMarkdownRenderer renderer, MarkdownPipeline pipeline = null, MarkdownParserContext context = null)
|
||||
{
|
||||
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
|
||||
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
|
||||
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
|
||||
if (markdown == null) ThrowHelper.ArgumentNullException_markdown();
|
||||
if (renderer == null) ThrowHelper.ArgumentNullException(nameof(renderer));
|
||||
pipeline ??= new MarkdownPipelineBuilder().Build();
|
||||
|
||||
pipeline = CheckForSelfPipeline(pipeline, markdown);
|
||||
var document = Parse(markdown, pipeline, context);
|
||||
@@ -138,7 +139,7 @@ namespace Markdig
|
||||
/// <exception cref="System.ArgumentNullException">if markdown variable is null</exception>
|
||||
public static MarkdownDocument Parse(string markdown)
|
||||
{
|
||||
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
|
||||
if (markdown == null) ThrowHelper.ArgumentNullException_markdown();
|
||||
return Parse(markdown, null);
|
||||
}
|
||||
|
||||
@@ -152,8 +153,8 @@ namespace Markdig
|
||||
/// <exception cref="System.ArgumentNullException">if markdown variable is null</exception>
|
||||
public static MarkdownDocument Parse(string markdown, MarkdownPipeline pipeline, MarkdownParserContext context = null)
|
||||
{
|
||||
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
|
||||
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
|
||||
if (markdown == null) ThrowHelper.ArgumentNullException_markdown();
|
||||
pipeline ??= new MarkdownPipelineBuilder().Build();
|
||||
|
||||
pipeline = CheckForSelfPipeline(pipeline, markdown);
|
||||
return MarkdownParser.Parse(markdown, pipeline, context);
|
||||
@@ -180,9 +181,9 @@ namespace Markdig
|
||||
/// <exception cref="System.ArgumentNullException">if reader or writer variable are null</exception>
|
||||
public static MarkdownDocument ToPlainText(string markdown, TextWriter writer, MarkdownPipeline pipeline = null, MarkdownParserContext context = null)
|
||||
{
|
||||
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
|
||||
if (writer == null) throw new ArgumentNullException(nameof(writer));
|
||||
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
|
||||
if (markdown == null) ThrowHelper.ArgumentNullException_markdown();
|
||||
if (writer == null) ThrowHelper.ArgumentNullException_writer();
|
||||
pipeline ??= new MarkdownPipelineBuilder().Build();
|
||||
pipeline = CheckForSelfPipeline(pipeline, markdown);
|
||||
|
||||
// We override the renderer with our own writer
|
||||
@@ -211,7 +212,7 @@ namespace Markdig
|
||||
/// <exception cref="System.ArgumentNullException">if markdown variable is null</exception>
|
||||
public static string ToPlainText(string markdown, MarkdownPipeline pipeline = null, MarkdownParserContext context = null)
|
||||
{
|
||||
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
|
||||
if (markdown == null) ThrowHelper.ArgumentNullException_markdown();
|
||||
var writer = new StringWriter();
|
||||
ToPlainText(markdown, writer, pipeline, context);
|
||||
return writer.ToString();
|
||||
|
||||
@@ -34,7 +34,8 @@ using Markdig.Extensions.Yaml;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Parsers.Inlines;
|
||||
using Markdig.Extensions.Globalization;
|
||||
|
||||
using Markdig.Helpers;
|
||||
|
||||
namespace Markdig
|
||||
{
|
||||
/// <summary>
|
||||
@@ -138,7 +139,7 @@ namespace Markdig
|
||||
{
|
||||
if (pipeline.Extensions.Count != 0)
|
||||
{
|
||||
throw new InvalidOperationException("The SelfPipeline extension cannot be used with other extensions");
|
||||
ThrowHelper.InvalidOperationException("The SelfPipeline extension cannot be used with other extensions");
|
||||
}
|
||||
|
||||
pipeline.Extensions.Add(new SelfPipelineExtension(defaultTag, defaultExtensions));
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Markdig
|
||||
/// <summary>
|
||||
/// Provides a context that can be used as part of parsing Markdown documents.
|
||||
/// </summary>
|
||||
public sealed class MarkdownParserContext
|
||||
public class MarkdownParserContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the context property collection.
|
||||
|
||||
@@ -20,15 +20,14 @@ namespace Markdig
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MarkdownPipeline" /> class.
|
||||
/// </summary>
|
||||
internal MarkdownPipeline(OrderedList<IMarkdownExtension> extensions, BlockParserList blockParsers, InlineParserList inlineParsers, StringBuilderCache cache, TextWriter debugLog, ProcessDocumentDelegate documentProcessed)
|
||||
internal MarkdownPipeline(OrderedList<IMarkdownExtension> extensions, BlockParserList blockParsers, InlineParserList inlineParsers, TextWriter debugLog, ProcessDocumentDelegate documentProcessed)
|
||||
{
|
||||
if (blockParsers == null) throw new ArgumentNullException(nameof(blockParsers));
|
||||
if (inlineParsers == null) throw new ArgumentNullException(nameof(inlineParsers));
|
||||
if (blockParsers == null) ThrowHelper.ArgumentNullException(nameof(blockParsers));
|
||||
if (inlineParsers == null) ThrowHelper.ArgumentNullException(nameof(inlineParsers));
|
||||
// Add all default parsers
|
||||
Extensions = extensions;
|
||||
BlockParsers = blockParsers;
|
||||
InlineParsers = inlineParsers;
|
||||
StringBuilderCache = cache;
|
||||
DebugLog = debugLog;
|
||||
DocumentProcessed = documentProcessed;
|
||||
}
|
||||
@@ -44,8 +43,6 @@ namespace Markdig
|
||||
|
||||
internal InlineParserList InlineParsers { get; }
|
||||
|
||||
internal StringBuilderCache StringBuilderCache { get; }
|
||||
|
||||
// TODO: Move the log to a better place
|
||||
internal TextWriter DebugLog { get; }
|
||||
|
||||
@@ -57,7 +54,7 @@ namespace Markdig
|
||||
/// <param name="renderer">The markdown renderer to setup</param>
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
{
|
||||
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
|
||||
if (renderer == null) ThrowHelper.ArgumentNullException(nameof(renderer));
|
||||
foreach (var extension in Extensions)
|
||||
{
|
||||
extension.Setup(this, renderer);
|
||||
|
||||
@@ -49,8 +49,6 @@ namespace Markdig
|
||||
};
|
||||
|
||||
Extensions = new OrderedList<IMarkdownExtension>();
|
||||
|
||||
StringBuilderCache = new StringBuilderCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -68,11 +66,6 @@ namespace Markdig
|
||||
/// </summary>
|
||||
public OrderedList<IMarkdownExtension> Extensions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the string builder cache used by the parsers.
|
||||
/// </summary>
|
||||
public StringBuilderCache StringBuilderCache { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to enable precise source location (slower parsing but accurate position for block and inline elements)
|
||||
/// </summary>
|
||||
@@ -111,7 +104,7 @@ namespace Markdig
|
||||
{
|
||||
if (extension == null)
|
||||
{
|
||||
throw new InvalidOperationException("An extension cannot be null");
|
||||
ThrowHelper.InvalidOperationException("An extension cannot be null");
|
||||
}
|
||||
extension.Setup(this);
|
||||
}
|
||||
@@ -120,7 +113,6 @@ namespace Markdig
|
||||
new OrderedList<IMarkdownExtension>(Extensions),
|
||||
new BlockParserList(BlockParsers),
|
||||
new InlineParserList(InlineParsers),
|
||||
StringBuilderCache,
|
||||
DebugLog,
|
||||
GetDocumentProcessed)
|
||||
{
|
||||
|
||||
@@ -25,9 +25,9 @@ namespace Markdig.Parsers
|
||||
{
|
||||
if (OpeningCharacters != null)
|
||||
{
|
||||
for (int i = 0; i < OpeningCharacters.Length; i++)
|
||||
foreach (char openingChar in OpeningCharacters)
|
||||
{
|
||||
if (OpeningCharacters[i] == c)
|
||||
if (openingChar == c)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace Markdig.Parsers
|
||||
// These properties are not changing between a parent and a children BlockProcessor
|
||||
this.root = root;
|
||||
this.parserStateCache = root.parserStateCache;
|
||||
StringBuilders = root.StringBuilders;
|
||||
Document = root.Document;
|
||||
Parsers = root.Parsers;
|
||||
|
||||
@@ -41,13 +40,11 @@ namespace Markdig.Parsers
|
||||
/// <param name="parsers">The list of parsers.</param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// </exception>
|
||||
public BlockProcessor(StringBuilderCache stringBuilders, MarkdownDocument document, BlockParserList parsers, MarkdownParserContext context)
|
||||
public BlockProcessor(MarkdownDocument document, BlockParserList parsers, MarkdownParserContext context)
|
||||
{
|
||||
if (stringBuilders == null) throw new ArgumentNullException(nameof(stringBuilders));
|
||||
if (document == null) throw new ArgumentNullException(nameof(document));
|
||||
if (parsers == null) throw new ArgumentNullException(nameof(parsers));
|
||||
if (document == null) ThrowHelper.ArgumentNullException(nameof(document));
|
||||
if (parsers == null) ThrowHelper.ArgumentNullException(nameof(parsers));
|
||||
parserStateCache = new BlockParserStateCache(this);
|
||||
StringBuilders = stringBuilders;
|
||||
Document = document;
|
||||
document.IsOpen = true;
|
||||
Parsers = parsers;
|
||||
@@ -153,11 +150,6 @@ namespace Markdig.Parsers
|
||||
/// </summary>
|
||||
public int StartBeforeIndent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cache of string builders.
|
||||
/// </summary>
|
||||
public StringBuilderCache StringBuilders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current stack of <see cref="Block"/> being processed.
|
||||
/// </summary>
|
||||
@@ -400,8 +392,8 @@ namespace Markdig.Parsers
|
||||
/// <exception cref="System.ArgumentException">The block must be opened</exception>
|
||||
public void Open(Block block)
|
||||
{
|
||||
if (block == null) throw new ArgumentNullException(nameof(block));
|
||||
if (!block.IsOpen) throw new ArgumentException("The block must be opened", nameof(block));
|
||||
if (block == null) ThrowHelper.ArgumentNullException(nameof(block));
|
||||
if (!block.IsOpen) ThrowHelper.ArgumentException("The block must be opened", nameof(block));
|
||||
OpenedBlocks.Add(block);
|
||||
}
|
||||
|
||||
@@ -477,7 +469,7 @@ namespace Markdig.Parsers
|
||||
{
|
||||
if (this == root)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot release the root parser state");
|
||||
ThrowHelper.InvalidOperationException("Cannot release the root parser state");
|
||||
}
|
||||
parserStateCache.Release(this);
|
||||
}
|
||||
@@ -633,7 +625,7 @@ namespace Markdig.Parsers
|
||||
// If a parser is adding a block, it must be the last of the list
|
||||
if ((i + 1) < OpenedBlocks.Count && NewBlocks.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException("A pending parser cannot add a new block when it is not the last pending block");
|
||||
ThrowHelper.InvalidOperationException("A pending parser cannot add a new block when it is not the last pending block");
|
||||
}
|
||||
|
||||
// If we have a leaf block
|
||||
@@ -680,7 +672,7 @@ namespace Markdig.Parsers
|
||||
// Security check so that the parser can't go into a crazy infinite loop if one extension is messing
|
||||
if (previousStart == Start)
|
||||
{
|
||||
throw new InvalidOperationException($"The parser is in an invalid infinite loop while trying to parse blocks at line [{LineIndex}] with line [{Line}]");
|
||||
ThrowHelper.InvalidOperationException($"The parser is in an invalid infinite loop while trying to parse blocks at line [{LineIndex}] with line [{Line}]");
|
||||
}
|
||||
previousStart = Start;
|
||||
|
||||
@@ -810,7 +802,7 @@ namespace Markdig.Parsers
|
||||
|
||||
if (block.Parser == null)
|
||||
{
|
||||
throw new InvalidOperationException($"The new block [{block.GetType()}] must have a valid Parser property");
|
||||
ThrowHelper.InvalidOperationException($"The new block [{block.GetType()}] must have a valid Parser property");
|
||||
}
|
||||
|
||||
block.Line = LineIndex;
|
||||
@@ -826,7 +818,7 @@ namespace Markdig.Parsers
|
||||
|
||||
if (newBlocks.Count > 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
ThrowHelper.InvalidOperationException(
|
||||
"The NewBlocks is not empty. This is happening if a LeafBlock is not the last to be pushed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -16,7 +16,7 @@ namespace Markdig.Parsers
|
||||
/// </summary>
|
||||
/// <param name="blockState">State of the block.</param>
|
||||
/// <returns><c>true</c> if the block state is in discard state</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsDiscard(this BlockState blockState)
|
||||
{
|
||||
return blockState == BlockState.ContinueDiscard || blockState == BlockState.BreakDiscard;
|
||||
@@ -27,7 +27,7 @@ namespace Markdig.Parsers
|
||||
/// </summary>
|
||||
/// <param name="blockState">State of the block.</param>
|
||||
/// <returns><c>true</c> if the block state is in continue state</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsContinue(this BlockState blockState)
|
||||
{
|
||||
return blockState == BlockState.Continue || blockState == BlockState.ContinueDiscard;
|
||||
@@ -38,7 +38,7 @@ namespace Markdig.Parsers
|
||||
/// </summary>
|
||||
/// <param name="blockState">State of the block.</param>
|
||||
/// <returns><c>true</c> if the block state is in break state</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsBreak(this BlockState blockState)
|
||||
{
|
||||
return blockState == BlockState.Break || blockState == BlockState.BreakDiscard;
|
||||
|
||||
@@ -36,12 +36,10 @@ namespace Markdig.Parsers
|
||||
/// <param name="inlineCreated">The inline created event.</param>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// </exception>
|
||||
public InlineProcessor(StringBuilderCache stringBuilders, MarkdownDocument document, InlineParserList parsers, bool preciseSourcelocation, MarkdownParserContext context)
|
||||
public InlineProcessor(MarkdownDocument document, InlineParserList parsers, bool preciseSourcelocation, MarkdownParserContext context)
|
||||
{
|
||||
if (stringBuilders == null) throw new ArgumentNullException(nameof(stringBuilders));
|
||||
if (document == null) throw new ArgumentNullException(nameof(document));
|
||||
if (parsers == null) throw new ArgumentNullException(nameof(parsers));
|
||||
StringBuilders = stringBuilders;
|
||||
if (document == null) ThrowHelper.ArgumentNullException(nameof(document));
|
||||
if (parsers == null) ThrowHelper.ArgumentNullException(nameof(parsers));
|
||||
Document = document;
|
||||
Parsers = parsers;
|
||||
Context = context;
|
||||
@@ -91,11 +89,6 @@ namespace Markdig.Parsers
|
||||
/// </summary>
|
||||
public MarkdownDocument Document { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cache string builders.
|
||||
/// </summary>
|
||||
public StringBuilderCache StringBuilders { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the line from the begining of the document being processed.
|
||||
/// </summary>
|
||||
@@ -176,7 +169,7 @@ namespace Markdig.Parsers
|
||||
/// <param name="leafBlock">The leaf block.</param>
|
||||
public void ProcessInlineLeaf(LeafBlock leafBlock)
|
||||
{
|
||||
if (leafBlock == null) throw new ArgumentNullException(nameof(leafBlock));
|
||||
if (leafBlock == null) ThrowHelper.ArgumentNullException_leafBlock();
|
||||
|
||||
// clear parser states
|
||||
Array.Clear(ParserStates, 0, ParserStates.Length);
|
||||
@@ -201,7 +194,7 @@ namespace Markdig.Parsers
|
||||
// Security check so that the parser can't go into a crazy infinite loop if one extension is messing
|
||||
if (previousStart == text.Start)
|
||||
{
|
||||
throw new InvalidOperationException($"The parser is in an invalid infinite loop while trying to parse inlines for block [{leafBlock.GetType().Name}] at position ({leafBlock.ToPositionText()}");
|
||||
ThrowHelper.InvalidOperationException($"The parser is in an invalid infinite loop while trying to parse inlines for block [{leafBlock.GetType().Name}] at position ({leafBlock.ToPositionText()}");
|
||||
}
|
||||
previousStart = text.Start;
|
||||
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
// 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;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Parsers.Inlines
|
||||
{
|
||||
/// <summary>
|
||||
/// An inline parser for a <see cref="CodeInline"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Parsers.InlineParser" />
|
||||
public class CodeInlineParser : InlineParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CodeInlineParser"/> class.
|
||||
/// </summary>
|
||||
public CodeInlineParser()
|
||||
{
|
||||
OpeningCharacters = new[] { '`' };
|
||||
}
|
||||
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
var match = slice.CurrentChar;
|
||||
if (slice.PeekCharExtra(-1) == match)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var startPosition = slice.Start;
|
||||
|
||||
int openSticks = 0;
|
||||
int closeSticks = 0;
|
||||
|
||||
// Match the opened sticks
|
||||
char c = slice.CurrentChar;
|
||||
while (c == match)
|
||||
{
|
||||
openSticks++;
|
||||
c = slice.NextChar();
|
||||
}
|
||||
|
||||
var builder = processor.StringBuilders.Get();
|
||||
// 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;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Parsers.Inlines
|
||||
{
|
||||
/// <summary>
|
||||
/// An inline parser for a <see cref="CodeInline"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Parsers.InlineParser" />
|
||||
public class CodeInlineParser : InlineParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CodeInlineParser"/> class.
|
||||
/// </summary>
|
||||
public CodeInlineParser()
|
||||
{
|
||||
OpeningCharacters = new[] { '`' };
|
||||
}
|
||||
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
var match = slice.CurrentChar;
|
||||
if (slice.PeekCharExtra(-1) == match)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var startPosition = slice.Start;
|
||||
|
||||
int openSticks = 0;
|
||||
int closeSticks = 0;
|
||||
|
||||
// Match the opened sticks
|
||||
char c = slice.CurrentChar;
|
||||
while (c == match)
|
||||
{
|
||||
openSticks++;
|
||||
c = slice.NextChar();
|
||||
}
|
||||
|
||||
var builder = StringBuilderCache.Local();
|
||||
|
||||
// A backtick string is a string of one or more backtick characters (`) that is neither preceded nor followed by a backtick.
|
||||
// A code span begins with a backtick string and ends with a backtick string of equal length.
|
||||
@@ -53,71 +53,74 @@ namespace Markdig.Parsers.Inlines
|
||||
// 2. If the resulting string both begins AND ends with a space character, but does not consist entirely
|
||||
// of space characters, a single space character is removed from the front and back.
|
||||
// This allows you to include code that begins or ends with backtick characters, which must be separated by
|
||||
// whitespace from the opening or closing backtick strings.
|
||||
|
||||
bool allSpace = true;
|
||||
|
||||
while (c != '\0')
|
||||
{
|
||||
// Transform '\n' into a single space
|
||||
if (c == '\n')
|
||||
{
|
||||
c = ' ';
|
||||
}
|
||||
|
||||
// whitespace from the opening or closing backtick strings.
|
||||
|
||||
bool allSpace = true;
|
||||
|
||||
while (c != '\0')
|
||||
{
|
||||
// Transform '\n' into a single space
|
||||
if (c == '\n')
|
||||
{
|
||||
c = ' ';
|
||||
}
|
||||
|
||||
if (c == match)
|
||||
{
|
||||
do
|
||||
{
|
||||
closeSticks++;
|
||||
c = slice.NextChar();
|
||||
do
|
||||
{
|
||||
closeSticks++;
|
||||
c = slice.NextChar();
|
||||
}
|
||||
while (c == match);
|
||||
|
||||
if (openSticks == closeSticks)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
allSpace = false;
|
||||
builder.Append(match, closeSticks);
|
||||
if (openSticks == closeSticks)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
allSpace = false;
|
||||
builder.Append(match, closeSticks);
|
||||
closeSticks = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(c);
|
||||
if (c != ' ')
|
||||
{
|
||||
allSpace = false;
|
||||
}
|
||||
}
|
||||
c = slice.NextChar();
|
||||
}
|
||||
}
|
||||
|
||||
bool isMatching = false;
|
||||
if (closeSticks == openSticks)
|
||||
{
|
||||
// Remove one space from front and back if the string is not all spaces
|
||||
if (!allSpace && builder.Length > 2 && builder[0] == ' ' && builder[builder.Length - 1] == ' ')
|
||||
{
|
||||
builder.Length--;
|
||||
builder.Remove(0, 1); // More expensive, alternative is to have a double-pass algorithm
|
||||
}
|
||||
|
||||
processor.Inline = new CodeInline()
|
||||
{
|
||||
Delimiter = match,
|
||||
Content = builder.ToString(),
|
||||
Span = new SourceSpan(processor.GetSourcePosition(startPosition, out int line, out int column), processor.GetSourcePosition(slice.Start - 1)),
|
||||
Line = line,
|
||||
Column = column
|
||||
};
|
||||
isMatching = true;
|
||||
}
|
||||
|
||||
// Release the builder if not used
|
||||
processor.StringBuilders.Release(builder);
|
||||
return isMatching;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isMatching = false;
|
||||
if (closeSticks == openSticks)
|
||||
{
|
||||
string content;
|
||||
|
||||
// Remove one space from front and back if the string is not all spaces
|
||||
if (!allSpace && builder.Length > 2 && builder[0] == ' ' && builder[builder.Length - 1] == ' ')
|
||||
{
|
||||
content = builder.ToString(1, builder.Length - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
content = builder.ToString();
|
||||
}
|
||||
|
||||
processor.Inline = new CodeInline()
|
||||
{
|
||||
Delimiter = match,
|
||||
Content = content,
|
||||
Span = new SourceSpan(processor.GetSourcePosition(startPosition, out int line, out int column), processor.GetSourcePosition(slice.Start - 1)),
|
||||
Line = line,
|
||||
Column = column
|
||||
};
|
||||
isMatching = true;
|
||||
}
|
||||
|
||||
return isMatching;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using System;
|
||||
|
||||
namespace Markdig.Parsers.Inlines
|
||||
@@ -20,9 +21,9 @@ namespace Markdig.Parsers.Inlines
|
||||
/// <param name="enableWithinWord">if set to <c>true</c> the emphasis can be used inside a word.</param>
|
||||
public EmphasisDescriptor(char character, int minimumCount, int maximumCount, bool enableWithinWord)
|
||||
{
|
||||
if (minimumCount < 1) throw new ArgumentOutOfRangeException(nameof(minimumCount), "minimumCount must be >= 1");
|
||||
if (maximumCount < 1) throw new ArgumentOutOfRangeException(nameof(maximumCount), "maximumCount must be >= 1");
|
||||
if (minimumCount > maximumCount) throw new ArgumentOutOfRangeException(nameof(minimumCount), "minimumCount must be <= maximumCount");
|
||||
if (minimumCount < 1) ThrowHelper.ArgumentOutOfRangeException(nameof(minimumCount), "minimumCount must be >= 1");
|
||||
if (maximumCount < 1) ThrowHelper.ArgumentOutOfRangeException(nameof(maximumCount), "maximumCount must be >= 1");
|
||||
if (minimumCount > maximumCount) ThrowHelper.ArgumentOutOfRangeException(nameof(minimumCount), "minimumCount must be <= maximumCount");
|
||||
|
||||
Character = character;
|
||||
MinimumCount = minimumCount;
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Markdig.Parsers.Inlines
|
||||
var emphasis = EmphasisDescriptors[i];
|
||||
if (Array.IndexOf(OpeningCharacters, emphasis.Character) >= 0)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
ThrowHelper.InvalidOperationException(
|
||||
$"The character `{emphasis.Character}` is already used by another emphasis descriptor");
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System.Text;
|
||||
using System;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
@@ -27,10 +27,7 @@ namespace Markdig.Parsers.Inlines
|
||||
public static bool TryParse(ref StringSlice slice, out string literal, out int match)
|
||||
{
|
||||
literal = null;
|
||||
int entityNameStart;
|
||||
int entityNameLength;
|
||||
int entityValue;
|
||||
match = HtmlHelper.ScanEntity(slice, out entityValue, out entityNameStart, out entityNameLength);
|
||||
match = HtmlHelper.ScanEntity(slice, out int entityValue, out int entityNameStart, out int entityNameLength);
|
||||
if (match == 0)
|
||||
{
|
||||
return false;
|
||||
@@ -38,11 +35,11 @@ namespace Markdig.Parsers.Inlines
|
||||
|
||||
if (entityNameLength > 0)
|
||||
{
|
||||
literal = EntityHelper.DecodeEntity(new StringSlice(slice.Text, entityNameStart, entityNameStart + entityNameLength - 1).ToString());
|
||||
literal = EntityHelper.DecodeEntity(slice.Text.AsSpan(entityNameStart, entityNameLength));
|
||||
}
|
||||
else if (entityValue >= 0)
|
||||
{
|
||||
literal = (entityValue == 0 ? null : EntityHelper.DecodeEntity(entityValue)) ?? CharHelper.ZeroSafeString;
|
||||
literal = EntityHelper.DecodeEntity(entityValue);
|
||||
}
|
||||
return literal != null;
|
||||
}
|
||||
|
||||
@@ -300,19 +300,19 @@ namespace Markdig.Parsers.Inlines
|
||||
|
||||
private void MarkParentAsInactive(Inline inline)
|
||||
{
|
||||
if (inline == null)
|
||||
while (inline != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var parent in inline.FindParentOfType<LinkDelimiterInline>())
|
||||
{
|
||||
if (parent.IsImage)
|
||||
if (inline is LinkDelimiterInline linkInline)
|
||||
{
|
||||
break;
|
||||
if (linkInline.IsImage)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
linkInline.IsActive = false;
|
||||
}
|
||||
|
||||
parent.IsActive = false;
|
||||
inline = inline.Parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,14 +41,14 @@ namespace Markdig.Parsers
|
||||
{
|
||||
if (itemParser.OpeningCharacters == null)
|
||||
{
|
||||
throw new InvalidOperationException($"The list item parser of type [{itemParser.GetType()}] cannot have OpeningCharacters to null. It must define a list of valid opening characters");
|
||||
ThrowHelper.InvalidOperationException($"The list item parser of type [{itemParser.GetType()}] cannot have OpeningCharacters to null. It must define a list of valid opening characters");
|
||||
}
|
||||
|
||||
foreach (var openingCharacter in itemParser.OpeningCharacters)
|
||||
{
|
||||
if (tempMap.ContainsKey(openingCharacter))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
ThrowHelper.InvalidOperationException(
|
||||
$"A list item parser with the same opening character `{openingCharacter}` is already registered");
|
||||
}
|
||||
tempMap.Add(openingCharacter, itemParser);
|
||||
@@ -101,9 +101,8 @@ namespace Markdig.Parsers
|
||||
// TODO: Check with specs, it is not clear that list marker or bullet marker must be followed by at least 1 space
|
||||
|
||||
// If we have already a ListItemBlock, we are going to try to append to it
|
||||
var listItem = block as ListItemBlock;
|
||||
result = BlockState.None;
|
||||
if (listItem != null)
|
||||
if (block is ListItemBlock listItem)
|
||||
{
|
||||
result = TryContinueListItem(processor, listItem);
|
||||
}
|
||||
@@ -123,12 +122,9 @@ namespace Markdig.Parsers
|
||||
// Allow all blanks lines if the last block is a fenced code block
|
||||
// Allow 1 blank line inside a list
|
||||
// If > 1 blank line, terminate this list
|
||||
var isBlankLine = state.IsBlankLine;
|
||||
|
||||
var isCurrentBlockBreakable = state.CurrentBlock != null && state.CurrentBlock.IsBreakable;
|
||||
if (isBlankLine)
|
||||
if (state.IsBlankLine)
|
||||
{
|
||||
if (isCurrentBlockBreakable)
|
||||
if (state.CurrentBlock != null && state.CurrentBlock.IsBreakable)
|
||||
{
|
||||
if (!(state.NextContinue is ListBlock))
|
||||
{
|
||||
@@ -195,21 +191,21 @@ namespace Markdig.Parsers
|
||||
|
||||
var c = state.CurrentChar;
|
||||
var itemParser = mapItemParsers[c];
|
||||
bool isOrdered = itemParser is OrderedListItemParser;
|
||||
if (itemParser == null)
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
// Try to parse the list item
|
||||
ListInfo listInfo;
|
||||
if (!itemParser.TryParse(state, currentParent?.BulletType ?? '\0', out listInfo))
|
||||
if (!itemParser.TryParse(state, currentParent?.BulletType ?? '\0', out ListInfo listInfo))
|
||||
{
|
||||
// Reset to an a start position
|
||||
state.GoToColumn(initColumn);
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
bool isOrdered = itemParser is OrderedListItemParser;
|
||||
|
||||
// Gets the current character after a successful parsing of the list information
|
||||
c = state.CurrentChar;
|
||||
|
||||
@@ -251,11 +247,10 @@ namespace Markdig.Parsers
|
||||
// Starts/continue the list unless:
|
||||
// - an empty list item follows a paragraph
|
||||
// - an ordered list is not starting by '1'
|
||||
var isPreviousParagraph = (block ?? state.LastBlock) is ParagraphBlock;
|
||||
if (isPreviousParagraph)
|
||||
if ((block ?? state.LastBlock) is ParagraphBlock previousParagraph)
|
||||
{
|
||||
var isOpen = state.IsOpen(block ?? state.LastBlock);
|
||||
if (state.IsBlankLine || (isOpen && listInfo.BulletType == '1' && listInfo.OrderedStart != "1"))
|
||||
if (state.IsBlankLine ||
|
||||
state.IsOpen(previousParagraph) && listInfo.BulletType == '1' && listInfo.OrderedStart != "1")
|
||||
{
|
||||
state.GoToColumn(initColumn);
|
||||
return BlockState.None;
|
||||
@@ -310,64 +305,47 @@ namespace Markdig.Parsers
|
||||
|
||||
public override bool Close(BlockProcessor processor, Block blockToClose)
|
||||
{
|
||||
var listBlock = blockToClose as ListBlock;
|
||||
|
||||
// Process only if we have blank lines
|
||||
if (listBlock == null || listBlock.CountAllBlankLines <= 0)
|
||||
if (blockToClose is ListBlock listBlock && listBlock.CountAllBlankLines > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: This code is UGLY and WAY TOO LONG, simplify!
|
||||
bool isLastListItem = true;
|
||||
for (int listIndex = listBlock.Count - 1; listIndex >= 0; listIndex--)
|
||||
{
|
||||
var block = listBlock[listIndex];
|
||||
var listItem = (ListItemBlock) block;
|
||||
bool isLastElement = true;
|
||||
for (int i = listItem.Count - 1; i >= 0; i--)
|
||||
if (listBlock.Parent is ListItemBlock parentListItemBlock &&
|
||||
listBlock.LastChild is ListItemBlock lastListItem &&
|
||||
lastListItem.LastChild is BlankLineBlock)
|
||||
{
|
||||
var item = listItem[i];
|
||||
if (item is BlankLineBlock)
|
||||
// Inform the outer list that we have a blank line
|
||||
var parentList = (ListBlock)parentListItemBlock.Parent;
|
||||
|
||||
parentList.CountAllBlankLines++;
|
||||
parentListItemBlock.Add(new BlankLineBlock());
|
||||
}
|
||||
|
||||
for (int listIndex = listBlock.Count - 1; listIndex >= 0; listIndex--)
|
||||
{
|
||||
var listItem = (ListItemBlock)listBlock[listIndex];
|
||||
|
||||
for (int i = listItem.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if ((isLastElement && listIndex < listBlock.Count - 1) || (listItem.Count > 2 && (i > 0 && i < (listItem.Count - 1))))
|
||||
if (listItem[i] is BlankLineBlock)
|
||||
{
|
||||
listBlock.IsLoose = true;
|
||||
}
|
||||
|
||||
if (isLastElement && isLastListItem)
|
||||
{
|
||||
// Inform the outer list that we have a blank line
|
||||
var parentListItemBlock = listBlock.Parent as ListItemBlock;
|
||||
if (parentListItemBlock != null)
|
||||
if (i == listItem.Count - 1 ? listIndex < listBlock.Count - 1 : i > 0)
|
||||
{
|
||||
var parentList = (ListBlock) parentListItemBlock.Parent;
|
||||
listBlock.IsLoose = true;
|
||||
}
|
||||
|
||||
parentList.CountAllBlankLines++;
|
||||
parentListItemBlock.Add(new BlankLineBlock());
|
||||
listItem.RemoveAt(i);
|
||||
|
||||
// If we have removed all blank lines, we can exit
|
||||
listBlock.CountAllBlankLines--;
|
||||
if (listBlock.CountAllBlankLines == 0)
|
||||
{
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
listItem.RemoveAt(i);
|
||||
|
||||
// If we have remove all blank lines, we can exit
|
||||
listBlock.CountAllBlankLines--;
|
||||
if (listBlock.CountAllBlankLines == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
isLastElement = false;
|
||||
}
|
||||
isLastListItem = false;
|
||||
}
|
||||
|
||||
//// Update end-position for the list
|
||||
//if (listBlock.Count > 0)
|
||||
//{
|
||||
// listBlock.Span.End = listBlock[listBlock.Count - 1].Span.End;
|
||||
//}
|
||||
|
||||
done:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ namespace Markdig.Parsers
|
||||
/// </exception>
|
||||
private MarkdownParser(string text, MarkdownPipeline pipeline, MarkdownParserContext context)
|
||||
{
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
if (pipeline == null) throw new ArgumentNullException(nameof(pipeline));
|
||||
if (text == null) ThrowHelper.ArgumentNullException_text();
|
||||
if (pipeline == null) ThrowHelper.ArgumentNullException(nameof(pipeline));
|
||||
|
||||
roughLineCountEstimate = text.Length / 40;
|
||||
text = FixupZero(text);
|
||||
@@ -51,15 +51,13 @@ namespace Markdig.Parsers
|
||||
preciseSourceLocation = pipeline.PreciseSourceLocation;
|
||||
|
||||
// Initialize the pipeline
|
||||
var stringBuilderCache = pipeline.StringBuilderCache ?? new StringBuilderCache();
|
||||
|
||||
document = new MarkdownDocument();
|
||||
|
||||
// Initialize the block parsers
|
||||
blockProcessor = new BlockProcessor(stringBuilderCache, document, pipeline.BlockParsers, context);
|
||||
blockProcessor = new BlockProcessor(document, pipeline.BlockParsers, context);
|
||||
|
||||
// Initialize the inline parsers
|
||||
inlineProcessor = new InlineProcessor(stringBuilderCache, document, pipeline.InlineParsers, pipeline.PreciseSourceLocation, context)
|
||||
inlineProcessor = new InlineProcessor(document, pipeline.InlineParsers, pipeline.PreciseSourceLocation, context)
|
||||
{
|
||||
DebugLog = pipeline.DebugLog
|
||||
};
|
||||
@@ -77,8 +75,8 @@ namespace Markdig.Parsers
|
||||
/// <exception cref="System.ArgumentNullException">if reader variable is null</exception>
|
||||
public static MarkdownDocument Parse(string text, MarkdownPipeline pipeline = null, MarkdownParserContext context = null)
|
||||
{
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
|
||||
if (text == null) ThrowHelper.ArgumentNullException_text();
|
||||
pipeline ??= new MarkdownPipelineBuilder().Build();
|
||||
|
||||
// Perform the parsing
|
||||
var markdownParser = new MarkdownParser(text, pipeline, context);
|
||||
@@ -131,7 +129,7 @@ namespace Markdig.Parsers
|
||||
/// <param name="text">The text to secure.</param>
|
||||
private string FixupZero(string text)
|
||||
{
|
||||
return text.Replace('\0', CharHelper.ZeroSafeChar);
|
||||
return text.Replace('\0', CharHelper.ReplacementChar);
|
||||
}
|
||||
|
||||
private sealed class ContainerItemCache : DefaultObjectCache<ContainerItem>
|
||||
|
||||
@@ -33,9 +33,9 @@ namespace Markdig.Parsers
|
||||
{
|
||||
// Check if we have an ordered delimiter
|
||||
orderedDelimiter = state.CurrentChar;
|
||||
for (int i = 0; i < OrderedDelimiters.Length; i++)
|
||||
foreach (char delimiter in OrderedDelimiters)
|
||||
{
|
||||
if (OrderedDelimiters[i] == orderedDelimiter)
|
||||
if (delimiter == orderedDelimiter)
|
||||
{
|
||||
state.NextChar();
|
||||
return true;
|
||||
|
||||
@@ -50,23 +50,24 @@ namespace Markdig.Parsers
|
||||
{
|
||||
if (block is ParagraphBlock paragraph)
|
||||
{
|
||||
TryMatchLinkReferenceDefinition(ref paragraph.Lines, processor);
|
||||
ref var lines = ref paragraph.Lines;
|
||||
|
||||
TryMatchLinkReferenceDefinition(ref lines, processor);
|
||||
|
||||
int lineCount = lines.Count;
|
||||
|
||||
// If Paragraph is empty, we can discard it
|
||||
if (paragraph.Lines.Count == 0)
|
||||
if (lineCount == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var lineCount = paragraph.Lines.Count;
|
||||
for (int i = 0; i < lineCount; i++)
|
||||
{
|
||||
paragraph.Lines.Lines[i].Slice.TrimStart();
|
||||
}
|
||||
if (lineCount > 0)
|
||||
{
|
||||
paragraph.Lines.Lines[lineCount - 1].Slice.TrimEnd();
|
||||
lines.Lines[i].Slice.TrimStart();
|
||||
}
|
||||
|
||||
lines.Lines[lineCount - 1].Slice.TrimEnd();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -74,48 +75,14 @@ namespace Markdig.Parsers
|
||||
|
||||
private BlockState TryParseSetexHeading(BlockProcessor state, Block block)
|
||||
{
|
||||
var paragraph = (ParagraphBlock) block;
|
||||
var headingChar = (char)0;
|
||||
bool checkForSpaces = false;
|
||||
var line = state.Line;
|
||||
var c = line.CurrentChar;
|
||||
while (c != '\0')
|
||||
{
|
||||
if (headingChar == 0)
|
||||
{
|
||||
if (c == '=' || c == '-')
|
||||
{
|
||||
headingChar = c;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (checkForSpaces)
|
||||
{
|
||||
if (!c.IsSpaceOrTab())
|
||||
{
|
||||
headingChar = (char)0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (c != headingChar)
|
||||
{
|
||||
if (c.IsSpaceOrTab())
|
||||
{
|
||||
checkForSpaces = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
headingChar = (char)0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
c = line.NextChar();
|
||||
}
|
||||
char headingChar = GetHeadingChar(ref line);
|
||||
|
||||
if (headingChar != 0)
|
||||
{
|
||||
var paragraph = (ParagraphBlock)block;
|
||||
|
||||
// If we matched a LinkReferenceDefinition before matching the heading, and the remaining
|
||||
// lines are empty, we can early exit and remove the paragraph
|
||||
if (!(TryMatchLinkReferenceDefinition(ref paragraph.Lines, state) && paragraph.Lines.Count == 0))
|
||||
@@ -123,7 +90,7 @@ namespace Markdig.Parsers
|
||||
// We discard the paragraph that will be transformed to a heading
|
||||
state.Discard(paragraph);
|
||||
|
||||
var level = headingChar == '=' ? 1 : 2;
|
||||
int level = headingChar == '=' ? 1 : 2;
|
||||
|
||||
var heading = new HeadingBlock(this)
|
||||
{
|
||||
@@ -146,7 +113,37 @@ namespace Markdig.Parsers
|
||||
return BlockState.Continue;
|
||||
}
|
||||
|
||||
private bool TryMatchLinkReferenceDefinition(ref StringLineGroup lines, BlockProcessor state)
|
||||
private static char GetHeadingChar(ref StringSlice line)
|
||||
{
|
||||
char c = line.CurrentChar;
|
||||
|
||||
if (c == '=' || c == '-')
|
||||
{
|
||||
char headingChar = c;
|
||||
|
||||
while ((c = line.NextChar()) == headingChar)
|
||||
{
|
||||
}
|
||||
|
||||
if (c == '\0')
|
||||
{
|
||||
return headingChar;
|
||||
}
|
||||
|
||||
while ((c = line.NextChar()).IsSpaceOrTab())
|
||||
{
|
||||
}
|
||||
|
||||
if (c == '\0')
|
||||
{
|
||||
return headingChar;
|
||||
}
|
||||
}
|
||||
|
||||
return (char)0;
|
||||
}
|
||||
|
||||
private static bool TryMatchLinkReferenceDefinition(ref StringLineGroup lines, BlockProcessor state)
|
||||
{
|
||||
bool atLeastOneFound = false;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Markdig.Parsers
|
||||
var parser = this[i];
|
||||
if (parser == null)
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected null parser found");
|
||||
ThrowHelper.InvalidOperationException("Unexpected null parser found");
|
||||
}
|
||||
|
||||
parser.Initialize();
|
||||
@@ -100,8 +100,8 @@ namespace Markdig.Parsers
|
||||
/// </summary>
|
||||
/// <param name="openingChar">The opening character.</param>
|
||||
/// <returns>A list of parsers valid for the specified opening character or null if no parsers registered.</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
public T[] GetParsersForOpeningCharacter(char openingChar)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T[] GetParsersForOpeningCharacter(uint openingChar)
|
||||
{
|
||||
return charMap[openingChar];
|
||||
}
|
||||
@@ -113,7 +113,7 @@ namespace Markdig.Parsers
|
||||
/// <param name="start">The start.</param>
|
||||
/// <param name="end">The end.</param>
|
||||
/// <returns>Index position within the string of the first opening character found in the specified text; if not found, returns -1</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOfOpeningCharacter(string text, int start, int end)
|
||||
{
|
||||
return charMap.IndexOfOpeningCharacter(text, start, end);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Renderers.Html
|
||||
@@ -41,7 +42,7 @@ namespace Markdig.Renderers.Html
|
||||
/// <param name="name">The css class name.</param>
|
||||
public void AddClass(string name)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (name == null) ThrowHelper.ArgumentNullException_name();
|
||||
if (Classes == null)
|
||||
{
|
||||
Classes = new List<string>(2);
|
||||
@@ -61,7 +62,7 @@ namespace Markdig.Renderers.Html
|
||||
/// <param name="value">The value.</param>
|
||||
public void AddProperty(string name, string value)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (name == null) ThrowHelper.ArgumentNullException_name();
|
||||
if (Properties == null)
|
||||
{
|
||||
Properties = new List<KeyValuePair<string, string>>(2); // Use half list compare to default capacity (4), as we don't expect lots of classes
|
||||
@@ -76,7 +77,7 @@ namespace Markdig.Renderers.Html
|
||||
/// <param name="value">The value.</param>
|
||||
public void AddPropertyIfNotExist(string name, object value)
|
||||
{
|
||||
if (name == null) throw new ArgumentNullException(nameof(name));
|
||||
if (name == null) ThrowHelper.ArgumentNullException_name();
|
||||
if (Properties == null)
|
||||
{
|
||||
Properties = new List<KeyValuePair<string, string>>(4);
|
||||
@@ -104,7 +105,7 @@ namespace Markdig.Renderers.Html
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
public void CopyTo(HtmlAttributes htmlAttributes, bool mergeIdAndProperties = false, bool shared = true)
|
||||
{
|
||||
if (htmlAttributes == null) throw new ArgumentNullException(nameof(htmlAttributes));
|
||||
if (htmlAttributes == null) ThrowHelper.ArgumentNullException(nameof(htmlAttributes));
|
||||
// Add html htmlAttributes to the object
|
||||
if (!mergeIdAndProperties || Id != null)
|
||||
{
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace Markdig.Renderers
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <returns>This instance</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public HtmlRenderer WriteEscape(string content)
|
||||
{
|
||||
if (string.IsNullOrEmpty(content))
|
||||
@@ -107,7 +107,7 @@ namespace Markdig.Renderers
|
||||
/// <param name="slice">The slice.</param>
|
||||
/// <param name="softEscape">Only escape < and &</param>
|
||||
/// <returns>This instance</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public HtmlRenderer WriteEscape(ref StringSlice slice, bool softEscape = false)
|
||||
{
|
||||
if (slice.Start > slice.End)
|
||||
@@ -123,7 +123,7 @@ namespace Markdig.Renderers
|
||||
/// <param name="slice">The slice.</param>
|
||||
/// <param name="softEscape">Only escape < and &</param>
|
||||
/// <returns>This instance</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public HtmlRenderer WriteEscape(StringSlice slice, bool softEscape = false)
|
||||
{
|
||||
return WriteEscape(ref slice, softEscape);
|
||||
@@ -318,12 +318,12 @@ namespace Markdig.Renderers
|
||||
/// <summary>
|
||||
/// Writes the attached <see cref="HtmlAttributes"/> on the specified <see cref="MarkdownObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object.</param>
|
||||
/// <param name="markdownObject">The object.</param>
|
||||
/// <returns></returns>
|
||||
public HtmlRenderer WriteAttributes(MarkdownObject obj)
|
||||
public HtmlRenderer WriteAttributes(MarkdownObject markdownObject)
|
||||
{
|
||||
if (obj == null) throw new ArgumentNullException(nameof(obj));
|
||||
return WriteAttributes(obj.TryGetAttributes());
|
||||
if (markdownObject == null) ThrowHelper.ArgumentNullException_markdownObject();
|
||||
return WriteAttributes(markdownObject.TryGetAttributes());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -383,7 +383,7 @@ namespace Markdig.Renderers
|
||||
/// <returns>This instance</returns>
|
||||
public HtmlRenderer WriteLeafRawLines(LeafBlock leafBlock, bool writeEndOfLines, bool escape, bool softEscape = false)
|
||||
{
|
||||
if (leafBlock == null) throw new ArgumentNullException(nameof(leafBlock));
|
||||
if (leafBlock == null) ThrowHelper.ArgumentNullException_leafBlock();
|
||||
if (leafBlock.Lines.Lines != null)
|
||||
{
|
||||
var lines = leafBlock.Lines;
|
||||
|
||||
@@ -6,6 +6,7 @@ using System;
|
||||
using System.IO;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Renderers.Normalize.Inlines;
|
||||
using Markdig.Helpers;
|
||||
|
||||
namespace Markdig.Renderers.Normalize
|
||||
{
|
||||
@@ -130,7 +131,7 @@ namespace Markdig.Renderers.Normalize
|
||||
/// <returns>This instance</returns>
|
||||
public NormalizeRenderer WriteLeafRawLines(LeafBlock leafBlock, bool writeEndOfLines, bool indent = false)
|
||||
{
|
||||
if (leafBlock == null) throw new ArgumentNullException(nameof(leafBlock));
|
||||
if (leafBlock == null) ThrowHelper.ArgumentNullException_leafBlock();
|
||||
if (leafBlock.Lines.Lines != null)
|
||||
{
|
||||
var lines = leafBlock.Lines;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
@@ -56,19 +56,19 @@ namespace Markdig.Renderers
|
||||
return;
|
||||
}
|
||||
|
||||
bool saveIsFirstInContainer = IsFirstInContainer;
|
||||
bool saveIsLastInContainer = IsLastInContainer;
|
||||
|
||||
var children = containerBlock;
|
||||
for (int i = 0; i < children.Count; i++)
|
||||
{
|
||||
var saveIsFirstInContainer = IsFirstInContainer;
|
||||
var saveIsLastInContainer = IsLastInContainer;
|
||||
|
||||
IsFirstInContainer = i == 0;
|
||||
IsLastInContainer = i + 1 == children.Count;
|
||||
Write(children[i]);
|
||||
|
||||
IsFirstInContainer = saveIsFirstInContainer;
|
||||
IsLastInContainer = saveIsLastInContainer;
|
||||
}
|
||||
|
||||
IsFirstInContainer = saveIsFirstInContainer;
|
||||
IsLastInContainer = saveIsLastInContainer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,46 +82,50 @@ namespace Markdig.Renderers
|
||||
return;
|
||||
}
|
||||
|
||||
bool saveIsFirstInContainer = IsFirstInContainer;
|
||||
bool saveIsLastInContainer = IsLastInContainer;
|
||||
|
||||
bool isFirst = true;
|
||||
var inline = containerInline.FirstChild;
|
||||
while (inline != null)
|
||||
{
|
||||
var saveIsFirstInContainer = IsFirstInContainer;
|
||||
var saveIsLastInContainer = IsLastInContainer;
|
||||
IsFirstInContainer = isFirst;
|
||||
IsLastInContainer = inline.NextSibling == null;
|
||||
|
||||
Write(inline);
|
||||
inline = inline.NextSibling;
|
||||
|
||||
IsFirstInContainer = saveIsFirstInContainer;
|
||||
IsLastInContainer = saveIsLastInContainer;
|
||||
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
IsFirstInContainer = saveIsFirstInContainer;
|
||||
IsLastInContainer = saveIsLastInContainer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the specified Markdown object.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">A MarkdownObject type</typeparam>
|
||||
/// <param name="obj">The Markdown object to write to this renderer.</param>
|
||||
public void Write<T>(T obj) where T : MarkdownObject
|
||||
public void Write(MarkdownObject obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Calls before writing an object
|
||||
ObjectWriteBefore?.Invoke(this, obj);
|
||||
|
||||
var objectType = obj.GetType();
|
||||
|
||||
// Calls before writing an object
|
||||
var writeBefore = ObjectWriteBefore;
|
||||
writeBefore?.Invoke(this, obj);
|
||||
IMarkdownObjectRenderer renderer;
|
||||
|
||||
// Handle regular renderers
|
||||
IMarkdownObjectRenderer renderer = previousObjectType == objectType ? previousRenderer : null;
|
||||
if (renderer == null && !renderersPerType.TryGetValue(objectType, out renderer))
|
||||
if (objectType == previousObjectType)
|
||||
{
|
||||
renderer = previousRenderer;
|
||||
}
|
||||
else if (!renderersPerType.TryGetValue(objectType, out renderer))
|
||||
{
|
||||
for (int i = 0; i < ObjectRenderers.Count; i++)
|
||||
{
|
||||
@@ -133,33 +137,25 @@ namespace Markdig.Renderers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (renderer != null)
|
||||
{
|
||||
renderer.Write(this, obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
var containerBlock = obj as ContainerBlock;
|
||||
if (containerBlock != null)
|
||||
{
|
||||
WriteChildren(containerBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
var containerInline = obj as ContainerInline;
|
||||
if (containerInline != null)
|
||||
{
|
||||
WriteChildren(containerInline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
previousObjectType = objectType;
|
||||
previousRenderer = renderer;
|
||||
previousObjectType = objectType;
|
||||
previousRenderer = renderer;
|
||||
}
|
||||
else if (obj is ContainerBlock containerBlock)
|
||||
{
|
||||
WriteChildren(containerBlock);
|
||||
}
|
||||
else if (obj is ContainerInline containerInline)
|
||||
{
|
||||
WriteChildren(containerInline);
|
||||
}
|
||||
|
||||
// Calls after writing an object
|
||||
var writeAfter = ObjectWriteAfter;
|
||||
writeAfter?.Invoke(this, obj);
|
||||
ObjectWriteAfter?.Invoke(this, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace Markdig.Renderers
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
protected TextRendererBase(TextWriter writer)
|
||||
{
|
||||
if (writer == null) throw new ArgumentNullException(nameof(writer));
|
||||
if (writer == null) ThrowHelper.ArgumentNullException_writer();
|
||||
this.Writer = writer;
|
||||
// By default we output a newline with '\n' only even on Windows platforms
|
||||
Writer.NewLine = "\n";
|
||||
@@ -43,7 +43,7 @@ namespace Markdig.Renderers
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
ThrowHelper.ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
writer = value;
|
||||
@@ -97,7 +97,7 @@ namespace Markdig.Renderers
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Cannot reset this TextWriter instance");
|
||||
ThrowHelper.InvalidOperationException("Cannot reset this TextWriter instance");
|
||||
}
|
||||
|
||||
previousWasLine = true;
|
||||
@@ -119,7 +119,7 @@ namespace Markdig.Renderers
|
||||
|
||||
public void PushIndent(string indent)
|
||||
{
|
||||
if (indent == null) throw new ArgumentNullException(nameof(indent));
|
||||
if (indent == null) ThrowHelper.ArgumentNullException(nameof(indent));
|
||||
indents.Add(indent);
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace Markdig.Renderers
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <returns>This instance</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Write(string content)
|
||||
{
|
||||
WriteIndent();
|
||||
@@ -161,7 +161,7 @@ namespace Markdig.Renderers
|
||||
/// </summary>
|
||||
/// <param name="slice">The slice.</param>
|
||||
/// <returns>This instance</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Write(ref StringSlice slice)
|
||||
{
|
||||
if (slice.Start > slice.End)
|
||||
@@ -176,7 +176,7 @@ namespace Markdig.Renderers
|
||||
/// </summary>
|
||||
/// <param name="slice">The slice.</param>
|
||||
/// <returns>This instance</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Write(StringSlice slice)
|
||||
{
|
||||
return Write(ref slice);
|
||||
@@ -187,7 +187,7 @@ namespace Markdig.Renderers
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <returns>This instance</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Write(char content)
|
||||
{
|
||||
WriteIndent();
|
||||
@@ -241,7 +241,7 @@ namespace Markdig.Renderers
|
||||
/// Writes a newline.
|
||||
/// </summary>
|
||||
/// <returns>This instance</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T WriteLine()
|
||||
{
|
||||
WriteIndent();
|
||||
@@ -255,7 +255,7 @@ namespace Markdig.Renderers
|
||||
/// </summary>
|
||||
/// <param name="content">The content.</param>
|
||||
/// <returns>This instance</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T WriteLine(string content)
|
||||
{
|
||||
WriteIndent();
|
||||
@@ -269,10 +269,10 @@ namespace Markdig.Renderers
|
||||
/// </summary>
|
||||
/// <param name="leafBlock">The leaf block.</param>
|
||||
/// <returns>This instance</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T WriteLeafInline(LeafBlock leafBlock)
|
||||
{
|
||||
if (leafBlock == null) throw new ArgumentNullException(nameof(leafBlock));
|
||||
if (leafBlock == null) ThrowHelper.ArgumentNullException_leafBlock();
|
||||
var inline = (Inline) leafBlock.Inline;
|
||||
if (inline != null)
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Markdig.Syntax
|
||||
/// <param name="parser">The parser used to create this block.</param>
|
||||
protected ContainerBlock(BlockParser parser) : base(parser)
|
||||
{
|
||||
children = ArrayHelper<Block>.Empty;
|
||||
children = Array.Empty<Block>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -56,10 +56,12 @@ namespace Markdig.Syntax
|
||||
|
||||
public void Add(Block item)
|
||||
{
|
||||
if (item == null) throw new ArgumentNullException(nameof(item));
|
||||
if (item == null)
|
||||
ThrowHelper.ArgumentNullException_item();
|
||||
|
||||
if (item.Parent != null)
|
||||
{
|
||||
throw new ArgumentException("Cannot add this block as it as already attached to another container (block.Parent != null)");
|
||||
ThrowHelper.ArgumentException("Cannot add this block as it as already attached to another container (block.Parent != null)");
|
||||
}
|
||||
|
||||
if (Count == children.Length)
|
||||
@@ -104,7 +106,9 @@ namespace Markdig.Syntax
|
||||
|
||||
public bool Contains(Block item)
|
||||
{
|
||||
if (item == null) throw new ArgumentNullException(nameof(item));
|
||||
if (item == null)
|
||||
ThrowHelper.ArgumentNullException_item();
|
||||
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (children[i] == item)
|
||||
@@ -122,7 +126,9 @@ namespace Markdig.Syntax
|
||||
|
||||
public bool Remove(Block item)
|
||||
{
|
||||
if (item == null) throw new ArgumentNullException(nameof(item));
|
||||
if (item == null)
|
||||
ThrowHelper.ArgumentNullException_item();
|
||||
|
||||
for (int i = Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (children[i] == item)
|
||||
@@ -140,7 +146,9 @@ namespace Markdig.Syntax
|
||||
|
||||
public int IndexOf(Block item)
|
||||
{
|
||||
if (item == null) throw new ArgumentNullException(nameof(item));
|
||||
if (item == null)
|
||||
ThrowHelper.ArgumentNullException_item();
|
||||
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
if (children[i] == item)
|
||||
@@ -153,14 +161,16 @@ namespace Markdig.Syntax
|
||||
|
||||
public void Insert(int index, Block item)
|
||||
{
|
||||
if (item == null) throw new ArgumentNullException(nameof(item));
|
||||
if (item == null)
|
||||
ThrowHelper.ArgumentNullException_item();
|
||||
|
||||
if (item.Parent != null)
|
||||
{
|
||||
throw new ArgumentException("Cannot add this block as it as already attached to another container (block.Parent != null)");
|
||||
ThrowHelper.ArgumentException("Cannot add this block as it as already attached to another container (block.Parent != null)");
|
||||
}
|
||||
if (index < 0 || index > Count)
|
||||
if ((uint)index > (uint)Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
ThrowHelper.ArgumentOutOfRangeException_index();
|
||||
}
|
||||
if (Count == children.Length)
|
||||
{
|
||||
@@ -177,7 +187,9 @@ namespace Markdig.Syntax
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException(nameof(index));
|
||||
if ((uint)index > (uint)Count)
|
||||
ThrowHelper.ArgumentOutOfRangeException_index();
|
||||
|
||||
Count--;
|
||||
// previous children
|
||||
var item = children[index];
|
||||
@@ -210,13 +222,13 @@ namespace Markdig.Syntax
|
||||
|
||||
public void Sort(IComparer<Block> comparer)
|
||||
{
|
||||
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
|
||||
if (comparer == null) ThrowHelper.ArgumentNullException(nameof(comparer));
|
||||
Array.Sort(children, 0, Count, comparer);
|
||||
}
|
||||
|
||||
public void Sort(Comparison<Block> comparison)
|
||||
{
|
||||
if (comparison == null) throw new ArgumentNullException(nameof(comparison));
|
||||
if (comparison == null) ThrowHelper.ArgumentNullException(nameof(comparison));
|
||||
Array.Sort(children, 0, Count, new BlockComparer(comparison));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using System;
|
||||
using System.Collections;
|
||||
@@ -50,10 +51,10 @@ namespace Markdig.Syntax.Inlines
|
||||
/// <exception cref="System.ArgumentException">Inline has already a parent</exception>
|
||||
public virtual ContainerInline AppendChild(Inline child)
|
||||
{
|
||||
if (child == null) throw new ArgumentNullException(nameof(child));
|
||||
if (child == null) ThrowHelper.ArgumentNullException(nameof(child));
|
||||
if (child.Parent != null)
|
||||
{
|
||||
throw new ArgumentException("Inline has already a parent", nameof(child));
|
||||
ThrowHelper.ArgumentException("Inline has already a parent", nameof(child));
|
||||
}
|
||||
|
||||
if (FirstChild == null)
|
||||
@@ -98,7 +99,7 @@ namespace Markdig.Syntax.Inlines
|
||||
{
|
||||
if (FirstChild is null)
|
||||
{
|
||||
return ArrayHelper<T>.Empty;
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -145,7 +146,7 @@ namespace Markdig.Syntax.Inlines
|
||||
/// <param name="parent">The parent.</param>
|
||||
public void MoveChildrenAfter(Inline parent)
|
||||
{
|
||||
if (parent == null) throw new ArgumentNullException(nameof(parent));
|
||||
if (parent == null) ThrowHelper.ArgumentNullException(nameof(parent));
|
||||
var child = FirstChild;
|
||||
var nextSibling = parent;
|
||||
while (child != null)
|
||||
@@ -166,7 +167,7 @@ namespace Markdig.Syntax.Inlines
|
||||
/// <exception cref="System.ArgumentNullException">If the container is null</exception>
|
||||
public void EmbraceChildrenBy(ContainerInline container)
|
||||
{
|
||||
if (container == null) throw new ArgumentNullException(nameof(container));
|
||||
if (container == null) ThrowHelper.ArgumentNullException(nameof(container));
|
||||
var child = FirstChild;
|
||||
while (child != null)
|
||||
{
|
||||
@@ -238,7 +239,7 @@ namespace Markdig.Syntax.Inlines
|
||||
|
||||
public Enumerator(ContainerInline container) : this()
|
||||
{
|
||||
if (container == null) throw new ArgumentNullException(nameof(container));
|
||||
if (container == null) ThrowHelper.ArgumentNullException(nameof(container));
|
||||
this.container = container;
|
||||
currentChild = nextChild = container.FirstChild;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Markdig.Syntax.Inlines
|
||||
{
|
||||
protected DelimiterInline(InlineParser parser)
|
||||
{
|
||||
if (parser == null) throw new ArgumentNullException(nameof(parser));
|
||||
if (parser == null) ThrowHelper.ArgumentNullException(nameof(parser));
|
||||
Parser = parser;
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,10 @@ namespace Markdig.Syntax.Inlines
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public EmphasisDelimiterInline(InlineParser parser, EmphasisDescriptor descriptor) : base(parser)
|
||||
{
|
||||
Descriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor));
|
||||
if (descriptor is null)
|
||||
ThrowHelper.ArgumentNullException(nameof(descriptor));
|
||||
|
||||
Descriptor = descriptor;
|
||||
DelimiterChar = descriptor.Character;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
|
||||
namespace Markdig.Syntax.Inlines
|
||||
@@ -42,10 +43,10 @@ namespace Markdig.Syntax.Inlines
|
||||
/// <exception cref="System.ArgumentException">Inline has already a parent</exception>
|
||||
public void InsertAfter(Inline next)
|
||||
{
|
||||
if (next == null) throw new ArgumentNullException(nameof(next));
|
||||
if (next == null) ThrowHelper.ArgumentNullException(nameof(next));
|
||||
if (next.Parent != null)
|
||||
{
|
||||
throw new ArgumentException("Inline has already a parent", nameof(next));
|
||||
ThrowHelper.ArgumentException("Inline has already a parent", nameof(next));
|
||||
}
|
||||
|
||||
var previousNext = NextSibling;
|
||||
@@ -73,10 +74,10 @@ namespace Markdig.Syntax.Inlines
|
||||
/// <exception cref="System.ArgumentException">Inline has already a parent</exception>
|
||||
public void InsertBefore(Inline previous)
|
||||
{
|
||||
if (previous == null) throw new ArgumentNullException(nameof(previous));
|
||||
if (previous == null) ThrowHelper.ArgumentNullException(nameof(previous));
|
||||
if (previous.Parent != null)
|
||||
{
|
||||
throw new ArgumentException("Inline has already a parent", nameof(previous));
|
||||
ThrowHelper.ArgumentException("Inline has already a parent", nameof(previous));
|
||||
}
|
||||
|
||||
var previousSibling = PreviousSibling;
|
||||
@@ -129,7 +130,7 @@ namespace Markdig.Syntax.Inlines
|
||||
/// <exception cref="System.ArgumentNullException">If inline is null</exception>
|
||||
public Inline ReplaceBy(Inline inline, bool copyChildren = true)
|
||||
{
|
||||
if (inline == null) throw new ArgumentNullException(nameof(inline));
|
||||
if (inline == null) ThrowHelper.ArgumentNullException(nameof(inline));
|
||||
|
||||
// Save sibling
|
||||
var parent = Parent;
|
||||
@@ -271,7 +272,7 @@ namespace Markdig.Syntax.Inlines
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
public void DumpTo(TextWriter writer)
|
||||
{
|
||||
if (writer == null) throw new ArgumentNullException(nameof(writer));
|
||||
if (writer == null) ThrowHelper.ArgumentNullException_writer();
|
||||
DumpTo(writer, 0);
|
||||
}
|
||||
|
||||
@@ -283,7 +284,7 @@ namespace Markdig.Syntax.Inlines
|
||||
/// <exception cref="System.ArgumentNullException">if writer is null</exception>
|
||||
public void DumpTo(TextWriter writer, int level)
|
||||
{
|
||||
if (writer == null) throw new ArgumentNullException(nameof(writer));
|
||||
if (writer == null) ThrowHelper.ArgumentNullException_writer();
|
||||
for (int i = 0; i < level; i++)
|
||||
{
|
||||
writer.Write(' ');
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Markdig.Syntax.Inlines
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
public LiteralInline(string text)
|
||||
{
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
if (text == null) ThrowHelper.ArgumentNullException_text();
|
||||
Content = new StringSlice(text);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using Markdig.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -15,7 +16,7 @@ namespace Markdig.Syntax
|
||||
|
||||
public static bool ContainsLinkReferenceDefinition(this MarkdownDocument document, string label)
|
||||
{
|
||||
if (label == null) throw new ArgumentNullException(nameof(label));
|
||||
if (label == null) ThrowHelper.ArgumentNullException_label();
|
||||
var references = document.GetData(DocumentKey) as LinkReferenceDefinitionGroup;
|
||||
if (references == null)
|
||||
{
|
||||
@@ -26,14 +27,14 @@ namespace Markdig.Syntax
|
||||
|
||||
public static void SetLinkReferenceDefinition(this MarkdownDocument document, string label, LinkReferenceDefinition linkReferenceDefinition)
|
||||
{
|
||||
if (label == null) throw new ArgumentNullException(nameof(label));
|
||||
if (label == null) ThrowHelper.ArgumentNullException_label();
|
||||
var references = document.GetLinkReferenceDefinitions();
|
||||
references.Set(label, linkReferenceDefinition);
|
||||
}
|
||||
|
||||
public static bool TryGetLinkReferenceDefinition(this MarkdownDocument document, string label, out LinkReferenceDefinition linkReferenceDefinition)
|
||||
{
|
||||
if (label == null) throw new ArgumentNullException(nameof(label));
|
||||
if (label == null) ThrowHelper.ArgumentNullException_label();
|
||||
linkReferenceDefinition = null;
|
||||
var references = document.GetData(DocumentKey) as LinkReferenceDefinitionGroup;
|
||||
if (references == null)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -28,7 +29,7 @@ namespace Markdig.Syntax
|
||||
|
||||
public void Set(string label, LinkReferenceDefinition link)
|
||||
{
|
||||
if (link == null) throw new ArgumentNullException(nameof(link));
|
||||
if (link == null) ThrowHelper.ArgumentNullException(nameof(link));
|
||||
if (!Contains(link))
|
||||
{
|
||||
Add(link);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using Markdig.Helpers;
|
||||
using System;
|
||||
|
||||
namespace Markdig.Syntax
|
||||
@@ -55,7 +56,7 @@ namespace Markdig.Syntax
|
||||
/// <exception cref="System.ArgumentNullException">if key is null</exception>
|
||||
public void SetData(object key, object value)
|
||||
{
|
||||
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||
if (key == null) ThrowHelper.ArgumentNullException_key();
|
||||
if (attachedDatas == null)
|
||||
{
|
||||
attachedDatas = new DataEntry[1];
|
||||
@@ -89,7 +90,7 @@ namespace Markdig.Syntax
|
||||
/// <exception cref="System.ArgumentNullException">if key is null</exception>
|
||||
public bool ContainsData(object key)
|
||||
{
|
||||
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||
if (key == null) ThrowHelper.ArgumentNullException_key();
|
||||
if (attachedDatas == null)
|
||||
{
|
||||
return false;
|
||||
@@ -113,7 +114,7 @@ namespace Markdig.Syntax
|
||||
/// <exception cref="System.ArgumentNullException">if key is null</exception>
|
||||
public object GetData(object key)
|
||||
{
|
||||
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||
if (key == null) ThrowHelper.ArgumentNullException_key();
|
||||
if (attachedDatas == null)
|
||||
{
|
||||
return null;
|
||||
@@ -136,7 +137,7 @@ namespace Markdig.Syntax
|
||||
/// <exception cref="System.ArgumentNullException"></exception>
|
||||
public bool RemoveData(object key)
|
||||
{
|
||||
if (key == null) throw new ArgumentNullException(nameof(key));
|
||||
if (key == null) ThrowHelper.ArgumentNullException_key();
|
||||
if (attachedDatas == null)
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// 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.Diagnostics;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Syntax
|
||||
@@ -94,7 +94,7 @@ namespace Markdig.Syntax
|
||||
}
|
||||
}
|
||||
|
||||
return ArrayHelper<T>.Empty;
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -124,7 +124,7 @@ namespace Markdig.Syntax
|
||||
}
|
||||
else
|
||||
{
|
||||
return ArrayHelper<T>.Empty;
|
||||
return Array.Empty<T>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace SpecFileGen
|
||||
|
||||
if (anyChanged && Environment.GetEnvironmentVariable("CI") != null)
|
||||
{
|
||||
EmitError("Run SpecFileGen when changing specification files");
|
||||
EmitError("Error - Specification files have changed. You must run SpecFileGen when changing specification files.");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user