mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-07 13:57:13 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c01cf0549 | ||
|
|
bcbd8e47ac | ||
|
|
d6e88f16f7 | ||
|
|
03bdf60086 | ||
|
|
5c78932f55 | ||
|
|
191e33ab32 | ||
|
|
800235ba7a | ||
|
|
d5f8a809a0 | ||
|
|
781d9b5365 | ||
|
|
543570224e | ||
|
|
4dc0be88b4 | ||
|
|
0e9e80e1cd | ||
|
|
1b04599c44 | ||
|
|
5e6fb2d1c5 | ||
|
|
14406bc60d | ||
|
|
2aa6780a30 | ||
|
|
c43646586c | ||
|
|
d548b82bcd | ||
|
|
aab5543cb5 | ||
|
|
2e1d741aaf | ||
|
|
80c50e31e2 | ||
|
|
7ff8db9016 | ||
|
|
c69fb9ae73 | ||
|
|
5a3c206076 | ||
|
|
b92890094c |
@@ -12,8 +12,9 @@ insert_final_newline = false
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# Solution Files
|
||||
[*.sln]
|
||||
indent_style = tab
|
||||
[*.slnx]
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
|
||||
# XML Project Files
|
||||
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
|
||||
@@ -35,3 +36,8 @@ insert_final_newline = true
|
||||
# Bash Files
|
||||
[*.sh]
|
||||
end_of_line = lf
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
# License header
|
||||
file_header_template = Copyright (c) Alexandre Mutel. All rights reserved.\nThis file is licensed under the BSD-Clause 2 license.\nSee the license.txt file in the project root for more information.
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,3 +1,3 @@
|
||||
* text=auto
|
||||
*.cs text=auto diff=csharp
|
||||
*.sln text=auto eol=crlf
|
||||
*.slnx text=auto eol=crlf
|
||||
20
readme.md
20
readme.md
@@ -2,7 +2,7 @@
|
||||
|
||||
<img align="right" width="160px" height="160px" src="img/markdig.png">
|
||||
|
||||
Markdig is a fast, powerful, [CommonMark](http://commonmark.org/) compliant, extensible Markdown processor for .NET.
|
||||
Markdig is a fast, powerful, [CommonMark](https://commonmark.org/) compliant, extensible Markdown processor for .NET.
|
||||
|
||||
> NOTE: The repository is under construction. There will be a dedicated website and proper documentation at some point!
|
||||
|
||||
@@ -14,7 +14,7 @@ You can **try Markdig online** and compare it to other implementations on [babel
|
||||
- **Abstract Syntax Tree** with precise source code location for syntax tree, useful when building a Markdown editor.
|
||||
- Checkout [Markdown Editor v2 for Visual Studio 2022](https://marketplace.visualstudio.com/items?itemName=MadsKristensen.MarkdownEditor2) powered by Markdig!
|
||||
- Converter to **HTML**
|
||||
- Passing more than **600+ tests** from the latest [CommonMark specs (0.31.2)](http://spec.commonmark.org/)
|
||||
- Passing more than **600+ tests** from the latest [CommonMark specs (0.31.2)](https://spec.commonmark.org/)
|
||||
- Includes all the core elements of CommonMark:
|
||||
- including **GFM fenced code blocks**.
|
||||
- **Extensible** architecture
|
||||
@@ -22,9 +22,9 @@ You can **try Markdig online** and compare it to other implementations on [babel
|
||||
- [**Roundtrip support**](./src/Markdig/Roundtrip.md): Parses trivia (whitespace, newlines and other characters) to support lossless parse ⭢ render roundtrip. This enables changing markdown documents without introducing undesired trivia changes.
|
||||
- Built-in with **20+ extensions**, including:
|
||||
- 2 kind of tables:
|
||||
- [**Pipe tables**](src/Markdig.Tests/Specs/PipeTableSpecs.md) (inspired from GitHub tables and [PanDoc - Pipe Tables](http://pandoc.org/README.html#extension-pipe_tables))
|
||||
- [**Grid tables**](src/Markdig.Tests/Specs/GridTableSpecs.md) (inspired from [Pandoc - Grid Tables](http://pandoc.org/README.html#extension-grid_tables))
|
||||
- [**Extra emphasis**](src/Markdig.Tests/Specs/EmphasisExtraSpecs.md) (inspired from [Pandoc - Emphasis](http://pandoc.org/README.html#strikeout) and [Markdown-it](https://markdown-it.github.io/))
|
||||
- [**Pipe tables**](src/Markdig.Tests/Specs/PipeTableSpecs.md) (inspired from GitHub tables and [PanDoc - Pipe Tables](https://pandoc.org/MANUAL.html#extension-pipe_tables))
|
||||
- [**Grid tables**](src/Markdig.Tests/Specs/GridTableSpecs.md) (inspired from [Pandoc - Grid Tables](https://pandoc.org/MANUAL.html#extension-grid_tables))
|
||||
- [**Extra emphasis**](src/Markdig.Tests/Specs/EmphasisExtraSpecs.md) (inspired from [Pandoc - Emphasis](https://pandoc.org/MANUAL.html#strikeout) and [Markdown-it](https://markdown-it.github.io/))
|
||||
- strike through `~~`,
|
||||
- Subscript `~`
|
||||
- Superscript `^`
|
||||
@@ -33,7 +33,7 @@ You can **try Markdig online** and compare it to other implementations on [babel
|
||||
- [**Special attributes**](src/Markdig.Tests/Specs/GenericAttributesSpecs.md) or attached HTML attributes (inspired from [PHP Markdown Extra - Special Attributes](https://michelf.ca/projects/php-markdown/extra/#spe-attr))
|
||||
- [**Definition lists**](src/Markdig.Tests/Specs/DefinitionListSpecs.md) (inspired from [PHP Markdown Extra - Definitions Lists](https://michelf.ca/projects/php-markdown/extra/#def-list))
|
||||
- [**Footnotes**](src/Markdig.Tests/Specs/FootnotesSpecs.md) (inspired from [PHP Markdown Extra - Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes))
|
||||
- [**Auto-identifiers**](src/Markdig.Tests/Specs/AutoIdentifierSpecs.md) for headings (similar to [Pandoc - Auto Identifiers](http://pandoc.org/README.html#extension-auto_identifiers))
|
||||
- [**Auto-identifiers**](src/Markdig.Tests/Specs/AutoIdentifierSpecs.md) for headings (similar to [Pandoc - Auto Identifiers](https://pandoc.org/MANUAL.html#extension-auto_identifiers))
|
||||
- [**Auto-links**](src/Markdig.Tests/Specs/AutoLinks.md) generates links if a text starts with `http://` or `https://` or `ftp://` or `mailto:` or `www.xxx.yyy`
|
||||
- [**Task Lists**](src/Markdig.Tests/Specs/TaskListSpecs.md) inspired from [Github Task lists](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments).
|
||||
- [**Extra bullet lists**](src/Markdig.Tests/Specs/ListExtraSpecs.md), supporting alpha bullet `a.` `b.` and roman bullet (`i`, `ii`...etc.)
|
||||
@@ -70,7 +70,7 @@ If you are looking for support for an old .NET Framework 3.5 or 4.0, you can dow
|
||||
|
||||
While there is not yet a dedicated documentation, you can find from the [specs documentation](src/Markdig.Tests/Specs/readme.md) how to use these extensions.
|
||||
|
||||
In the meantime, you can have a "behind the scene" article about Markdig in my blog post ["Implementing a Markdown Engine for .NET"](http://xoofx.github.io/blog/2016/06/13/implementing-a-markdown-processor-for-dotnet/)
|
||||
In the meantime, you can have a "behind the scene" article about Markdig in my blog post ["Implementing a Markdown Engine for .NET"](https://xoofx.github.io/blog/2016/06/13/implementing-a-markdown-processor-for-dotnet/)
|
||||
|
||||
## Download
|
||||
|
||||
@@ -153,7 +153,7 @@ image editing, optimization, and delivery server](https://github.com/imazen/imag
|
||||
|
||||
## Credits
|
||||
|
||||
Thanks to the fantastic work done by [John Mac Farlane](http://johnmacfarlane.net/) for the CommonMark specs and all the people involved in making Markdown a better standard!
|
||||
Thanks to the fantastic work done by [John Mac Farlane](https://johnmacfarlane.net/) for the CommonMark specs and all the people involved in making Markdown a better standard!
|
||||
|
||||
This project would not have been possible without this huge foundation.
|
||||
|
||||
@@ -161,7 +161,7 @@ Thanks also to the project [BenchmarkDotNet](https://github.com/PerfDotNet/Bench
|
||||
|
||||
Some decoding part (e.g HTML [EntityHelper.cs](https://github.com/lunet-io/markdig/blob/master/src/Markdig/Helpers/EntityHelper.cs)) have been re-used from [CommonMark.NET](https://github.com/Knagis/CommonMark.NET)
|
||||
|
||||
Thanks to the work done by @clarkd on the JIRA Link extension (https://github.com/clarkd/MarkdigJiraLinker), now included with this project!
|
||||
Thanks to the work done by @clarkd on the [JIRA Link extension](https://github.com/clarkd/MarkdigJiraLinker), now included with this project!
|
||||
## Author
|
||||
|
||||
Alexandre MUTEL aka [xoofx](http://xoofx.github.io)
|
||||
Alexandre MUTEL aka [xoofx](https://xoofx.github.io/)
|
||||
|
||||
4
src/Markdig.Fuzzing/.gitignore
vendored
Normal file
4
src/Markdig.Fuzzing/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
corpus
|
||||
libfuzzer-dotnet-windows.exe
|
||||
crash-*
|
||||
timeout-*
|
||||
19
src/Markdig.Fuzzing/Markdig.Fuzzing.csproj
Normal file
19
src/Markdig.Fuzzing/Markdig.Fuzzing.csproj
Normal file
@@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SharpFuzz" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Markdig\Markdig.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
71
src/Markdig.Fuzzing/Program.cs
Normal file
71
src/Markdig.Fuzzing/Program.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using Markdig;
|
||||
using Markdig.Renderers.Roundtrip;
|
||||
using Markdig.Syntax;
|
||||
using SharpFuzz;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
ReadOnlySpanAction fuzzTarget = ParseRenderFuzzer.FuzzTarget;
|
||||
|
||||
if (args.Length > 0)
|
||||
{
|
||||
// Run the target on existing inputs
|
||||
string[] files = Directory.Exists(args[0])
|
||||
? Directory.GetFiles(args[0])
|
||||
: [args[0]];
|
||||
|
||||
Debugger.Launch();
|
||||
|
||||
foreach (string inputFile in files)
|
||||
{
|
||||
fuzzTarget(File.ReadAllBytes(inputFile));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Fuzzer.LibFuzzer.Run(fuzzTarget);
|
||||
}
|
||||
|
||||
sealed class ParseRenderFuzzer
|
||||
{
|
||||
private static readonly MarkdownPipeline s_advancedPipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
private static readonly ResettableRoundtripRenderer _roundtripRenderer = new();
|
||||
|
||||
public static void FuzzTarget(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
string text = Encoding.UTF8.GetString(bytes);
|
||||
|
||||
try
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse(text);
|
||||
_ = document.ToHtml();
|
||||
|
||||
document = Markdown.Parse(text, s_advancedPipeline);
|
||||
_ = document.ToHtml(s_advancedPipeline);
|
||||
|
||||
document = Markdown.Parse(text, trackTrivia: true);
|
||||
_ = document.ToHtml();
|
||||
_roundtripRenderer.Reset();
|
||||
_roundtripRenderer.Render(document);
|
||||
|
||||
_ = Markdown.Normalize(text);
|
||||
_ = Markdown.ToPlainText(text);
|
||||
}
|
||||
catch (Exception ex) when (IsIgnorableException(ex)) { }
|
||||
}
|
||||
|
||||
private static bool IsIgnorableException(Exception exception)
|
||||
{
|
||||
return exception.Message.Contains("Markdown elements in the input are too deeply nested", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private sealed class ResettableRoundtripRenderer : RoundtripRenderer
|
||||
{
|
||||
public ResettableRoundtripRenderer() : base(new StringWriter(new StringBuilder(1024 * 1024))) { }
|
||||
|
||||
public new void Reset() => base.Reset();
|
||||
}
|
||||
}
|
||||
86
src/Markdig.Fuzzing/run-fuzzer.ps1
Normal file
86
src/Markdig.Fuzzing/run-fuzzer.ps1
Normal file
@@ -0,0 +1,86 @@
|
||||
param (
|
||||
[string]$configuration = $null
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
$libFuzzer = "libfuzzer-dotnet-windows.exe"
|
||||
$outputDir = "bin"
|
||||
|
||||
function Get-LibFuzzer {
|
||||
param (
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
$libFuzzerUrl = "https://github.com/Metalnem/libfuzzer-dotnet/releases/download/v2025.05.02.0904/libfuzzer-dotnet-windows.exe"
|
||||
$expectedHash = "17af5b3f6ff4d2c57b44b9a35c13051b570eb66f0557d00015df3832709050bf"
|
||||
|
||||
Write-Output "Downloading libFuzzer from $libFuzzerUrl..."
|
||||
|
||||
try {
|
||||
$tempFile = "$Path.tmp"
|
||||
Invoke-WebRequest -Uri $libFuzzerUrl -OutFile $tempFile -UseBasicParsing
|
||||
|
||||
$downloadedHash = (Get-FileHash -Path $tempFile -Algorithm SHA256).Hash
|
||||
|
||||
if ($downloadedHash -eq $ExpectedHash) {
|
||||
Move-Item -Path $tempFile -Destination $Path -Force
|
||||
Write-Output "libFuzzer downloaded successfully to $Path"
|
||||
}
|
||||
else {
|
||||
Write-Error "Hash validation failed."
|
||||
Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Error "Failed to download libFuzzer: $($_.Exception.Message)"
|
||||
Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Check if libFuzzer exists, download if not
|
||||
if (-not (Test-Path $libFuzzer)) {
|
||||
Get-LibFuzzer -Path $libFuzzer
|
||||
}
|
||||
|
||||
$toolListOutput = dotnet tool list --global sharpFuzz.CommandLine 2>$null
|
||||
if (-not ($toolListOutput -match "sharpfuzz")) {
|
||||
Write-Output "Installing sharpfuzz CLI"
|
||||
dotnet tool install --global sharpFuzz.CommandLine
|
||||
}
|
||||
|
||||
if (Test-Path $outputDir) {
|
||||
Remove-Item -Recurse -Force $outputDir
|
||||
}
|
||||
|
||||
if ($configuration -eq $null) {
|
||||
$configuration = "Debug"
|
||||
}
|
||||
|
||||
dotnet publish -c $configuration -o $outputDir
|
||||
|
||||
$project = Join-Path $outputDir "Markdig.Fuzzing.dll"
|
||||
|
||||
$fuzzingTarget = Join-Path $outputDir "Markdig.dll"
|
||||
|
||||
Write-Output "Instrumenting $fuzzingTarget"
|
||||
& sharpfuzz $fuzzingTarget
|
||||
|
||||
if ($LastExitCode -ne 0) {
|
||||
Write-Error "An error occurred while instrumenting $fuzzingTarget"
|
||||
exit 1
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path corpus | Out-Null
|
||||
|
||||
$libFuzzerArgs = @("--target_path=dotnet", "--target_arg=$project", "-timeout=10", "corpus")
|
||||
|
||||
# Add any additional arguments passed to the script
|
||||
if ($args) {
|
||||
$libFuzzerArgs += $args
|
||||
}
|
||||
|
||||
Write-Output "Starting libFuzzer with arguments: $libFuzzerArgs"
|
||||
& ./$libFuzzer @libFuzzerArgs
|
||||
@@ -9,6 +9,7 @@
|
||||
<StartupObject>Markdig.Tests.Program</StartupObject>
|
||||
<SpecExecutable>$(MSBuildProjectDirectory)\..\SpecFileGen\bin\$(Configuration)\$(TargetFramework)\SpecFileGen.dll</SpecExecutable>
|
||||
<SpecTimestamp>$(MSBuildProjectDirectory)\..\SpecFileGen\bin\$(Configuration)\$(TargetFramework)\SpecFileGen.timestamp</SpecTimestamp>
|
||||
<NoWarn>$(NoWarn);NETSDK1138</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -386,4 +386,27 @@ Also not a note.</p>
|
||||
";
|
||||
TestParser.TestSpec(input, expected, new MarkdownPipelineBuilder().UseAlertBlocks().Build());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void TestIssue845ListItemBlankLine()
|
||||
{
|
||||
TestParser.TestSpec("-\n\n foo",@"
|
||||
<ul>
|
||||
<li></li>
|
||||
</ul>
|
||||
<p>foo</p>");
|
||||
TestParser.TestSpec("-\n-\n\n foo",@"
|
||||
<ul>
|
||||
<li></li>
|
||||
<li></li>
|
||||
</ul>
|
||||
<p>foo</p>");
|
||||
TestParser.TestSpec("-\n\n-\n\n foo",@"
|
||||
<ul>
|
||||
<li></li>
|
||||
<li></li>
|
||||
</ul>
|
||||
<p>foo</p>");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ public class TestUnorderedList
|
||||
[TestCase("-\ti1")]
|
||||
[TestCase("-\ti1\n-\ti2")]
|
||||
[TestCase("-\ti1\n- i2\n-\ti3")]
|
||||
[TestCase("- 1.\n- 2.")]
|
||||
public void Test(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
|
||||
@@ -98,5 +98,22 @@ namespace Markdig.Tests.Specs.GenericAttributes
|
||||
|
||||
TestParser.TestSpec("[Foo](url){data-x=1}\n\n[Foo](url){data-x='1'}\n\n[Foo](url){data-x=11}", "<p><a href=\"url\" data-x=\"1\">Foo</a></p>\n<p><a href=\"url\" data-x=\"1\">Foo</a></p>\n<p><a href=\"url\" data-x=\"11\">Foo</a></p>", "attributes|advanced", context: "Example 3\nSection Extensions / Generic Attributes\n");
|
||||
}
|
||||
|
||||
// Attributes that occur immediately before a block element, on a line by themselves, affect that element
|
||||
[Test]
|
||||
public void ExtensionsGenericAttributes_Example004()
|
||||
{
|
||||
// Example 4
|
||||
// Section: Extensions / Generic Attributes
|
||||
//
|
||||
// The following Markdown:
|
||||
// {.center}
|
||||
// A paragraph
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p class="center">A paragraph</p>
|
||||
|
||||
TestParser.TestSpec("{.center}\nA paragraph", "<p class=\"center\">A paragraph</p>", "attributes|advanced", context: "Example 4\nSection Extensions / Generic Attributes\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,3 +61,12 @@ Attribute values can be one character long
|
||||
<p><a href="url" data-x="1">Foo</a></p>
|
||||
<p><a href="url" data-x="11">Foo</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
Attributes that occur immediately before a block element, on a line by themselves, affect that element
|
||||
|
||||
```````````````````````````````` example
|
||||
{.center}
|
||||
A paragraph
|
||||
.
|
||||
<p class="center">A paragraph</p>
|
||||
````````````````````````````````
|
||||
|
||||
@@ -386,5 +386,34 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
|
||||
TestParser.TestSpec("+", "<ul>\n<li></li>\n</ul>", "gridtables|advanced", context: "Example 11\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// A table may begin right after a paragraph without an empty line in between:
|
||||
[Test]
|
||||
public void ExtensionsGridTable_Example012()
|
||||
{
|
||||
// Example 12
|
||||
// Section: Extensions / Grid Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// Some
|
||||
// **text**.
|
||||
// +---+
|
||||
// | A |
|
||||
// +---+
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>Some
|
||||
// <strong>text</strong>.</p>
|
||||
// <table>
|
||||
// <col style="width:100%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>A</td>
|
||||
// </tr>
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
TestParser.TestSpec("Some\n**text**.\n+---+\n| A |\n+---+", "<p>Some\n<strong>text</strong>.</p>\n<table>\n<col style=\"width:100%\" />\n<tbody>\n<tr>\n<td>A</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced", context: "Example 12\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,3 +285,24 @@ An empty `+` on a line should result in a simple empty list output:
|
||||
<li></li>
|
||||
</ul>
|
||||
````````````````````````````````
|
||||
|
||||
A table may begin right after a paragraph without an empty line in between:
|
||||
|
||||
```````````````````````````````` example
|
||||
Some
|
||||
**text**.
|
||||
+---+
|
||||
| A |
|
||||
+---+
|
||||
.
|
||||
<p>Some
|
||||
<strong>text</strong>.</p>
|
||||
<table>
|
||||
<col style="width:100%" />
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>A</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
@@ -825,5 +825,190 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
|
||||
TestParser.TestSpec("a | b\n-- | - \n0 | 1 | 2", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n<th></th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n<td>2</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 25\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// A table may begin right after a paragraph without an empty line in between:
|
||||
[Test]
|
||||
public void ExtensionsPipeTable_Example026()
|
||||
{
|
||||
// Example 26
|
||||
// Section: Extensions / Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// Some
|
||||
// **text**.
|
||||
// | A |
|
||||
// |---|
|
||||
// | B |
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>Some
|
||||
// <strong>text</strong>.</p>
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>A</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>B</td>
|
||||
// </tr>
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
TestParser.TestSpec("Some\n**text**.\n| A |\n|---|\n| B |", "<p>Some\n<strong>text</strong>.</p>\n<table>\n<thead>\n<tr>\n<th>A</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>B</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 26\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// Tables can be nested inside other blocks, like lists:
|
||||
[Test]
|
||||
public void ExtensionsPipeTable_Example027()
|
||||
{
|
||||
// Example 27
|
||||
// Section: Extensions / Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// Bullet list
|
||||
// * Table 1
|
||||
//
|
||||
// | Header 1 | Header 2 |
|
||||
// |----------------|----------------|
|
||||
// | Row 1 Column 1 | Row 1 Column 2 |
|
||||
//
|
||||
// * Table 2
|
||||
// | Header 1 | Header 2 |
|
||||
// |----------------|----------------|
|
||||
// | Row 1 Column 1 | Row 1 Column 2 |
|
||||
//
|
||||
// * Table 3
|
||||
// Lorem ipsum ...
|
||||
// Lorem ipsum ...
|
||||
// | Header 1 | Header 2 |
|
||||
// |----------------|----------------|
|
||||
// | Row 1 Column 1 | Row 1 Column 2 |
|
||||
//
|
||||
//
|
||||
// Ordered list
|
||||
// 1. Table 1
|
||||
//
|
||||
// | Header 1 | Header 2 |
|
||||
// |----------------|----------------|
|
||||
// | Row 1 Column 1 | Row 1 Column 2 |
|
||||
//
|
||||
// 2. Table 2
|
||||
// | Header 1 | Header 2 |
|
||||
// |----------------|----------------|
|
||||
// | Row 1 Column 1 | Row 1 Column 2 |
|
||||
//
|
||||
// 3. Table 3
|
||||
// Lorem ipsum ...
|
||||
// Lorem ipsum ...
|
||||
// | Header 1 | Header 2 |
|
||||
// |----------------|----------------|
|
||||
// | Row 1 Column 1 | Row 1 Column 2 |
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>Bullet list</p>
|
||||
// <ul>
|
||||
// <li><p>Table 1</p>
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>Header 1</th>
|
||||
// <th>Header 2</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>Row 1 Column 1</td>
|
||||
// <td>Row 1 Column 2</td>
|
||||
// </tr>
|
||||
// </tbody>
|
||||
// </table></li>
|
||||
// <li><p>Table 2</p>
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>Header 1</th>
|
||||
// <th>Header 2</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>Row 1 Column 1</td>
|
||||
// <td>Row 1 Column 2</td>
|
||||
// </tr>
|
||||
// </tbody>
|
||||
// </table></li>
|
||||
// <li><p>Table 3
|
||||
// Lorem ipsum ...
|
||||
// Lorem ipsum ...</p>
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>Header 1</th>
|
||||
// <th>Header 2</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>Row 1 Column 1</td>
|
||||
// <td>Row 1 Column 2</td>
|
||||
// </tr>
|
||||
// </tbody>
|
||||
// </table></li>
|
||||
// </ul>
|
||||
// <p>Ordered list</p>
|
||||
// <ol>
|
||||
// <li><p>Table 1</p>
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>Header 1</th>
|
||||
// <th>Header 2</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>Row 1 Column 1</td>
|
||||
// <td>Row 1 Column 2</td>
|
||||
// </tr>
|
||||
// </tbody>
|
||||
// </table></li>
|
||||
// <li><p>Table 2</p>
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>Header 1</th>
|
||||
// <th>Header 2</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>Row 1 Column 1</td>
|
||||
// <td>Row 1 Column 2</td>
|
||||
// </tr>
|
||||
// </tbody>
|
||||
// </table></li>
|
||||
// <li><p>Table 3
|
||||
// Lorem ipsum ...
|
||||
// Lorem ipsum ...</p>
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>Header 1</th>
|
||||
// <th>Header 2</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>Row 1 Column 1</td>
|
||||
// <td>Row 1 Column 2</td>
|
||||
// </tr>
|
||||
// </tbody>
|
||||
// </table></li>
|
||||
// </ol>
|
||||
|
||||
TestParser.TestSpec("Bullet list\n* Table 1\n\n | Header 1 | Header 2 |\n |----------------|----------------|\n | Row 1 Column 1 | Row 1 Column 2 |\n\n* Table 2\n | Header 1 | Header 2 |\n |----------------|----------------|\n | Row 1 Column 1 | Row 1 Column 2 |\n\n* Table 3\n Lorem ipsum ...\n Lorem ipsum ...\n | Header 1 | Header 2 |\n |----------------|----------------|\n | Row 1 Column 1 | Row 1 Column 2 |\n\n\nOrdered list\n1. Table 1\n\n | Header 1 | Header 2 |\n |----------------|----------------|\n | Row 1 Column 1 | Row 1 Column 2 |\n\n2. Table 2\n | Header 1 | Header 2 |\n |----------------|----------------|\n | Row 1 Column 1 | Row 1 Column 2 |\n\n3. Table 3\n Lorem ipsum ...\n Lorem ipsum ...\n | Header 1 | Header 2 |\n |----------------|----------------|\n | Row 1 Column 1 | Row 1 Column 2 |", "<p>Bullet list</p>\n<ul>\n<li><p>Table 1</p>\n<table>\n<thead>\n<tr>\n<th>Header 1</th>\n<th>Header 2</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Row 1 Column 1</td>\n<td>Row 1 Column 2</td>\n</tr>\n</tbody>\n</table></li>\n<li><p>Table 2</p>\n<table>\n<thead>\n<tr>\n<th>Header 1</th>\n<th>Header 2</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Row 1 Column 1</td>\n<td>Row 1 Column 2</td>\n</tr>\n</tbody>\n</table></li>\n<li><p>Table 3\nLorem ipsum ...\nLorem ipsum ...</p>\n<table>\n<thead>\n<tr>\n<th>Header 1</th>\n<th>Header 2</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Row 1 Column 1</td>\n<td>Row 1 Column 2</td>\n</tr>\n</tbody>\n</table></li>\n</ul>\n<p>Ordered list</p>\n<ol>\n<li><p>Table 1</p>\n<table>\n<thead>\n<tr>\n<th>Header 1</th>\n<th>Header 2</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Row 1 Column 1</td>\n<td>Row 1 Column 2</td>\n</tr>\n</tbody>\n</table></li>\n<li><p>Table 2</p>\n<table>\n<thead>\n<tr>\n<th>Header 1</th>\n<th>Header 2</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Row 1 Column 1</td>\n<td>Row 1 Column 2</td>\n</tr>\n</tbody>\n</table></li>\n<li><p>Table 3\nLorem ipsum ...\nLorem ipsum ...</p>\n<table>\n<thead>\n<tr>\n<th>Header 1</th>\n<th>Header 2</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Row 1 Column 1</td>\n<td>Row 1 Column 2</td>\n</tr>\n</tbody>\n</table></li>\n</ol>", "pipetables|advanced", context: "Example 27\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,4 +612,173 @@ a | b
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
A table may begin right after a paragraph without an empty line in between:
|
||||
|
||||
```````````````````````````````` example
|
||||
Some
|
||||
**text**.
|
||||
| A |
|
||||
|---|
|
||||
| B |
|
||||
.
|
||||
<p>Some
|
||||
<strong>text</strong>.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>A</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>B</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
Tables can be nested inside other blocks, like lists:
|
||||
|
||||
```````````````````````````````` example
|
||||
Bullet list
|
||||
* Table 1
|
||||
|
||||
| Header 1 | Header 2 |
|
||||
|----------------|----------------|
|
||||
| Row 1 Column 1 | Row 1 Column 2 |
|
||||
|
||||
* Table 2
|
||||
| Header 1 | Header 2 |
|
||||
|----------------|----------------|
|
||||
| Row 1 Column 1 | Row 1 Column 2 |
|
||||
|
||||
* Table 3
|
||||
Lorem ipsum ...
|
||||
Lorem ipsum ...
|
||||
| Header 1 | Header 2 |
|
||||
|----------------|----------------|
|
||||
| Row 1 Column 1 | Row 1 Column 2 |
|
||||
|
||||
|
||||
Ordered list
|
||||
1. Table 1
|
||||
|
||||
| Header 1 | Header 2 |
|
||||
|----------------|----------------|
|
||||
| Row 1 Column 1 | Row 1 Column 2 |
|
||||
|
||||
2. Table 2
|
||||
| Header 1 | Header 2 |
|
||||
|----------------|----------------|
|
||||
| Row 1 Column 1 | Row 1 Column 2 |
|
||||
|
||||
3. Table 3
|
||||
Lorem ipsum ...
|
||||
Lorem ipsum ...
|
||||
| Header 1 | Header 2 |
|
||||
|----------------|----------------|
|
||||
| Row 1 Column 1 | Row 1 Column 2 |
|
||||
.
|
||||
<p>Bullet list</p>
|
||||
<ul>
|
||||
<li><p>Table 1</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Header 1</th>
|
||||
<th>Header 2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Row 1 Column 1</td>
|
||||
<td>Row 1 Column 2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></li>
|
||||
<li><p>Table 2</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Header 1</th>
|
||||
<th>Header 2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Row 1 Column 1</td>
|
||||
<td>Row 1 Column 2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></li>
|
||||
<li><p>Table 3
|
||||
Lorem ipsum ...
|
||||
Lorem ipsum ...</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Header 1</th>
|
||||
<th>Header 2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Row 1 Column 1</td>
|
||||
<td>Row 1 Column 2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></li>
|
||||
</ul>
|
||||
<p>Ordered list</p>
|
||||
<ol>
|
||||
<li><p>Table 1</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Header 1</th>
|
||||
<th>Header 2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Row 1 Column 1</td>
|
||||
<td>Row 1 Column 2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></li>
|
||||
<li><p>Table 2</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Header 1</th>
|
||||
<th>Header 2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Row 1 Column 1</td>
|
||||
<td>Row 1 Column 2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></li>
|
||||
<li><p>Table 3
|
||||
Lorem ipsum ...
|
||||
Lorem ipsum ...</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Header 1</th>
|
||||
<th>Header 2</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Row 1 Column 1</td>
|
||||
<td>Row 1 Column 2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></li>
|
||||
</ol>
|
||||
````````````````````````````````
|
||||
10
src/Markdig.Tests/TestCodeInline.cs
Normal file
10
src/Markdig.Tests/TestCodeInline.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Markdig.Tests;
|
||||
|
||||
public class TestCodeInline
|
||||
{
|
||||
[Test]
|
||||
public void UnpairedCodeInlineWithTrailingChars()
|
||||
{
|
||||
TestParser.TestSpec("*`\n\f", "<p>*`</p>");
|
||||
}
|
||||
}
|
||||
@@ -148,6 +148,9 @@ public class TestEmphasisExtended
|
||||
[TestCase("1Foo1", "<one-only>Foo</one-only>")]
|
||||
[TestCase("1121", "1<one-only>2</one-only>")]
|
||||
[TestCase("22322", "<two-only>3</two-only>")]
|
||||
[TestCase("2223222", "2<two-only>32</two-only>")]
|
||||
[TestCase("22223222", "22<two-only>32</two-only>")]
|
||||
[TestCase("22223223222", "22223<two-only>3</two-only>2")]
|
||||
[TestCase("2232", "2232")]
|
||||
[TestCase("333", "333")]
|
||||
[TestCase("3334333", "<three-only>4</three-only>")]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Markdig;
|
||||
using Markdig.Extensions.Tables;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Tests;
|
||||
|
||||
@@ -10,6 +12,9 @@ public sealed class TestPipeTable
|
||||
[TestCase("| S | T |\r\n|---|---|\t\r\n| G | H |")]
|
||||
[TestCase("| S | T |\r\n|---|---|\f\r\n| G | H |")]
|
||||
[TestCase("| S | \r\n|---|\r\n| G |\r\n\r\n| D | D |\r\n| ---| ---| \r\n| V | V |", 2)]
|
||||
[TestCase("a\r| S | T |\r|---|---|")]
|
||||
[TestCase("a\n| S | T |\r|---|---|")]
|
||||
[TestCase("a\r\n| S | T |\r|---|---|")]
|
||||
public void TestTableBug(string markdown, int tableCount = 1)
|
||||
{
|
||||
MarkdownDocument document =
|
||||
@@ -55,4 +60,147 @@ public sealed class TestPipeTable
|
||||
Assert.AreEqual(0, column.Width);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TableWithUnbalancedCodeSpanParsesWithoutDepthLimitError()
|
||||
{
|
||||
const string markdown = """
|
||||
| Count | A | B | C | D | E |
|
||||
|-------|---|---|---|---|---|
|
||||
| 0 | B | C | D | E | F |
|
||||
| 1 | B | `C | D | E | F |
|
||||
| 2 | B | `C | D | E | F |
|
||||
| 3 | B | C | D | E | F |
|
||||
| 4 | B | C | D | E | F |
|
||||
| 5 | B | C | D | E | F |
|
||||
| 6 | B | C | D | E | F |
|
||||
| 7 | B | C | D | E | F |
|
||||
| 8 | B | C | D | E | F |
|
||||
| 9 | B | C | D | E | F |
|
||||
| 10 | B | C | D | E | F |
|
||||
| 11 | B | C | D | E | F |
|
||||
| 12 | B | C | D | E | F |
|
||||
| 13 | B | C | D | E | F |
|
||||
| 14 | B | C | D | E | F |
|
||||
| 15 | B | C | D | E | F |
|
||||
| 16 | B | C | D | E | F |
|
||||
| 17 | B | C | D | E | F |
|
||||
| 18 | B | C | D | E | F |
|
||||
| 19 | B | C | D | E | F |
|
||||
""";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
MarkdownDocument document = null!;
|
||||
Assert.DoesNotThrow(() => document = Markdown.Parse(markdown, pipeline));
|
||||
|
||||
var tables = document.Descendants().OfType<Table>().ToArray();
|
||||
Assert.That(tables, Has.Length.EqualTo(1));
|
||||
|
||||
string html = string.Empty;
|
||||
Assert.DoesNotThrow(() => html = Markdown.ToHtml(markdown, pipeline));
|
||||
Assert.That(html, Does.Contain("<table"));
|
||||
Assert.That(html, Does.Contain("<td>`C</td>"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CodeInlineWithPipeDelimitersRemainsCodeInline()
|
||||
{
|
||||
const string markdown = "`|| hidden text ||`";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
var document = Markdown.Parse(markdown, pipeline);
|
||||
|
||||
var codeInline = document.Descendants().OfType<CodeInline>().SingleOrDefault();
|
||||
Assert.IsNotNull(codeInline);
|
||||
Assert.That(codeInline!.Content, Is.EqualTo("|| hidden text ||"));
|
||||
Assert.That(document.ToHtml(), Is.EqualTo("<p><code>|| hidden text ||</code></p>\n"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultiLineCodeInlineWithPipeDelimitersRendersAsCode()
|
||||
{
|
||||
string markdown =
|
||||
"""
|
||||
`
|
||||
|| hidden text ||
|
||||
`
|
||||
""".ReplaceLineEndings("\n");
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
Assert.That(html, Is.EqualTo("<p><code>|| hidden text ||</code></p>\n"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TableCellWithCodeInlineRendersCorrectly()
|
||||
{
|
||||
const string markdown =
|
||||
"""
|
||||
| Count | A | B | C | D | E |
|
||||
|-------|---|---|---|---|---|
|
||||
| 0 | B | C | D | E | F |
|
||||
| 1 | B | `Code block` | D | E | F |
|
||||
| 2 | B | C | D | E | F |
|
||||
""";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
Assert.That(html, Does.Contain("<td><code>Code block</code></td>"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CodeInlineWithIndentedContentPreservesWhitespace()
|
||||
{
|
||||
const string markdown = "`\n foo\n`";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
var document = Markdown.Parse(markdown, pipeline);
|
||||
var codeInline = document.Descendants().OfType<CodeInline>().Single();
|
||||
|
||||
Assert.That(codeInline.Content, Is.EqualTo("foo"));
|
||||
Assert.That(Markdown.ToHtml(markdown, pipeline), Is.EqualTo("<p><code>foo</code></p>\n"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TableWithIndentedPipeAfterCodeInlineParsesCorrectly()
|
||||
{
|
||||
var markdown =
|
||||
"""
|
||||
`
|
||||
|| hidden text ||
|
||||
`
|
||||
|
||||
| Count | Value |
|
||||
|-------|-------|
|
||||
| 0 | B |
|
||||
|
||||
""".ReplaceLineEndings("\n");
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
Assert.That(html, Does.Contain("<p><code>|| hidden text ||</code></p>"));
|
||||
Assert.That(html, Does.Contain("<table"));
|
||||
Assert.That(html, Does.Contain("<td>B</td>"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -834,6 +834,29 @@ literal ( 2, 2) 11-11
|
||||
", "pipetables");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGridTable()
|
||||
{
|
||||
Check("0\n\n+-+-+\n|A|B|\n+=+=+\n|C|D|\n+-+-+", @"
|
||||
paragraph ( 0, 0) 0-0
|
||||
literal ( 0, 0) 0-0
|
||||
table ( 2, 0) 3-31
|
||||
tablerow ( 3, 0) 9-13
|
||||
tablecell ( 3, 0) 9-11
|
||||
paragraph ( 3, 1) 10-10
|
||||
literal ( 3, 1) 10-10
|
||||
tablecell ( 3, 2) 11-13
|
||||
paragraph ( 3, 3) 12-12
|
||||
literal ( 3, 3) 12-12
|
||||
tablerow ( 5, 0) 21-25
|
||||
tablecell ( 5, 0) 21-23
|
||||
paragraph ( 5, 1) 22-22
|
||||
literal ( 5, 1) 22-22
|
||||
tablecell ( 5, 2) 23-25
|
||||
paragraph ( 5, 3) 24-24
|
||||
literal ( 5, 3) 24-24", "gridtables");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIndentedCode()
|
||||
{
|
||||
|
||||
@@ -73,8 +73,6 @@ public class AbbreviationParser : BlockParser
|
||||
|
||||
private void DocumentOnProcessInlinesBegin(InlineProcessor inlineProcessor, Inline? inline)
|
||||
{
|
||||
inlineProcessor.Document.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;
|
||||
|
||||
var abbreviations = inlineProcessor.Document.GetAbbreviations();
|
||||
// Should not happen, but another extension could decide to remove them, so...
|
||||
if (abbreviations is null)
|
||||
|
||||
@@ -101,7 +101,6 @@ public class AutoIdentifierExtension : IMarkdownExtension
|
||||
private void DocumentOnProcessInlinesBegin(InlineProcessor processor, Inline? inline)
|
||||
{
|
||||
var doc = processor.Document;
|
||||
doc.ProcessInlinesBegin -= _processInlinesBegin;
|
||||
var dictionary = (Dictionary<string, HeadingLinkReferenceDefinition>)doc.GetData(this)!;
|
||||
foreach (var keyPair in dictionary)
|
||||
{
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Parsers;
|
||||
|
||||
namespace Markdig.Extensions.AutoLinks;
|
||||
|
||||
public class AutoLinkOptions
|
||||
public class AutoLinkOptions : LinkOptions
|
||||
{
|
||||
public AutoLinkOptions()
|
||||
{
|
||||
@@ -13,11 +15,6 @@ public class AutoLinkOptions
|
||||
|
||||
public string ValidPreviousCharacters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the link open in a new window when clicked (false by default)
|
||||
/// </summary>
|
||||
public bool OpenInNewWindow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should a www link be prefixed with https:// instead of http:// (false by default)
|
||||
/// </summary>
|
||||
|
||||
@@ -134,9 +134,6 @@ public class FootnoteParser : BlockParser
|
||||
/// <param name="inline">The inline.</param>
|
||||
private void Document_ProcessInlinesEnd(InlineProcessor state, Inline? inline)
|
||||
{
|
||||
// Unregister
|
||||
state.Document.ProcessInlinesEnd -= Document_ProcessInlinesEnd;
|
||||
|
||||
var footnotes = (FootnoteGroup)state.Document.GetData(DocumentKey)!;
|
||||
// Remove the footnotes from the document and readd them at the end
|
||||
state.Document.Remove(footnotes);
|
||||
|
||||
@@ -109,6 +109,15 @@ public class GenericAttributesParser : InlineParser
|
||||
{
|
||||
isValid = true;
|
||||
line.SkipChar(); // skip }
|
||||
// skip line breaks
|
||||
if (line.CurrentChar == '\n')
|
||||
{
|
||||
line.SkipChar();
|
||||
}
|
||||
else if (line.CurrentChar == '\r' && line.PeekChar() == '\n')
|
||||
{
|
||||
line.Start += 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
|
||||
namespace Markdig.Extensions.JiraLinks;
|
||||
|
||||
/// <summary>
|
||||
/// Available options for replacing JIRA links
|
||||
/// </summary>
|
||||
public class JiraLinkOptions
|
||||
public class JiraLinkOptions : LinkOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The base Url (e.g. `https://mycompany.atlassian.net`)
|
||||
@@ -21,11 +22,6 @@ public class JiraLinkOptions
|
||||
/// </summary>
|
||||
public string BasePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the link open in a new window when clicked
|
||||
/// </summary>
|
||||
public bool OpenInNewWindow { get; set; }
|
||||
|
||||
public JiraLinkOptions(string baseUrl)
|
||||
{
|
||||
OpenInNewWindow = true; //default
|
||||
|
||||
@@ -204,8 +204,6 @@ public class SmartyPantsInlineParser : InlineParser, IPostInlineProcessor
|
||||
|
||||
private void BlockOnProcessInlinesEnd(InlineProcessor processor, Inline? inline)
|
||||
{
|
||||
processor.Block!.ProcessInlinesEnd -= BlockOnProcessInlinesEnd;
|
||||
|
||||
var pants = (ListSmartyPants) processor.ParserStates[Index];
|
||||
|
||||
var openers = new Stack<Opener>(4);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Syntax;
|
||||
using System.Linq;
|
||||
|
||||
namespace Markdig.Extensions.Tables;
|
||||
|
||||
@@ -60,7 +61,12 @@ public class GridTableParser : BlockParser
|
||||
}
|
||||
// Store the line (if we need later to build a ParagraphBlock because the GridTable was in fact invalid)
|
||||
tableState.AddLine(ref processor.Line);
|
||||
var table = new Table(this);
|
||||
var table = new Table(this)
|
||||
{
|
||||
Line = processor.LineIndex,
|
||||
Column = processor.Column,
|
||||
Span = { Start = lineStart }
|
||||
};
|
||||
table.SetData(typeof(GridTableState), tableState);
|
||||
|
||||
// Calculate the total width of all columns
|
||||
@@ -94,10 +100,12 @@ public class GridTableParser : BlockParser
|
||||
tableState.AddLine(ref processor.Line);
|
||||
if (processor.CurrentChar == '+')
|
||||
{
|
||||
gridTable.UpdateSpanEnd(processor.Line.End);
|
||||
return HandleNewRow(processor, tableState, gridTable);
|
||||
}
|
||||
if (processor.CurrentChar == '|')
|
||||
{
|
||||
gridTable.UpdateSpanEnd(processor.Line.End);
|
||||
return HandleContents(processor, tableState, gridTable);
|
||||
}
|
||||
TerminateCurrentRow(processor, tableState, gridTable, true);
|
||||
@@ -182,8 +190,18 @@ public class GridTableParser : BlockParser
|
||||
var columnSlice = columns[i];
|
||||
if (columnSlice.CurrentCell != null)
|
||||
{
|
||||
currentRow ??= new TableRow();
|
||||
|
||||
if (currentRow == null)
|
||||
{
|
||||
TableCell firstCell = columns.First(c => c.CurrentCell != null).CurrentCell!;
|
||||
TableCell lastCell = columns.Last(c => c.CurrentCell != null).CurrentCell!;
|
||||
|
||||
currentRow ??= new TableRow()
|
||||
{
|
||||
Span = new SourceSpan(firstCell.Span.Start, lastCell.Span.End),
|
||||
Line = firstCell.Line
|
||||
};
|
||||
}
|
||||
|
||||
// If this cell does not already belong to a row
|
||||
if (columnSlice.CurrentCell.Parent is null)
|
||||
{
|
||||
@@ -271,7 +289,10 @@ public class GridTableParser : BlockParser
|
||||
columnSlice.CurrentCell = new TableCell(this)
|
||||
{
|
||||
ColumnSpan = columnSlice.CurrentColumnSpan,
|
||||
ColumnIndex = i
|
||||
ColumnIndex = i,
|
||||
Column = columnSlice.Start,
|
||||
Line = processor.LineIndex,
|
||||
Span = new SourceSpan(line.Start + columnSlice.Start, line.Start + columnSlice.End)
|
||||
};
|
||||
|
||||
columnSlice.BlockProcessor ??= processor.CreateChild();
|
||||
@@ -281,7 +302,8 @@ public class GridTableParser : BlockParser
|
||||
}
|
||||
// Process the content of the cell
|
||||
columnSlice.BlockProcessor!.LineIndex = processor.LineIndex;
|
||||
columnSlice.BlockProcessor.ProcessLine(sliceForCell);
|
||||
|
||||
columnSlice.BlockProcessor.ProcessLinePart(sliceForCell, sliceForCell.Start - line.Start);
|
||||
}
|
||||
|
||||
// Go to next column
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Parsers.Inlines;
|
||||
@@ -60,13 +59,12 @@ public class PipeTableParser : InlineParser, IPostInlineProcessor
|
||||
|
||||
if (tableState is null)
|
||||
{
|
||||
|
||||
// A table could be preceded by an empty line or a line containing an inline
|
||||
// that has not been added to the stack, so we consider this as a valid
|
||||
// start for a table. Typically, with this, we can have an attributes {...}
|
||||
// starting on the first line of a pipe table, even if the first line
|
||||
// doesn't have a pipe
|
||||
if (processor.Inline != null && (localLineIndex > 0 || c == '\n' || c == '\r'))
|
||||
if (processor.Inline != null && (c == '\n' || c == '\r'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -75,6 +73,7 @@ public class PipeTableParser : InlineParser, IPostInlineProcessor
|
||||
{
|
||||
isFirstLineEmpty = true;
|
||||
}
|
||||
|
||||
// Else setup a table processor
|
||||
tableState = new TableState();
|
||||
processor.ParserStates[Index] = tableState;
|
||||
@@ -88,7 +87,6 @@ public class PipeTableParser : InlineParser, IPostInlineProcessor
|
||||
}
|
||||
tableState.LineHasPipe = false;
|
||||
lineBreakParser.Match(processor, ref slice);
|
||||
tableState.LineIndex++;
|
||||
if (!isFirstLineEmpty)
|
||||
{
|
||||
tableState.ColumnAndLineDelimiters.Add(processor.Inline!);
|
||||
@@ -104,13 +102,8 @@ public class PipeTableParser : InlineParser, IPostInlineProcessor
|
||||
Column = column,
|
||||
LocalLineIndex = localLineIndex
|
||||
};
|
||||
var deltaLine = localLineIndex - tableState.LineIndex;
|
||||
if (deltaLine > 0)
|
||||
{
|
||||
tableState.IsInvalidTable = true;
|
||||
}
|
||||
|
||||
tableState.LineHasPipe = true;
|
||||
tableState.LineIndex = localLineIndex;
|
||||
slice.SkipChar(); // Skip the `|` character
|
||||
|
||||
tableState.ColumnAndLineDelimiters.Add(processor.Inline);
|
||||
@@ -196,6 +189,16 @@ public class PipeTableParser : InlineParser, IPostInlineProcessor
|
||||
// Continue
|
||||
if (tableState is null || container is null || tableState.IsInvalidTable || !tableState.LineHasPipe ) //|| tableState.LineIndex != state.LocalLineIndex)
|
||||
{
|
||||
if (tableState is not null)
|
||||
{
|
||||
foreach (var inline in tableState.ColumnAndLineDelimiters)
|
||||
{
|
||||
if (inline is PipeTableDelimiterInline pipeDelimiter)
|
||||
{
|
||||
pipeDelimiter.ReplaceByLiteral();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -218,7 +221,6 @@ public class PipeTableParser : InlineParser, IPostInlineProcessor
|
||||
attributes.CopyTo(table.GetAttributes());
|
||||
}
|
||||
|
||||
state.BlockNew = table;
|
||||
var cells = tableState.Cells;
|
||||
cells.Clear();
|
||||
|
||||
@@ -477,6 +479,35 @@ public class PipeTableParser : InlineParser, IPostInlineProcessor
|
||||
table.NormalizeUsingMaxWidth();
|
||||
}
|
||||
|
||||
if (state.Block is ParagraphBlock { Inline.FirstChild: not null } leadingParagraph)
|
||||
{
|
||||
// The table was preceded by a non-empty paragraph, e.g.
|
||||
// ```md
|
||||
// Some text
|
||||
// | Header |
|
||||
// ```
|
||||
//
|
||||
// Keep the paragraph as-is and insert the table after it.
|
||||
// Since we've already processed all the inlines in this table block,
|
||||
// we can't insert it while the parent is still being processed.
|
||||
// Hook up a callback that inserts the table after we're done with ProcessInlines for the parent block.
|
||||
|
||||
// We've processed inlines in the table, but not the leading paragraph itself yet.
|
||||
state.PostProcessInlines(0, leadingParagraph.Inline, null, isFinalProcessing: true);
|
||||
|
||||
ContainerBlock parent = leadingParagraph.Parent!;
|
||||
|
||||
parent.ProcessInlinesEnd += (_, _) =>
|
||||
{
|
||||
parent.Insert(parent.IndexOf(leadingParagraph) + 1, table);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing interesting in the existing block, just replace it.
|
||||
state.BlockNew = table;
|
||||
}
|
||||
|
||||
// We don't want to continue procesing delimiters, as we are already processing them here
|
||||
return false;
|
||||
}
|
||||
@@ -672,8 +703,6 @@ public class PipeTableParser : InlineParser, IPostInlineProcessor
|
||||
|
||||
public bool LineHasPipe { get; set; }
|
||||
|
||||
public int LineIndex { get; set; }
|
||||
|
||||
public List<Inline> ColumnAndLineDelimiters { get; } = [];
|
||||
|
||||
public List<TableCell> Cells { get; } = [];
|
||||
|
||||
@@ -47,6 +47,7 @@ public static class TableHelper
|
||||
/// <param name="slice">The text slice.</param>
|
||||
/// <param name="delimiterChar">The delimiter character (either `-` or `=`). If `\0`, it will detect the character (either `-` or `=`)</param>
|
||||
/// <param name="align">The alignment of the column.</param>
|
||||
/// <param name="delimiterCount">The number of times <paramref name="delimiterChar"/> appeared in the column header.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if parsing was successful
|
||||
/// </returns>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
@@ -430,7 +430,7 @@ public static class HtmlHelper
|
||||
|
||||
const string EndOfComment = "-->";
|
||||
|
||||
int endOfComment = slice.IndexOf(EndOfComment, StringComparison.Ordinal);
|
||||
int endOfComment = slice.IndexOf(EndOfComment.AsSpan(), StringComparison.Ordinal);
|
||||
if (endOfComment < 0)
|
||||
{
|
||||
return false;
|
||||
@@ -474,7 +474,7 @@ public static class HtmlHelper
|
||||
public static string Unescape(string? text, bool removeBackSlash = true)
|
||||
{
|
||||
// Credits: code from CommonMark.NET
|
||||
// Copyright (c) 2014, Kārlis Gaņģis All rights reserved.
|
||||
// Copyright (c) 2014, Kārlis Gaņģis All rights reserved.
|
||||
// See license for details: https://github.com/Knagis/CommonMark.NET/blob/master/LICENSE.md
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
@@ -553,7 +553,7 @@ public static class HtmlHelper
|
||||
public static int ScanEntity<T>(T slice, out int numericEntity, out int namedEntityStart, out int namedEntityLength) where T : ICharIterator
|
||||
{
|
||||
// Credits: code from CommonMark.NET
|
||||
// Copyright (c) 2014, Kārlis Gaņģis All rights reserved.
|
||||
// Copyright (c) 2014, Kārlis Gaņģis All rights reserved.
|
||||
// See license for details: https://github.com/Knagis/CommonMark.NET/blob/master/LICENSE.md
|
||||
|
||||
numericEntity = 0;
|
||||
@@ -568,7 +568,7 @@ public static class HtmlHelper
|
||||
var start = slice.Start;
|
||||
char c = slice.NextChar();
|
||||
int counter = 0;
|
||||
|
||||
|
||||
if (c == '#')
|
||||
{
|
||||
c = slice.PeekChar();
|
||||
|
||||
@@ -22,7 +22,7 @@ internal static class UnicodeUtility
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void GetUtf16SurrogatesFromSupplementaryPlaneScalar(uint value, out char highSurrogateCodePoint, out char lowSurrogateCodePoint)
|
||||
{
|
||||
Debug.Assert(IsValidUnicodeScalar(value) && IsBmpCodePoint(value));
|
||||
Debug.Assert(IsValidUnicodeScalar(value) && !IsBmpCodePoint(value));
|
||||
|
||||
highSurrogateCodePoint = (char)((value + ((0xD800u - 0x40u) << 10)) >> 10);
|
||||
lowSurrogateCodePoint = (char)((value & 0x3FFu) + 0xDC00u);
|
||||
|
||||
@@ -538,7 +538,7 @@ public static class MarkdownExtensions
|
||||
var inlineParser = pipeline.InlineParsers.Find<AutolinkInlineParser>();
|
||||
if (inlineParser != null)
|
||||
{
|
||||
inlineParser.EnableHtmlParsing = false;
|
||||
inlineParser.Options.EnableHtmlParsing = false;
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
@@ -493,8 +493,34 @@ public class BlockProcessor
|
||||
|
||||
ContinueProcessingLine = true;
|
||||
|
||||
ResetLine(newLine);
|
||||
ResetLine(newLine, 0);
|
||||
|
||||
Process();
|
||||
|
||||
LineIndex++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes part of a line.
|
||||
/// </summary>
|
||||
/// <param name="line">The line.</param>
|
||||
/// <param name="column">The column.</param>
|
||||
public void ProcessLinePart(StringSlice line, int column)
|
||||
{
|
||||
CurrentLineStartPosition = line.Start - column;
|
||||
|
||||
ContinueProcessingLine = true;
|
||||
|
||||
ResetLine(line, column);
|
||||
|
||||
Process();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process current string slice.
|
||||
/// </summary>
|
||||
private void Process()
|
||||
{
|
||||
TryContinueBlocks();
|
||||
|
||||
// If the line was not entirely processed by pending blocks, try to process it with any new block
|
||||
@@ -502,8 +528,6 @@ public class BlockProcessor
|
||||
|
||||
// Close blocks that are no longer opened
|
||||
CloseAll(false);
|
||||
|
||||
LineIndex++;
|
||||
}
|
||||
|
||||
internal bool IsOpen(Block block)
|
||||
@@ -956,18 +980,17 @@ public class BlockProcessor
|
||||
ContinueProcessingLine = !result.IsDiscard();
|
||||
}
|
||||
|
||||
private void ResetLine(StringSlice newLine)
|
||||
private void ResetLine(StringSlice newLine, int column)
|
||||
{
|
||||
Line = newLine;
|
||||
Column = 0;
|
||||
Column = column;
|
||||
ColumnBeforeIndent = 0;
|
||||
StartBeforeIndent = Start;
|
||||
originalLineStart = newLine.Start;
|
||||
originalLineStart = newLine.Start - column;
|
||||
TriviaStart = newLine.Start;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[MemberNotNull(nameof(Document), nameof(Parsers))]
|
||||
internal void Setup(MarkdownDocument document, BlockParserList parsers, MarkdownParserContext? context, bool trackTrivia)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
@@ -14,19 +15,21 @@ namespace Markdig.Parsers.Inlines;
|
||||
/// <seealso cref="InlineParser" />
|
||||
public class AutolinkInlineParser : InlineParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AutolinkInlineParser"/> class.
|
||||
/// </summary>
|
||||
public AutolinkInlineParser()
|
||||
public AutolinkInlineParser() : this(new AutolinkOptions())
|
||||
{
|
||||
OpeningCharacters = ['<'];
|
||||
EnableHtmlParsing = true;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to enable HTML parsing. Default is <c>true</c>
|
||||
/// Initializes a new instance of the <see cref="AutolinkInlineParser"/> class.
|
||||
/// </summary>
|
||||
public bool EnableHtmlParsing { get; set; }
|
||||
public AutolinkInlineParser(AutolinkOptions options)
|
||||
{
|
||||
Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
OpeningCharacters = ['<'];
|
||||
}
|
||||
|
||||
public readonly AutolinkOptions Options;
|
||||
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
@@ -42,8 +45,12 @@ public class AutolinkInlineParser : InlineParser
|
||||
Line = line,
|
||||
Column = column
|
||||
};
|
||||
if (Options.OpenInNewWindow)
|
||||
{
|
||||
processor.Inline.GetAttributes().AddPropertyIfNotExist("target", "_blank");
|
||||
}
|
||||
}
|
||||
else if (EnableHtmlParsing)
|
||||
else if (Options.EnableHtmlParsing)
|
||||
{
|
||||
slice = saved;
|
||||
if (!HtmlHelper.TryParseHtmlTag(ref slice, out string? htmlTag))
|
||||
@@ -57,6 +64,10 @@ public class AutolinkInlineParser : InlineParser
|
||||
Line = line,
|
||||
Column = column
|
||||
};
|
||||
if (Options.OpenInNewWindow)
|
||||
{
|
||||
processor.Inline.GetAttributes().AddPropertyIfNotExist("target", "_blank");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
13
src/Markdig/Parsers/Inlines/AutolinkOptions.cs
Normal file
13
src/Markdig/Parsers/Inlines/AutolinkOptions.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
namespace Markdig.Parsers.Inlines;
|
||||
|
||||
public class AutolinkOptions : LinkOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to enable HTML parsing. Default is <c>true</c>
|
||||
/// </summary>
|
||||
public bool EnableHtmlParsing { get; set; } = true;
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
using Markdig.Extensions.Tables;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
@@ -35,6 +36,7 @@ public class CodeInlineParser : InlineParser
|
||||
Debug.Assert(match is not ('\r' or '\n'));
|
||||
|
||||
// Match the opened sticks
|
||||
int openingStart = slice.Start;
|
||||
int openSticks = slice.CountAndSkipChar(match);
|
||||
|
||||
// A backtick string is a string of one or more backtick characters (`) that is neither preceded nor followed by a backtick.
|
||||
@@ -75,8 +77,21 @@ public class CodeInlineParser : InlineParser
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (closeSticks == 0)
|
||||
|
||||
if (closeSticks == 0)
|
||||
{
|
||||
if (span.TrimStart(['\r', '\n']).StartsWith('|'))
|
||||
{
|
||||
// We saw the start of a code inline, but the close sticks are not present on the same line.
|
||||
// If the next line starts with a pipe character, this is likely an incomplete CodeInline within a table.
|
||||
// Treat it as regular text to avoid breaking the overall table shape.
|
||||
if (processor.Inline != null && processor.Inline.ContainsParentOfType<PipeTableDelimiterInline>())
|
||||
{
|
||||
slice.Start = openingStart;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
containsNewLines = true;
|
||||
span = span.Slice(1);
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Markdig.Parsers.Inlines;
|
||||
|
||||
/// <summary>
|
||||
/// Descriptor for an emphasis.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("Emphasis Char={Character}, Min={MinimumCount}, Max={MaximumCount}, EnableWithinWord={EnableWithinWord}")]
|
||||
public sealed class EmphasisDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -233,9 +233,9 @@ public class EmphasisInlineParser : InlineParser, IPostInlineProcessor
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((closeDelimiter.Type & DelimiterType.Close) != 0 && closeDelimiter.DelimiterCount >= emphasisDesc.MinimumCount)
|
||||
if ((closeDelimiter.Type & DelimiterType.Close) != 0)
|
||||
{
|
||||
while (true)
|
||||
while (closeDelimiter.DelimiterCount >= emphasisDesc.MinimumCount)
|
||||
{
|
||||
// Now, look back in the stack (staying above stack_bottom and the openers_bottom for this delimiter type)
|
||||
// for the first matching potential opener (“matching” means same delimiter).
|
||||
@@ -245,8 +245,7 @@ public class EmphasisInlineParser : InlineParser, IPostInlineProcessor
|
||||
{
|
||||
var previousOpenDelimiter = delimiters[j];
|
||||
|
||||
var isOddMatch = ((closeDelimiter.Type & DelimiterType.Open) != 0 ||
|
||||
(previousOpenDelimiter.Type & DelimiterType.Close) != 0) &&
|
||||
var isOddMatch = ((closeDelimiter.Type & DelimiterType.Open) != 0 || (previousOpenDelimiter.Type & DelimiterType.Close) != 0) &&
|
||||
previousOpenDelimiter.DelimiterCount != closeDelimiter.DelimiterCount &&
|
||||
(previousOpenDelimiter.DelimiterCount + closeDelimiter.DelimiterCount) % 3 == 0 &&
|
||||
(previousOpenDelimiter.DelimiterCount % 3 != 0 || closeDelimiter.DelimiterCount % 3 != 0);
|
||||
@@ -357,7 +356,8 @@ public class EmphasisInlineParser : InlineParser, IPostInlineProcessor
|
||||
}
|
||||
|
||||
// The current delimiters are matching
|
||||
if (openDelimiter.DelimiterCount >= emphasisDesc.MinimumCount)
|
||||
if (openDelimiter.DelimiterCount >= emphasisDesc.MinimumCount &&
|
||||
closeDelimiter.DelimiterCount >= emphasisDesc.MinimumCount)
|
||||
{
|
||||
goto process_delims;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
@@ -17,11 +18,22 @@ public class LinkInlineParser : InlineParser
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinkInlineParser"/> class.
|
||||
/// </summary>
|
||||
public LinkInlineParser()
|
||||
public LinkInlineParser() : this(new LinkOptions())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinkInlineParser"/> class.
|
||||
/// </summary>
|
||||
public LinkInlineParser(LinkOptions options)
|
||||
{
|
||||
Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
OpeningCharacters = ['[', ']', '!'];
|
||||
}
|
||||
|
||||
public readonly LinkOptions Options;
|
||||
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
// The following methods are inspired by the "An algorithm for parsing nested emphasis and links"
|
||||
@@ -169,6 +181,11 @@ public class LinkInlineParser : InlineParser
|
||||
linkInline.LocalLabel = localLabel;
|
||||
}
|
||||
|
||||
if (Options.OpenInNewWindow)
|
||||
{
|
||||
linkInline.GetAttributes().AddPropertyIfNotExist("target", "_blank");
|
||||
}
|
||||
|
||||
link = linkInline;
|
||||
}
|
||||
|
||||
|
||||
19
src/Markdig/Parsers/LinkOptions.cs
Normal file
19
src/Markdig/Parsers/LinkOptions.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Markdig.Parsers;
|
||||
|
||||
public class LinkOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Should the link open in a new window when clicked (false by default)
|
||||
/// </summary>
|
||||
public bool OpenInNewWindow { get; set; }
|
||||
}
|
||||
@@ -145,6 +145,7 @@ public class ListBlockParser : BlockParser
|
||||
if (list.CountBlankLinesReset == 1 && listItem.ColumnWidth < 0)
|
||||
{
|
||||
state.Close(listItem);
|
||||
list.CountBlankLinesReset = 0;
|
||||
|
||||
// Leave the list open
|
||||
list.IsOpen = true;
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
#if NET462 || NETSTANDARD2_0
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace System;
|
||||
|
||||
internal static class SpanExtensions
|
||||
{
|
||||
#if NET462 || NETSTANDARD2_0
|
||||
public static bool StartsWith(this ReadOnlySpan<char> span, string prefix, StringComparison comparisonType)
|
||||
{
|
||||
Debug.Assert(comparisonType is StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase);
|
||||
@@ -18,6 +18,15 @@ internal static class SpanExtensions
|
||||
span.Length >= prefix.Length &&
|
||||
span.Slice(0, prefix.Length).Equals(prefix.AsSpan(), comparisonType);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !NET9_0_OR_GREATER
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool StartsWith(this ReadOnlySpan<char> span, char c)
|
||||
{
|
||||
return span.Length > 0 && span[0] == c;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -85,12 +85,12 @@ public abstract class RendererBase : IMarkdownRenderer
|
||||
public bool IsLastInContainer { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when before writing an object.
|
||||
/// Occurs before writing an object.
|
||||
/// </summary>
|
||||
public event Action<IMarkdownRenderer, MarkdownObject>? ObjectWriteBefore;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when after writing an object.
|
||||
/// Occurs after writing an object.
|
||||
/// </summary>
|
||||
public event Action<IMarkdownRenderer, MarkdownObject>? ObjectWriteAfter;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Helpers;
|
||||
@@ -28,7 +28,15 @@ public class ListRenderer : RoundtripObjectRenderer<ListBlock>
|
||||
var bullet = listItem.SourceBullet.ToString();
|
||||
var delimiter = listBlock.OrderedDelimiter;
|
||||
renderer.PushIndent(new string[] { $"{bws}{bullet}{delimiter}" });
|
||||
renderer.WriteChildren(listItem);
|
||||
if (listItem.Count == 0)
|
||||
{
|
||||
renderer.Write(""); // trigger writing of indent
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.WriteChildren(listItem);
|
||||
}
|
||||
renderer.PopIndent();
|
||||
renderer.RenderLinesAfter(listItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,12 +92,20 @@ public abstract class Block : MarkdownObject, IBlock
|
||||
/// <summary>
|
||||
/// Occurs when the process of inlines begin.
|
||||
/// </summary>
|
||||
public event ProcessInlineDelegate? ProcessInlinesBegin;
|
||||
public event ProcessInlineDelegate? ProcessInlinesBegin
|
||||
{
|
||||
add => Trivia.ProcessInlinesBegin += value;
|
||||
remove => _trivia?.ProcessInlinesBegin -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when the process of inlines ends for this instance.
|
||||
/// </summary>
|
||||
public event ProcessInlineDelegate? ProcessInlinesEnd;
|
||||
public event ProcessInlineDelegate? ProcessInlinesEnd
|
||||
{
|
||||
add => Trivia.ProcessInlinesEnd += value;
|
||||
remove => _trivia?.ProcessInlinesEnd -= value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the process of inlines begin.
|
||||
@@ -105,7 +113,13 @@ public abstract class Block : MarkdownObject, IBlock
|
||||
/// <param name="state">The inline parser state.</param>
|
||||
internal void OnProcessInlinesBegin(InlineProcessor state)
|
||||
{
|
||||
ProcessInlinesBegin?.Invoke(state, null);
|
||||
if (_trivia is BlockTriviaProperties trivia)
|
||||
{
|
||||
trivia.ProcessInlinesBegin?.Invoke(state, null);
|
||||
|
||||
// Not exactly standard 'event' behavior, but these aren't expected to be called more than once.
|
||||
_trivia.ProcessInlinesBegin = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -114,7 +128,13 @@ public abstract class Block : MarkdownObject, IBlock
|
||||
/// <param name="state">The inline parser state.</param>
|
||||
internal void OnProcessInlinesEnd(InlineProcessor state)
|
||||
{
|
||||
ProcessInlinesEnd?.Invoke(state, null);
|
||||
if (_trivia is BlockTriviaProperties trivia)
|
||||
{
|
||||
trivia.ProcessInlinesEnd?.Invoke(state, null);
|
||||
|
||||
// Not exactly standard 'event' behavior, but these aren't expected to be called more than once.
|
||||
_trivia.ProcessInlinesEnd = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateSpanEnd(int spanEnd)
|
||||
@@ -156,6 +176,11 @@ public abstract class Block : MarkdownObject, IBlock
|
||||
// Used by derived types to store their own TriviaProperties
|
||||
public object? DerivedTriviaSlot;
|
||||
|
||||
// These callbacks are set on a tiny subset of blocks (usually only the main MarkdownDocument),
|
||||
// so we store them in a lazily-allocated container to save memory for the majority of blocks.
|
||||
public ProcessInlineDelegate? ProcessInlinesBegin;
|
||||
public ProcessInlineDelegate? ProcessInlinesEnd;
|
||||
|
||||
public StringSlice TriviaBefore;
|
||||
public StringSlice TriviaAfter;
|
||||
public List<StringSlice>? LinesBefore;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<TargetFrameworks>net6.0;net8.0;net9.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
<NoWarn>$(NoWarn);NETSDK1138</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[msbuild]
|
||||
project = ["markdig.sln", "./Markdig.Signed/Markdig.Signed.csproj"]
|
||||
project = ["markdig.slnx", "./Markdig.Signed/Markdig.Signed.csproj"]
|
||||
build_debug = true
|
||||
[github]
|
||||
user = "xoofx"
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32112.339
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{061866E2-005C-4D13-A338-EA464BBEC60F}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\.editorconfig = ..\.editorconfig
|
||||
..\.gitattributes = ..\.gitattributes
|
||||
..\.gitignore = ..\.gitignore
|
||||
..\changelog.md = ..\changelog.md
|
||||
..\.github\workflows\ci.yml = ..\.github\workflows\ci.yml
|
||||
global.json = global.json
|
||||
..\license.txt = ..\license.txt
|
||||
..\readme.md = ..\readme.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig", "Markdig\Markdig.csproj", "{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig.Tests", "Markdig.Tests\Markdig.Tests.csproj", "{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48} = {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig.Benchmarks", "Markdig.Benchmarks\Markdig.Benchmarks.csproj", "{6A19F040-BC7C-4283-873A-177B5324F1ED}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48} = {8A58A7E2-627C-4F41-933F-5AC92ADFAB48}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Markdig.WebApp", "Markdig.WebApp\Markdig.WebApp.csproj", "{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnicodeNormDApp", "UnicodeNormDApp\UnicodeNormDApp.csproj", "{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "mdtoc", "mdtoc\mdtoc.csproj", "{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpecFileGen", "SpecFileGen\SpecFileGen.csproj", "{DB6E2ED5-7884-4E97-84AF-35E2480CF685}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A58A7E2-627C-4F41-933F-5AC92ADFAB48}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A0C5CB5F-5568-40AB-B945-D6D2664D51B0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6A19F040-BC7C-4283-873A-177B5324F1ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6A19F040-BC7C-4283-873A-177B5324F1ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6A19F040-BC7C-4283-873A-177B5324F1ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6A19F040-BC7C-4283-873A-177B5324F1ED}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3CAD9801-9976-46BE-BACA-F6D0D21FDC00}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{33FFC0B9-0187-44F9-9424-BB5AF5B4FB84}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E3CDFF0F-5BFC-42E9-BDBA-2797651900A2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DB6E2ED5-7884-4E97-84AF-35E2480CF685}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DB6E2ED5-7884-4E97-84AF-35E2480CF685}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DB6E2ED5-7884-4E97-84AF-35E2480CF685}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DB6E2ED5-7884-4E97-84AF-35E2480CF685}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D068F7B6-6ACC-456C-A2E1-10EA746D956D}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,12 +0,0 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">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.</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue"><Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy></s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECodeCleanup_002EFileHeader_002EFileHeaderSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/NUnitProvider/SetCurrentDirectoryTo/@EntryValue">TestFolder</s:String>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Autolink/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Inlines/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Markdig/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
26
src/markdig.slnx
Normal file
26
src/markdig.slnx
Normal file
@@ -0,0 +1,26 @@
|
||||
<Solution>
|
||||
<Folder Name="/Build/">
|
||||
<File Path="../.editorconfig" />
|
||||
<File Path="../.gitattributes" />
|
||||
<File Path="../.github/workflows/ci.yml" />
|
||||
<File Path="../.gitignore" />
|
||||
<File Path="../changelog.md" />
|
||||
<File Path="../license.txt" />
|
||||
<File Path="../readme.md" />
|
||||
<File Path="global.json" />
|
||||
</Folder>
|
||||
<Project Path="Markdig.Benchmarks/Markdig.Benchmarks.csproj">
|
||||
<BuildDependency Project="Markdig/Markdig.csproj" />
|
||||
</Project>
|
||||
<Project Path="Markdig.Fuzzing/Markdig.Fuzzing.csproj">
|
||||
<BuildDependency Project="Markdig/Markdig.csproj" />
|
||||
</Project>
|
||||
<Project Path="Markdig.Tests/Markdig.Tests.csproj">
|
||||
<BuildDependency Project="Markdig/Markdig.csproj" />
|
||||
</Project>
|
||||
<Project Path="Markdig.WebApp/Markdig.WebApp.csproj" />
|
||||
<Project Path="Markdig/Markdig.csproj" />
|
||||
<Project Path="mdtoc/mdtoc.csproj" />
|
||||
<Project Path="SpecFileGen/SpecFileGen.csproj" />
|
||||
<Project Path="UnicodeNormDApp/UnicodeNormDApp.csproj" />
|
||||
</Solution>
|
||||
Reference in New Issue
Block a user