mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-04 05:44:50 +00:00
Compare commits
115 Commits
0.27.0
...
example-lu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62907f9314 | ||
|
|
5f80d86265 | ||
|
|
b85cc0daf5 | ||
|
|
76073e81c0 | ||
|
|
5b6621d729 | ||
|
|
9723eda455 | ||
|
|
7228ad5072 | ||
|
|
96f55d0aa6 | ||
|
|
2d69ac4499 | ||
|
|
5e91b9b763 | ||
|
|
e6255de62b | ||
|
|
c7b8772669 | ||
|
|
89a10ee76b | ||
|
|
7676079b4e | ||
|
|
495abab743 | ||
|
|
210b39e8fb | ||
|
|
f09d030fd3 | ||
|
|
f2ca6be7a6 | ||
|
|
94e07d11ce | ||
|
|
263041e899 | ||
|
|
e8f9274b64 | ||
|
|
b32e71aaeb | ||
|
|
f991b2123b | ||
|
|
a4a1a177bc | ||
|
|
59d59694f4 | ||
|
|
caf3c722e1 | ||
|
|
47c64d8815 | ||
|
|
2502fab340 | ||
|
|
17b5500b03 | ||
|
|
b754aef6b0 | ||
|
|
04843a08d2 | ||
|
|
fcc73691b6 | ||
|
|
cb8dc99d96 | ||
|
|
6f45ac0885 | ||
|
|
891e2fca78 | ||
|
|
925d4f9227 | ||
|
|
f0c200fc28 | ||
|
|
70184179b7 | ||
|
|
d69b989810 | ||
|
|
da756f4efe | ||
|
|
e192831db0 | ||
|
|
be3c93a9b0 | ||
|
|
6466f01a80 | ||
|
|
cb26f30f7b | ||
|
|
147c3f059a | ||
|
|
3201699053 | ||
|
|
e86d1ffce5 | ||
|
|
48c979dc74 | ||
|
|
3ae0c8b369 | ||
|
|
ee732e5a42 | ||
|
|
76e25833ad | ||
|
|
53dff53260 | ||
|
|
b2eeaf7185 | ||
|
|
47a22bc5e8 | ||
|
|
89e4c29f9f | ||
|
|
a946c6d0b4 | ||
|
|
e2770d8c11 | ||
|
|
6eacf8a170 | ||
|
|
e11a2630b8 | ||
|
|
4169e538af | ||
|
|
ccf455d316 | ||
|
|
8beb096814 | ||
|
|
6a35ec45b9 | ||
|
|
ed83943ba5 | ||
|
|
9adf60116b | ||
|
|
31904f6c53 | ||
|
|
3f3b3c46b6 | ||
|
|
f3d6c2775b | ||
|
|
bb6ace15b7 | ||
|
|
202ac1e4f9 | ||
|
|
2604239764 | ||
|
|
cc04208b95 | ||
|
|
14ab45cf8f | ||
|
|
e36d4564f1 | ||
|
|
358a5f09ef | ||
|
|
25db6cb414 | ||
|
|
315ffd42ab | ||
|
|
2675b4dd1e | ||
|
|
58d7fae12d | ||
|
|
e16ed79dcd | ||
|
|
9ef5171369 | ||
|
|
0cfe6d7da4 | ||
|
|
fe65c1b187 | ||
|
|
92385ee19a | ||
|
|
9f651feac0 | ||
|
|
d42b297128 | ||
|
|
b7d02cadbb | ||
|
|
6f75b5156c | ||
|
|
61452c91e9 | ||
|
|
b697a03c2b | ||
|
|
9f734ba3c9 | ||
|
|
88cdbf3a17 | ||
|
|
fb9561cf89 | ||
|
|
9145f47f89 | ||
|
|
1862b37bbd | ||
|
|
983187eace | ||
|
|
94581d9655 | ||
|
|
6aebd68413 | ||
|
|
a3f21b7639 | ||
|
|
653c9d7f16 | ||
|
|
9bbf6855e1 | ||
|
|
28f5503cfe | ||
|
|
ba990c739e | ||
|
|
0c45e19723 | ||
|
|
73b8bbe1c7 | ||
|
|
996b04cadb | ||
|
|
e7d7cbe010 | ||
|
|
ff89b48c33 | ||
|
|
58de5dbcea | ||
|
|
fbd7d83cda | ||
|
|
13a9c066f7 | ||
|
|
b7be61a914 | ||
|
|
c212f9efa8 | ||
|
|
e0961d7e86 | ||
|
|
c67a802e69 |
48
.github/workflows/ci.yml
vendored
48
.github/workflows/ci.yml
vendored
@@ -1,8 +1,5 @@
|
||||
name: ci
|
||||
|
||||
env:
|
||||
PROJECT_NAME: Markdig
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
@@ -19,46 +16,17 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install .NET 6.0
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '6.0.x'
|
||||
|
||||
- name: Build (Release)
|
||||
run: dotnet build src -c Release
|
||||
|
||||
- name: SpecFileGen
|
||||
run: dotnet src/SpecFileGen/bin/Release/net6.0/SpecFileGen.dll
|
||||
|
||||
- name: Test (Release)
|
||||
run: dotnet test src -c Release
|
||||
|
||||
- name: Build & Test (Debug)
|
||||
run: dotnet test src -c Debug
|
||||
|
||||
- name: Coverlet
|
||||
run: dotnet test src -c Release -f net6.0 /p:Include=\"[${{env.PROJECT_NAME}}]*\" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
|
||||
|
||||
- name: Coveralls Upload
|
||||
uses: coverallsapp/github-action@1.1.3
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path-to-lcov: src/${{env.PROJECT_NAME}}.Tests/coverage.info
|
||||
|
||||
- name: Pack
|
||||
run: dotnet pack src -c Release
|
||||
|
||||
- name: Pack Signed
|
||||
run: dotnet pack -c Release src/${{env.PROJECT_NAME}}.Signed/${{env.PROJECT_NAME}}.Signed.csproj
|
||||
|
||||
- name: Publish
|
||||
if: github.event_name == 'push'
|
||||
run: |
|
||||
if ( "${{github.ref}}" -match "^refs/tags/[0-9]+\.[0-9]+\.[0-9]+$" ) {
|
||||
dotnet nuget push src\${{env.PROJECT_NAME}}\bin\Release\*.nupkg -s nuget.org -k ${{secrets.NUGET_TOKEN}}
|
||||
dotnet nuget push src\${{env.PROJECT_NAME}}.Signed\bin\Release\*.nupkg -s nuget.org -k ${{secrets.NUGET_TOKEN}}
|
||||
} else {
|
||||
echo "publish is only enabled by tagging with a release tag"
|
||||
}
|
||||
- name: Build, Test, Pack, Publish
|
||||
shell: bash
|
||||
run: |
|
||||
dotnet tool install -g dotnet-releaser
|
||||
dotnet-releaser run --nuget-token "${{secrets.NUGET_TOKEN}}" --github-token "${{secrets.GITHUB_TOKEN}}" src/dotnet-releaser.toml
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -239,3 +239,9 @@ _Pvt_Extensions
|
||||
**/.idea/**/*.iml
|
||||
**/.idea/**/contentModel.xml
|
||||
**/.idea/**/modules.xml
|
||||
|
||||
# Remove artifacts produced by dotnet-releaser
|
||||
artifacts-dotnet-releaser/
|
||||
|
||||
# Remove .lunet temp folder
|
||||
.lunet/build
|
||||
134
doc/parsing-ast.md
Normal file
134
doc/parsing-ast.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# The Abstract Syntax Tree
|
||||
|
||||
If successful, the `Markdown.Parse(...)` method returns the abstract syntax tree (AST) of the source text.
|
||||
|
||||
This will be an object of the `MarkdownDocument` type, which is in turn derived from a more general block container and is part of a larger taxonomy of classes which represent different semantic constructs of a markdown syntax tree.
|
||||
|
||||
This document will discuss the different types of elements within the Markdig representation of the AST.
|
||||
|
||||
## Structure of the AST
|
||||
|
||||
Within Markdig, there are two general types of node in the markdown syntax tree: `Block`, and `Inline`. Block nodes may contain inline nodes, but the reverse is not true. Blocks may contain other blocks, and inlines may contain other inlines.
|
||||
|
||||
The root of the AST is the `MarkdownDocument` which is itself derived from a container block but also contains information on the line count and starting positions within the document. Nodes in the AST have links both to parent and children, allowing the edges in the tree to be traversed efficiently in either direction.
|
||||
|
||||
Different semantic constructs are represented by types derived from the `Block` and `Inline` types, which are both `abstract` themselves. These elements are produced by `BlockParser` and `InlineParser` derived types, respectively, and so new constructs can be added with the implementation of a new block or inline parser and a new block or inline type, as well as an extension to register it in the pipeline. For more information on extending Markdig this way refer to the [Extensions/Parsers](parsing-extensions.md) document.
|
||||
|
||||
The AST is assembled by the static method `Markdown.Parse(...)` using the collections of block and inline parsers contained in the `MarkdownPipeline`. For more detailed information refer to the [Markdig Parsing Overview](parsing-overview.md) document.
|
||||
|
||||
### Quick Examples: Descendants API
|
||||
|
||||
The easiest way to traverse the abstract syntax tree is with a group of extension methods that have the name `Descendants`. Several different overloads exist to allow it to search for both `Block` and `Inline` elements, starting from any node in the tree.
|
||||
|
||||
The `Descendants` methods return `IEnumerable<MarkdownObject>` or `IEnumerable<T>` as their results. Internally they are using `yield return` to perform edge traversals lazily.
|
||||
|
||||
#### Depth-First Like Traversal of All Elements
|
||||
|
||||
```csharp
|
||||
MarkdownDocument result = Markdown.Parse(sourceText, pipeline);
|
||||
|
||||
// Iterate through all MarkdownObjects in a depth-first order
|
||||
foreach (var item in result.Descendants())
|
||||
{
|
||||
Console.WriteLine(item.GetType());
|
||||
|
||||
// You can use pattern matching to isolate elements of certain type,
|
||||
// otherwise you can use the filtering mechanism demonstrated in the
|
||||
// next section
|
||||
if (item is ListItemBlock listItem)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Filtering of Specific Child Types
|
||||
|
||||
Filtering can be performed using the `Descendants<T>()` method, in which T is required to be derived from `MarkdownObject`.
|
||||
|
||||
```csharp
|
||||
MarkdownDocument result = Markdown.Parse(sourceText, pipeline);
|
||||
|
||||
// Iterate through all ListItem blocks
|
||||
foreach (var item in result.Descendants<ListItemBlock>())
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
// Iterate through all image links
|
||||
foreach (var item in result.Descendants<LinkInline>().Where(x => x.IsImage))
|
||||
{
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
#### Combined Hierarchies
|
||||
|
||||
The `Descendants` method can be used on any `MarkdownObject`, not just the root node, so complex hierarchies can be queried.
|
||||
|
||||
```csharp
|
||||
MarkdownDocument result = Markdown.Parse(sourceText, pipeline);
|
||||
|
||||
// Find all Emphasis inlines which descend from a ListItem block
|
||||
var items = document.Descendants<ListItemBlock>()
|
||||
.Select(block => block.Descendants<EmphasisInline>());
|
||||
|
||||
// Find all Emphasis inlines whose direct parent block is a ListItem
|
||||
var other = document.Descendants<EmphasisInline>()
|
||||
.Where(inline => inline.ParentBlock is ListItemBlock);
|
||||
```
|
||||
|
||||
## Block Elements
|
||||
|
||||
Block elements all derive from `Block` and may be one of two types:
|
||||
|
||||
1. `ContainerBlock`, which is a block which holds other blocks (`MarkdownDocument` is itself derived from this)
|
||||
2. `LeafBlock`, which is a block that has no child blocks, but may contain inlines
|
||||
|
||||
Block elements in markdown refer to things like paragraphs, headings, lists, code, etc. Most blocks may contain inlines, with the exception of things like code blocks.
|
||||
|
||||
### Properties of Blocks
|
||||
|
||||
The following are properties of `Block` objects which warrant elaboration. For a full list of properties see the generated API documentation (coming soon).
|
||||
|
||||
#### Block Parent
|
||||
All blocks have a reference to a parent (`Parent`) of type `ContainerBlock?`, which allows for efficient traversal up the abstract syntax tree. The parent will be `null` in the case of the root node (the `MarkdownDocument`).
|
||||
|
||||
#### Parser
|
||||
|
||||
All blocks have a reference to a parser (`Parser`) of type `BlockParser?` which refers to the instance of the parser which created this block.
|
||||
|
||||
#### IsOpen Flag
|
||||
|
||||
Blocks have an `IsOpen` boolean flag which is set true while they're being parsed and then closed when parsing is complete.
|
||||
|
||||
Blocks are created by `BlockParser` objects which are managed by an instance of a `BlockProcessor` object. During the parsing algorithm the `BlockProcessor` maintains a list of all currently open `Block` objects as it steps through the source line by line. The `IsOpen` flag indicates to the `BlockProcessor` that the block should remain open as the next line begins. If the `IsOpen` flag is not directly set by the `BlockParser` on each line, the `BlockProcessor` will consider the `Block` fully parsed and will no longer call its `BlockParser` on it.
|
||||
|
||||
#### IsBreakable Flag
|
||||
|
||||
Blocks are either breakable or not, specified by the `IsBreakable` flag. If a block is non-breakable it indicates to the parser that the close condition of any parent container do not apply so long as the non-breakable child block is still open.
|
||||
|
||||
The only built-in example of this is the `FencedCodeBlock`, which, if existing as the child of a container block of some sort, will prevent that container from being closed before the `FencedCodeBlock` is closed, since any characters inside the `FencedCodeBlock` are considered to be valid code and not the container's close condition.
|
||||
|
||||
#### RemoveAfterProcessInlines
|
||||
|
||||
|
||||
|
||||
## Inline Elements
|
||||
|
||||
Inlines in markdown refer to things like embellishments (italics, bold, underline, etc), links, urls, inline code, images, etc.
|
||||
|
||||
Inline elements may be one of two types:
|
||||
|
||||
1. `Inline`, whose parent is always a `ContainerInline`
|
||||
2. `ContainerInline`, derived from `Inline`, which contains other inlines. `ContainerInline` also has a `ParentBlock` property of type `LeafBlock?`
|
||||
|
||||
|
||||
**(Is there anything special worth documenting about inlines or types of inlines?)**
|
||||
|
||||
## The SourceSpan Struct
|
||||
|
||||
If the pipeline was configured with `.UsePreciseSourceLocation()`, all elements in the abstract syntax tree will contain a reference to the location in the original source where they occurred. This is done with the `SourceSpan` type, a custom Markdig `struct` which provides a start and end location.
|
||||
|
||||
All objects derived from `MarkdownObject` contain the `Span` property, which is of type `SourceSpan`.
|
||||
|
||||
127
doc/parsing-extensions.md
Normal file
127
doc/parsing-extensions.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Extensions and Parsers
|
||||
|
||||
Markdig was [implemented in such a way](http://xoofx.com/blog/2016/06/13/implementing-a-markdown-processor-for-dotnet/) as to be extremely pluggable, with even basic behaviors being mutable and extendable.
|
||||
|
||||
The basic mechanism for extension of Markdig is the `IMarkdownExtension` interface, which allows any implementing class to be registered with the pipeline builder and thus to directly modify the collections of `BlockParser` and `InlineParser` objects which end up in the pipeline.
|
||||
|
||||
This document discusses the `IMarkdownExtension` interface, the `BlockParser` abstract base class, and the `InlineParser` abstract base class, which together are the foundation of extending Markdig's parsing machinery.
|
||||
|
||||
## Creating Extensions
|
||||
|
||||
Extensions can vary from very simple to very complicated.
|
||||
|
||||
A simple extension, for example, might simply find a parser already in the pipeline and modify a setting on it. An example of this is the `SoftlineBreakAsHardlineExtension`, which locates the `LineBreakInlineParser` and modifies a single boolean flag on it.
|
||||
|
||||
A complex extension, on the other hand, might add an entire taxonomy of new `Block` and `Inline` types, as well as several related parsers and renderers, and require being added to the the pipeline in a specific order in relation to other extensions which are already configured. The `FootnoteExtension` and `PipeTableExtension` are examples of more complex extensions.
|
||||
|
||||
For extensions that don't require order considerations, the implementation of the extension itself is adequate, and the extension can be added to the pipeline with the generic `Use<TExtension>()` method on the pipeline builder. For extensions which do require order considerations, it is best to create an extension method on the `MarkdownPipelineBuilder` to perform the registration. See the following two sections for further information.
|
||||
|
||||
### Implementation of an Extension
|
||||
|
||||
The [IMarkdownExtension.cs](https://github.com/xoofx/markdig/blob/master/src/Markdig/IMarkdownExtension.cs) interface specifies two methods which must be implemented.
|
||||
|
||||
The first, which takes only the pipeline builder as an argument, is called when the `Build()` method on the pipeline builder is invoked, and should set up any modifications to the parsers or parser collections. These parsers will then be used by the main parsing algorithm to process the source text.
|
||||
|
||||
```csharp
|
||||
void Setup(MarkdownPipelineBuilder pipeline);
|
||||
```
|
||||
|
||||
The second, which takes the pipeline itself and a renderer, is used to set up a rendering component in order to convert any special `MarkdownObject` types associated with the extension into an output. This is not relevant for parsing, but is necessary for rendering.
|
||||
|
||||
```csharp
|
||||
void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer);
|
||||
```
|
||||
|
||||
The extension can then be registered to the pipeline builder using the `Use<TExtension>()` method. A skeleton example is given below:
|
||||
|
||||
```csharp
|
||||
public class MySpecialBlockParser : BlockParser
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
public class MyExtension : IMarkdownExtension
|
||||
{
|
||||
void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
pipeline.BlockParsers.AddIfNotAlready<MySpecialBlockParser>();
|
||||
}
|
||||
|
||||
void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) { }
|
||||
}
|
||||
```
|
||||
|
||||
```csharp
|
||||
var builder = new MarkdownPipelineBuilder()
|
||||
.Use<MyExtension>();
|
||||
```
|
||||
|
||||
### Pipeline Builder Extension Methods
|
||||
|
||||
For extensions which require specific ordering and/or need to perform multiple operations to register with the builder, it's recommended to create an extension method.
|
||||
|
||||
```csharp
|
||||
public static class MyExtensionMethods
|
||||
{
|
||||
public static MarkdownPipelineBuilder UseMyExtension(this MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
// Directly access or modify pipeline.Extensions here, with the ability to
|
||||
// search for other extensions, insert before or after, remove other extensions,
|
||||
// or modify their settings.
|
||||
|
||||
// ...
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Simple Extension Example
|
||||
|
||||
An example of a simple extension which does not add any new parsers, but instead creates a new, horrific emphasis tag, marked by triple percentage signs. This example is based on [CitationExtension.cs](https://github.com/xoofx/markdig/blob/master/src/Markdig/Extensions/Citations/CitationExtension.cs)
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// An extension which applies to text of the form %%%text%%%
|
||||
/// </summary>
|
||||
public class BlinkExtension : IMarkdownExtension
|
||||
{
|
||||
// This setup method will be run when the pipeline builder's `Build()` method is invoked. As this
|
||||
// is a simple, self-contained extension we won't be adding anything new, but rather finding an
|
||||
// existing parser already in the pipeline and adding some settings to it.
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
// We check the pipeline builder's inline parser collection and see if we can find a parser
|
||||
// registered of the type EmphasisInlineParser. This is the parser which nominally handles
|
||||
// bold and italic emphasis, but we know from its documentation that it is a general parser
|
||||
// that can have new characters added to it.
|
||||
var parser = pipeline.InlineParsers.FindExact<EmphasisInlineParser>();
|
||||
|
||||
// If we find the parser and it doesn't already have the % character registered, we add
|
||||
// a descriptor for 3 consecutive % signs. This is specific to the EmphasisInlineParser and
|
||||
// is just used here as an example.
|
||||
if (parser is not null && !parser.HasEmphasisChar('%'))
|
||||
{
|
||||
parser.EmphasisDescriptors.Add(new EmphasisDescriptor('%', 3, 3, false));
|
||||
}
|
||||
}
|
||||
|
||||
// This method is called by the pipeline before rendering, which is a separate operation from
|
||||
// parsing. This implementation is just here for the purpose of the example, in which we
|
||||
// daisy-chain a delegate specific to the EmphasisInlineRenderer to cause an unconscionable tag
|
||||
// to be inserted into the HTML output wherever a %%% annotated span was placed in the source.
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
if (renderer is not HtmlRenderer) return;
|
||||
|
||||
var emphasisRenderer = renderer.ObjectRenderers.FindExact<EmphasisInlineRenderer>();
|
||||
if (emphasisRenderer is null) return;
|
||||
|
||||
var previousTag = emphasisRenderer.GetTag;
|
||||
emphasisRenderer.GetTag = inline =>
|
||||
(inline.DelimiterCount == 3 && inline.DelimiterChar == '%' ? "blink" : null)
|
||||
?? previousTag(inline);
|
||||
}
|
||||
}
|
||||
```
|
||||
336
doc/parsing-overview.md
Normal file
336
doc/parsing-overview.md
Normal file
@@ -0,0 +1,336 @@
|
||||
# Markdig Parsing
|
||||
|
||||
Markdig provides efficient, regex-free parsing of markdown documents directly into an abstract syntax tree (AST). The AST is a representation of the markdown document's semantic constructs, which can be manipulated and explored programmatically.
|
||||
|
||||
* This document contains a general overview of the parsing system and components and their use
|
||||
* The [Abstract Syntax Tree](parsing-ast.md) document contains a discussion of how Markdig represents the product of the parsing operation
|
||||
* The [Extensions/Parsers](parsing-extensions.md) document explores extensions and block/inline parsers within the context of extending Markdig's parsing capabilities
|
||||
|
||||
## Introduction
|
||||
|
||||
Markdig's parsing machinery consists of two main components at its surface: the `Markdown.Parse(...)` method and the `MarkdownPipeline` type. The parsed document is represented by a `MarkdownDocument` object, which is a tree of objects derived from `MarkdownObject`, including block and inline elements.
|
||||
|
||||
The `Markdown` static class is the main entrypoint to the Markdig API. It contains the `Parse(...)` method, the main algorithm for parsing a markdown document. The `Parse(...)` method in turn uses a `MarkdownPipeline`, which is a sealed internal class which maintains some configuration information and the collections of parsers and extensions. The `MarkdownPipeline` determines how the parser behaves and what its capabilities are. The `MarkdownPipeline` can be modified with built-in as well as user developed extensions.
|
||||
|
||||
### Glossary of Relevant Types
|
||||
|
||||
The following is a table of some of the types relevant to parsing and mentioned in the related documentation. For an exhaustive list refer to API documentation (coming soon).
|
||||
|
||||
|Type|Description|
|
||||
|-|-|
|
||||
|`Markdown`|Static class with the entry point to the parsing algorithm via the `Parse(...)` method|
|
||||
|`MarkdownPipeline`|Configuration object for the parser, contains collections of block and inline parsers and registered extensions|
|
||||
|`MarkdownPipelineBuilder`|Responsible for constructing the `MarkdownPipeline`, used by client code to configure pipeline options and behaviors|
|
||||
|`IMarkdownExtension`|Interface for [Extensions](#extensions-imarkdownextension) which alter the behavior of the pipeline, this is the standard mechanism for extending Markdig|
|
||||
|`BlockParser`|Base type for an individual parsing component meant to identify `Block` elements in the markdown source|
|
||||
|`InlineParser`|Base type for an individual parsing component meant to identify `Inline` elements within a `Block`|
|
||||
|`Block`|A node in the AST representing a markdown block element, can either be a `ContainerBlock` or a `LeafBlock`|
|
||||
|`Inline`|A node in the AST representing a markdown inline element|
|
||||
|`MarkdownDocument`|The root node of the AST produced by the parser, derived from `ContainerBlock`|
|
||||
|`MarkdownObject`|The base type of all `Block` and `Inline` derived objects (as well as `HtmlAttributes`)|
|
||||
|
||||
### Simple Examples
|
||||
|
||||
*The following are simple examples of parsing to help get you started, see the following sections for an in-depth explanation of the different parts of Markdig's parsing mechanisms*
|
||||
|
||||
The `MarkdownPipeline` dictate how the parser will behave. The `Markdown.Parse(...)` method will construct a default pipeline if none is provided. A default pipeline will be CommonMark compliant but nothing else.
|
||||
|
||||
```csharp
|
||||
var markdownText = File.ReadAllText("sample.md");
|
||||
|
||||
// No pipeline provided means a default pipeline will be used
|
||||
var document = Markdown.Parse(markdownText);
|
||||
```
|
||||
|
||||
Pipelines can be created and configured manually, however this must be done using a `MarkdownPipelineBuilder` object, which then is configured through a fluent interface composed of extension methods.
|
||||
|
||||
```csharp
|
||||
var markdownText = File.ReadAllText("sample.md");
|
||||
|
||||
// Markdig's "UseAdvancedExtensions" option includes many common extensions beyond
|
||||
// CommonMark, such as citations, figures, footnotes, grid tables, mathematics
|
||||
// task lists, diagrams, and more.
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
var document = Markdown.Parse(markdownText, pipeline);
|
||||
```
|
||||
|
||||
Extensions can also be added individually:
|
||||
|
||||
```csharp
|
||||
var markdownText = File.ReadAllText("sample.md");
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseCitations()
|
||||
.UseFootnotes()
|
||||
.UseMyCustomExtension()
|
||||
.Build();
|
||||
|
||||
var document = Markdown.Parse(markdownText, pipeline);
|
||||
```
|
||||
|
||||
## Markdown.Parse and the MarkdownPipeline
|
||||
|
||||
As metioned in the [Introduction](#introduction), Markdig's parsing machinery involves two surface components: the `Markdown.Parse(...)` method, and the `MarkdownPipeline` type. The main parsing algorithm (not to be confused with individual `BlockParser` and `InlineParser` components) lives in the `Markdown.Parse(...)` static method. The `MarkdownPipeline` is responsible for configuring the behavior of the parser.
|
||||
|
||||
These two components are covered in further detail in the following sections.
|
||||
|
||||
### The MarkdownPipeline
|
||||
|
||||
The `MarkdownPipeline` is a sealed internal class which dictates what features the parsing algorithm has. The pipeline must be created by using a `MarkdownPipelineBuilder` as shown in the examples above.
|
||||
|
||||
The `MarkdownPipeline` holds configuration information and collections of extensions and parsers. Parsers fall into one of two categories:
|
||||
|
||||
* Block Parsers (`BlockParser`)
|
||||
* Inline Parsers (`InlineParser`)
|
||||
|
||||
Extensions are classes implementing `IMarkdownExtension` which are allowed to add to the list of parsers, or modify existing parsers and/or renderers. They are invoked to perform their mutations on the pipeline when the pipeline is built by the `MarkdownPipelineBuilder`.
|
||||
|
||||
Lastly, the `MarkdownPipeline` contains a few extra elements:
|
||||
|
||||
* A configuration setting determining whether or not trivial elements, referred to as *trivia*, (whitespace, extra heading characters, unescaped strings, etc) are to be tracked
|
||||
* A configuration setting determining whether or not nodes in the resultant abstract syntax tree will refer to their precise original locations in the source
|
||||
* An optional delegate which will be invoked when the document has been processed.
|
||||
* An optional `TextWriter` which will get debug logging from the parser
|
||||
|
||||
### The Markdown.Parse Method
|
||||
|
||||
`Markdown.Parse` is a static method which contains the overall parsing algorithm but not the actual parsing components, which instead are contained within the pipeline.
|
||||
|
||||
The `Markdown.Parse(...)` method takes a string containing raw markdown and returns a `MarkdownDocument`, which is the root node in the abstract syntax tree. The `Parse(...)` method optionally takes a pre-configured `MarkdownPipeline`, but if none is given will create a default pipeline which has minimal features.
|
||||
|
||||
Within the `Parse(...)` method, the following sequence of operations occur:
|
||||
|
||||
1. The block parsers contained in the pipeline are invoked on the raw markdown text, creating the initial tree of block elements
|
||||
2. If the pipeline is configured to track markdown trivia (trivial/non-contributing elements), the blocks are expanded to absorb neighboring trivia
|
||||
3. The inline parsers contained in the pipeline are now invoked on the blocks, populating the inline elements of the abstract syntax tree
|
||||
4. If a delegate has been configured for when the document has completed processing, it is now invoked
|
||||
5. The abstract syntax tree (`MarkdownDocument` object) is returned
|
||||
|
||||
## The Pipeline Builder and Extensions
|
||||
|
||||
The `MarkdownPipeline` determines the behavior and capabilities of the parser, and *extensions* added via the `MarkdownPipelineBuilder` determine the configuration of the pipeline.
|
||||
|
||||
This section discusses the pipeline builder and the concept of *extensions* in more detail.
|
||||
|
||||
### Extensions (IMarkdownExtension)
|
||||
|
||||
***Note**: This section discusses how to consume extensions by adding them to pipeline. For a discussion on how to implement an extension, refer to the [Extensions/Parsers](parsing-extensions.md) document.*
|
||||
|
||||
Extensions are the primary mechanism for modifying the parsers in the pipeline.
|
||||
|
||||
An extension is any class which implements the `IMarkdownExtension` interface found in [IMarkdownExtension.cs](https://github.com/xoofx/markdig/blob/master/src/Markdig/IMarkdownExtension.cs). This interface consists solely of two `Setup(...)` overloads, which both take a `MarkdownPipelineBuilder` as the first argument.
|
||||
|
||||
When the `MarkdownPipelineBuilder.Build()` method is invoked as the final stage in pipeline construction, the builder runs through the list of registered extensions in order and calls the `Setup(...)` method on each of them. The extension then has full access to modify both the parser collections themselves (by adding new parsers to it), or to find and modify existing parsers.
|
||||
|
||||
Because of this, *some* extensions may need to be ordered in relation to others, for instance if they modify a parser that gets added by a different extension. The `OrderedList<T>` class contains convenience methods to this end, which aid in finding other extensions by type and then being able to added an item before or after them.
|
||||
|
||||
### The MarkdownPipelineBuilder
|
||||
|
||||
Because the `MarkdownPipeline` is a sealed internal class, it cannot (and *should* not be attempted to) be created directly. Rather, the `MarkdownPipelineBuilder` manages the requisite construction of the pipeline after the configuration has been provided by the client code.
|
||||
|
||||
As discussed in the [section above](#the-markdownpipeline), the `MarkdownPipeline` primarily consists of a collection of block parsers and a collection of inline parsers, which are provided to the `Markdown.Parse(...)` method and thus determine its features and behavior. Both the collections and some of the parsers themselves are mutable, and the mechanism of mutation is the `Setup(...)` method of the `IMarkdownExtension` interface. This is covered in more detail in the section on [Extensions](#extensions-imarkdownextension).
|
||||
|
||||
#### The Fluent Interface
|
||||
|
||||
A collection of extension methods in the [MarkdownExtensions.cs](https://github.com/xoofx/markdig/blob/master/src/Markdig/MarkdownExtensions.cs) source file provides a convenient fluent API for the configuration of the pipeline builder. This should be considered the standard way of configuring the builder.
|
||||
|
||||
##### Configuration Options
|
||||
|
||||
There are several extension methods which apply configurations to the builder which change settings in the pipeline outside of the use of typical extensions.
|
||||
|
||||
|Method|Description|
|
||||
|-|-|
|
||||
|`.ConfigureNewLine(...)`|Takes a string which will serve as the newline delimiter during parsing|
|
||||
|`.DisableHeadings()`|Disables the parsing of ATX and Setex headings|
|
||||
|`.DisableHtml()`|Disables the parsing of HTML elements|
|
||||
|`.EnableTrackTrivia()`|Enables the tracking of trivia (trivial elements like whitespace)|
|
||||
|`.UsePreciseSourceLocation()`|Maps syntax objects to their precise location in the original source, such as would be required for syntax highlighting|
|
||||
|
||||
```csharp
|
||||
var builder = new MarkdownPipelineBuilder()
|
||||
.ConfigureNewLine("\r\n")
|
||||
.DisableHeadings()
|
||||
.DisableHtml()
|
||||
.EnableTrackTrivia()
|
||||
.UsePreciseSourceLocation();
|
||||
|
||||
var pipeline = builder.Build();
|
||||
```
|
||||
|
||||
##### Adding Extensions
|
||||
|
||||
All extensions which ship with Markdig can be added through a dedicated fluent method, while user code which implements the `IMarkdownExtension` interface can be added with one of the `Use()` methods, or via a custom extension method implemented in the client code.
|
||||
|
||||
Refer to [MarkdownExtensions.cs](https://github.com/xoofx/markdig/blob/master/src/Markdig/MarkdownExtensions.cs) for a full list of extension methods:
|
||||
|
||||
```csharp
|
||||
var builder = new MarkdownPipelineBuilder()
|
||||
.UseFootnotes()
|
||||
.UseFigures();
|
||||
```
|
||||
|
||||
For custom/user-provided extensions, the `Use<TExtension>(...)` methods allow either a type to be directly added or an already constructed instance to be put into the extension container. Internally they will prevent two of the same type of extension from being added to the container.
|
||||
|
||||
```csharp
|
||||
public class MyExtension : IMarkdownExtension
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
// Only works if MyExtension has an empty constructor (aka new())
|
||||
var builder = new MarkdownPipelineBuilder()
|
||||
.Use<MyExtension>();
|
||||
```
|
||||
|
||||
Alternatively:
|
||||
|
||||
```csharp
|
||||
public class MyExtension : IMarkdownExtension
|
||||
{
|
||||
public MyExtension(object someConfigurationObject) { /* ... */ }
|
||||
// ...
|
||||
}
|
||||
|
||||
var instance = new MyExtension(configData);
|
||||
|
||||
var builder = new MarkdownPipelineBuilder()
|
||||
.Use(instance);
|
||||
```
|
||||
|
||||
##### Adding Extensions with the Configure Method
|
||||
|
||||
The `MarkdownPipelineBuilder` has one additional method for the configuration of extensions worth mentioning: the `Configure(...)` method, which takes a `string?` of `+` delimited tokens specifying which extensions should be dynamically configured. This is a convenience method for the configuration of pipelines whose extensions are only known at runtime.
|
||||
|
||||
Refer to [MarkdownExtensions.cs's `Configure(...)`](https://github.com/xoofx/markdig/blob/983187eace6ba02ee16d1443c387267ad6e78f58/src/Markdig/MarkdownExtensions.cs#L538) code for the full list of extensions.
|
||||
|
||||
|
||||
```csharp
|
||||
var builder = new MarkdownPipelineBuilder()
|
||||
.Configure("common+footnotes+figures");
|
||||
|
||||
var pipeline = builder.Build();
|
||||
```
|
||||
|
||||
#### Manual Configuration
|
||||
|
||||
Internally, the fluent interface wraps manual operations on the three primary collections:
|
||||
|
||||
* `MarkdownPipelineBuilder.BlockParsers` - this is an `OrderedList<BlockParser>` of the block parsers
|
||||
* `MarkdownPipelineBuilder.InlineParsers` - this is an `OrderedList<InlineParser>` of the inline element parsers
|
||||
* `MarkdownPipelineBuilder.Extensions` - this is an `OrderedList<IMarkdownExtension>` of the extensions
|
||||
|
||||
All three collections are `OrderedList<T>`, which is a collection type custom to Markdig which contains special methods for finding and inserting derived types. With the builder created, manual configuration can be performed by accessing these collections and their elements and modifying them as necessary.
|
||||
|
||||
***Warning**: be aware that it should not be necessary to directly modify either the `BlockParsers` or the `InlineParsers` collections directly during the pipeline configuration. Rather, these can and should be modified whenever possible through the `Setup(...)` method of extensions, which will be deferred until the pipeline is actually built and will allow for ordering such that operations dependent on other operations can be accounted for.*
|
||||
|
||||
## Block and Inline Parsers
|
||||
|
||||
Let's dive deeper into the parsing system. With a configured pipeline, the `Markdown.Parse` method will run through two two conceptual passes to produce the abstract syntax tree.
|
||||
|
||||
1. First, `BlockProcessor.ProcessLine` is called on the file's lines, one by one, trying to identify block elements in the source
|
||||
2. Next, an `InlineProcessor` is created or borrowed and run on each block to identify inline elements.
|
||||
|
||||
These two conceptual operations dictate Markdig's two types of parsers, both of which derive from `ParserBase<TProcessor>`.
|
||||
|
||||
Block parsers, derived from `BlockParser`, identify block elements from lines in the source text and push them onto the abstract syntax tree. Inline parsers, derived from `InlineParser`, identify inline elements from `LeafBlock` elements and push them into an attached container: the `ContainerInline? LeafBlock.Inline` property.
|
||||
|
||||
Both inline and block parsers are regex-free, and instead work on finding opening characters and then making fast read-only views into the source text.
|
||||
|
||||
### Block Parser
|
||||
|
||||
**(The contents of this section I am very unsure of, this is from my reading of the code but I could use some guidance here)**
|
||||
|
||||
**(Does `CanInterrupt` specifically refer to interrupting a paragraph block?)**
|
||||
|
||||
In order to be added to the parsing pipeline, all block parsers must be derived from `BlockParser`.
|
||||
|
||||
Internally, the main parsing algorithm will be stepping through the source text, using the `HasOpeningCharacter(char c)` method of the block parser collection to pre-identify parsers which *could* be opening a block at a given position in the text based on the active character. Thus any derived implementation needs to set the value of the `char[]? OpeningCharacter` property with the initial characters that might begin the block.
|
||||
|
||||
If a parser can potentially open a block at a place in the source text it should expect to have the `TryOpen(BlockProcessor processor)` method called. This is a virtual method that must be implemented on any derived class. The `BlockProcessor` argument is a reference to an object which stores the current state of parsing and the position in the source.
|
||||
|
||||
**(What are the rules concerning how the `BlockState` return type should work for `TryOpen`? I see examples returning `None`, `Continue`, `BreakDiscard`, `ContinueDiscard`. How does the return value change the algorithm behavior?)**
|
||||
|
||||
**(Should a new block always be pushed into `processor.NewBlocks` in the `TryOpen` method?)**
|
||||
|
||||
As the main parsing algorithm moves forward, it will then call `TryContinue(...)` on blocks that were opened in `TryOpen(..)`.
|
||||
|
||||
**(Is this where/how you close a block? Is there anything that needs to be done to perform that beyond `block.UpdateSpanEnd` and returning `BlockState.Break`?)**
|
||||
|
||||
|
||||
### Inline Parsers
|
||||
|
||||
Inline parsers extract inline markdown elements from the source, but their starting point is the text of each individual `LeafBlock` produced by the block parsing process. To understand the role of each inline parser it is necessary to first understand the inline parsing process as a whole.
|
||||
|
||||
#### The Inline Parsing Process
|
||||
|
||||
After the block parsing process has occurred, the abstract syntax tree of the document has been populated only with block elements, starting from the root `MarkdownDocument` node and ending with the individual `LeafBlock` derived block elements, most of which will be `ParagraphBlocks`, but also include things like `CodeBlocks`, `HeadingBlocks`, `FigureCaptions`, and so on.
|
||||
|
||||
At this point, the parsing machinery will iterate through each `LeafBlock` one by one, creating and assigning its `LeafBlock.Inline` property with an empty `ContainerInline`, and then sweeping through the `LeafBlock`'s text running the inline parsers. This occurs by the following process:
|
||||
|
||||
Starting at the first character of the text it will run through all of its `InlineParser` objects which have that character as a possible opening character for the type of inline they extract. The parsers will run in order (as such ordering is the *only* way which conflicts between parsers are resolved, and thus is important to the overall behavior of the parsing system) and the `Match(...)` method will be called on each candidate parser, in order, until one of them returns `true`.
|
||||
|
||||
The `Match(...)` method will be passed a slice of the text beginning at the *specific character* being processed and running until the end of the `LeafBlock`'s complete text. If the parser can create an `Inline` element it will do so and return `true`, otherwise it will return `false`. The parser will store the created `Inline` object in the processor's `InlineProcessor.Inline` property, which as passed into the `Match(...)` method as an argument. The parser will also advance the start of the working `StringSlice` by the characters consumed in the match.
|
||||
|
||||
* If the parser has created an inline element and returned `true`, that element is pushed into the deepest open `ContainerInline`
|
||||
* If `false` was returned, a default `LiteralInlineParser` will run instead:
|
||||
* If the `InlineProcessor.Inline` property already has an existing `LiteralInline` in it, these characters will be added to the existing `LiteralInline`, effectively growing it
|
||||
* If no `LiteralInline` exists in the `InlineProcessor.Inline` property, a new one will be created containing the consumed characters and pushed into the deepest open `ContainerInline`
|
||||
|
||||
After that, the working text of the `LeafBlock` has been conceptually shortened by the advancing start of the working `StringSlice`, moving the starting character forward. If there is still text remaining, the process repeats from the new starting character until all of the text is consumed.
|
||||
|
||||
At this point, when all of the source text from the `LeafBlock` has been consumed, a post-processing step occurs. `InlineParser` objects in the pipeline which also implement `IPostInlineProcessor` are invoked on the `LeafBlock`'s root `ContainerInline`. This, for example, is the mechanism by which the unstructured output of the `EmphasisInlineParser` is then restructured into cleanly nested `EmphasisInline` and `LiteralInline` elements.
|
||||
|
||||
|
||||
#### Responsibilities of an Inline Parser
|
||||
|
||||
Like the block parsers, an inline parser must provide an array of opening characters with the `char[]? OpeningCharacter` property.
|
||||
|
||||
However, inline parsers only require one other method, the `Match(InlineProcessor processor, ref StringSlice slice)` method, which is expected to determine if a match for the related inline is located at the starting character of the slice.
|
||||
|
||||
Within the `Match` method a parser should:
|
||||
|
||||
1. Determine if a match begins at the starting character of the `slice` argument
|
||||
2. If no match exists, the method should return `false` and not advance the `Start` property of the `slice` argument
|
||||
3. If a match does exist, perform the following actions:
|
||||
* Instantiate the appropriate `Inline` derived class and assign it to the processor argument with `processor.Inline = myInlineObject`
|
||||
* Advance the `Start` property of the `slice` argument by the number of characters contained in the match, for example by using the `NextChar()`, `SkipChar()`, or other helper methods of the `StringSlice` class
|
||||
* Return `true`
|
||||
|
||||
While parsing, the `InlineProcessor` performing the processing, which is available to the `Match` function through the `processor` argument, contains a number of properties which can be used to access the current state of parsing. For example, the `processor.Inline` property is the mechanism for returning a new inline element, but before assignment it contains the last created inline, which in turn can be accessed for its parents.
|
||||
|
||||
Additionally, in the case of inlines which can be expected to contain other inlines, a possible strategy is to inject an inline element derived from `DelimiterInline` when the opening delimiter is detected, then to replace the opening delimiter with the final desired element when the closing delimiter is found. This is the strategy used by the `LinkInlineParser`, for example. In such cases the tools described in the next section, such as the `ReplaceBy` method, can be used. Note that if this method is used the post-processing should be invoked on the `InlineProcessor` in order to finalize any emphasis elements. For example, in the following code adapted from the `LinkInlineParser`:
|
||||
|
||||
```csharp
|
||||
var parent = processor.Inline?.FirstParentOfType<MyDelimiterInline>();
|
||||
if (parent is null) return;
|
||||
|
||||
var myInline = new MySpecialInline { /* set span and other parameters here */ };
|
||||
|
||||
// Replace the delimiter inline with the final inline type, adopting all of its children
|
||||
parent.ReplaceBy(myInline);
|
||||
|
||||
// Notifies processor as we are creating an inline locally
|
||||
processor.Inline = myInline;
|
||||
|
||||
// Process emphasis delimiters
|
||||
processor.PostProcessInlines(0, myInline, null, false);
|
||||
```
|
||||
|
||||
#### Inline Post-Processing
|
||||
|
||||
The purpose of post-processing inlines is typically to re-structure inline elements after the initial parsing is complete and the entire structure of the inline elements within a parent container is now available in a way it was not during the parsing process. Generally this consists of removing, replacing, and re-ordering `Inline` elements.
|
||||
|
||||
To this end, the `Inline` abstract base class contains several helper methods intended to allow manipulation of inline elements during the post-processing phase.
|
||||
|
||||
|Method|Purpose|
|
||||
|-|-|
|
||||
|`InsertAfter(...)`|Takes a new inline as an argument and inserts it into the same parent container after this instance|
|
||||
|`InsertBefore(...)`|Takes a new inline as an argument and inserts it into the same parent container before this instance|
|
||||
|`Remove()`|Removes this inline from its parent container|
|
||||
|`ReplaceBy(...)`|Removes this instance and replaces it with a new inline specified in the argument. Has an option to move all of the original inline's children into the new inline.|
|
||||
|
||||
Additionally, the `PreviousSibling` and `NextSibling` properties can be used to determine the siblings of an inline element within its parent container. The `FirstParentOfType<T>()` method can be used to search for a parent element, which is often useful when searching for `DelimiterInline` derived elements, which are implemented as containers.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.6 KiB |
92
readme.md
92
readme.md
@@ -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 [MarkdownEditor for Visual Studio](https://visualstudiogallery.msdn.microsoft.com/eaab33c3-437b-4918-8354-872dfe5d1bfe) powered by Markdig!
|
||||
- Converter to **HTML**
|
||||
- Passing more than **600+ tests** from the latest [CommonMark specs (0.29)](http://spec.commonmark.org/)
|
||||
- Passing more than **600+ tests** from the latest [CommonMark specs (0.30)](http://spec.commonmark.org/)
|
||||
- Includes all the core elements of CommonMark:
|
||||
- including **GFM fenced code blocks**.
|
||||
- **Extensible** architecture
|
||||
@@ -103,7 +103,7 @@ You can have a look at the [MarkdownExtensions](https://github.com/lunet-io/mark
|
||||
|
||||
## Build
|
||||
|
||||
In order to build Markdig, you need to install [.NET Core RTM](https://www.microsoft.com/net/core)
|
||||
In order to build Markdig, you need to install [.NET 6.0](https://dotnet.microsoft.com/en-us/download)
|
||||
|
||||
## License
|
||||
|
||||
@@ -112,82 +112,34 @@ This software is released under the [BSD-Clause 2 license](https://github.com/lu
|
||||
|
||||
## Benchmarking
|
||||
|
||||
This is an early preview of the benchmarking against various implementations:
|
||||
The latest benchmark was collected on April 23 2022, against the following implementations:
|
||||
|
||||
**C implementations**:
|
||||
|
||||
- [cmark](https://github.com/jgm/cmark) (version: 0.25.0): Reference C implementation of CommonMark, no support for extensions
|
||||
- [Moonshine](https://github.com/brandonc/moonshine) (version: : popular C Markdown processor
|
||||
|
||||
**.NET implementations**:
|
||||
|
||||
- [Markdig](https://github.com/lunet-io/markdig) (version: 0.5.x): itself
|
||||
- [CommonMark.NET(master)](https://github.com/Knagis/CommonMark.NET) (version: 0.11.0): CommonMark implementation for .NET, no support for extensions, port of cmark
|
||||
- [CommonMark.NET(pipe_tables)](https://github.com/AMDL/CommonMark.NET/tree/pipe-tables): An evolution of CommonMark.NET, supports extensions, not released yet
|
||||
- [MarkdownDeep](https://github.com/toptensoftware/markdowndeep) (version: 1.5.0): another .NET implementation
|
||||
- [MarkdownSharp](https://github.com/Kiri-rin/markdownsharp) (version: 1.13.0): Open source C# implementation of Markdown processor, as featured on Stack Overflow, regexp based.
|
||||
- [Marked.NET](https://github.com/T-Alex/MarkedNet) (version: 1.0.5) port of original [marked.js](https://github.com/chjj/marked) project
|
||||
- [Microsoft.DocAsCode.MarkdownLite](https://github.com/dotnet/docfx/tree/dev/src/Microsoft.DocAsCode.MarkdownLite) (version: 2.0.1) used by the [docfx](https://github.com/dotnet/docfx) project
|
||||
|
||||
### Analysis of the results:
|
||||
|
||||
- Markdig is roughly **x100 times faster than MarkdownSharp**, **30x times faster than docfx**
|
||||
- **Among the best in CPU**, Extremely competitive and often faster than other implementations (not feature wise equivalent)
|
||||
- **15% to 30% less allocations** and GC pressure
|
||||
|
||||
Because Marked.NET, MarkdownSharp and DocAsCode.MarkdownLite are way too slow, they are not included in the following charts:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
### Performance for x86:
|
||||
- [Markdig](https://github.com/lunet-io/markdig) (version: 0.30.2): itself
|
||||
- [cmark](https://github.com/commonmark/cmark) (version: 0.30.2): Reference C implementation of CommonMark, no support for extensions
|
||||
- [CommonMark.NET(master)](https://github.com/Knagis/CommonMark.NET) (version: 0.15.1): CommonMark implementation for .NET, no support for extensions, port of cmark, deprecated.
|
||||
- [MarkdownSharp](https://github.com/Kiri-rin/markdownsharp) (version: 2.0.5): Open source C# implementation of Markdown processor, as featured previously on Stack Overflow, regexp based.
|
||||
|
||||
```
|
||||
BenchmarkDotNet-Dev=v0.9.7.0+
|
||||
OS=Microsoft Windows NT 6.2.9200.0
|
||||
Processor=Intel(R) Core(TM) i7-4770 CPU 3.40GHz, ProcessorCount=8
|
||||
Frequency=3319351 ticks, Resolution=301.2637 ns, Timer=TSC
|
||||
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
|
||||
JitModules=clrjit-v4.6.1080.0
|
||||
// * Summary *
|
||||
|
||||
Type=Program Mode=SingleRun LaunchCount=2
|
||||
WarmupCount=2 TargetCount=10
|
||||
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.22000
|
||||
AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores
|
||||
.NET SDK=6.0.202
|
||||
[Host] : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT
|
||||
DefaultJob : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT
|
||||
|
||||
Method | Median | StdDev |Scaled | Gen 0 | Gen 1| Gen 2|Bytes Allocated/Op |
|
||||
--------------------------- |------------ |---------- |------ | ------ |------|---------|------------------ |
|
||||
Markdig | 5.5316 ms | 0.0372 ms | 0.71 | 56.00| 21.00| 49.00| 1,285,917.31 |
|
||||
CommonMark.NET(master) | 4.7035 ms | 0.0422 ms | 0.60 | 113.00| 7.00| 49.00| 1,502,404.60 |
|
||||
CommonMark.NET(pipe_tables) | 5.6164 ms | 0.0298 ms | 0.72 | 111.00| 56.00| 49.00| 1,863,128.13 |
|
||||
MarkdownDeep | 7.8193 ms | 0.0334 ms | 1.00 | 120.00| 56.00| 49.00| 1,884,854.85 |
|
||||
cmark | 4.2698 ms | 0.1526 ms | 0.55 | -| -| -| NA |
|
||||
Moonshine | 6.0929 ms | 0.1053 ms | 1.28 | -| -| -| NA |
|
||||
Marked.NET | 207.3169 ms | 5.2628 ms | 26.51 | 0.00| 0.00| 0.00| 303,125,228.65 |
|
||||
MarkdownSharp | 675.0185 ms | 2.8447 ms | 86.32 | 40.00| 27.00| 41.00| 2,413,394.17 |
|
||||
Microsoft DocfxMarkdownLite | 166.3357 ms | 0.4529 ms | 21.27 |4,452.00|948.00|11,167.00| 180,218,359.60 |
|
||||
|
||||
| Method | Mean | Error | StdDev |
|
||||
|------------------ |-----------:|----------:|----------:|
|
||||
| markdig | 1.979 ms | 0.0221 ms | 0.0185 ms |
|
||||
| cmark | 2.571 ms | 0.0081 ms | 0.0076 ms |
|
||||
| CommonMark.NET | 2.016 ms | 0.0169 ms | 0.0158 ms |
|
||||
| MarkdownSharp | 221.455 ms | 1.4442 ms | 1.3509 ms |
|
||||
```
|
||||
|
||||
### Performance for x64:
|
||||
- Markdig is roughly **x100 times faster than MarkdownSharp**
|
||||
- **20% faster than the reference cmark C implementation**
|
||||
|
||||
```
|
||||
BenchmarkDotNet-Dev=v0.9.6.0+
|
||||
OS=Microsoft Windows NT 6.2.9200.0
|
||||
Processor=Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz, ProcessorCount=8
|
||||
Frequency=3319351 ticks, Resolution=301.2637 ns, Timer=TSC
|
||||
HostCLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT]
|
||||
JitModules=clrjit-v4.6.1080.0
|
||||
|
||||
Type=Program Mode=SingleRun LaunchCount=2
|
||||
WarmupCount=2 TargetCount=10
|
||||
|
||||
Method | Median | StdDev | Gen 0 | Gen 1 | Gen 2 | Bytes Allocated/Op |
|
||||
--------------------- |---------- |---------- |------- |------- |------ |------------------- |
|
||||
TestMarkdig | 5.5276 ms | 0.0402 ms | 109.00 | 96.00 | 84.00 | 1,537,027.66 |
|
||||
TestCommonMarkNet | 4.4661 ms | 0.1190 ms | 157.00 | 96.00 | 84.00 | 1,747,432.06 |
|
||||
TestCommonMarkNetNew | 5.3151 ms | 0.0815 ms | 229.00 | 168.00 | 84.00 | 2,323,922.97 |
|
||||
TestMarkdownDeep | 7.4076 ms | 0.0617 ms | 318.00 | 186.00 | 84.00 | 2,576,728.69 |
|
||||
```
|
||||
|
||||
## Donate
|
||||
|
||||
|
||||
162
site/.lunet/css/main.css
Normal file
162
site/.lunet/css/main.css
Normal file
@@ -0,0 +1,162 @@
|
||||
/* main */
|
||||
:root {
|
||||
--base-html-font-size: 62.5%;
|
||||
--base-font-size: 1.65rem;
|
||||
--container-xl-max-width: 160rem;
|
||||
--site-logo-size: 45px;
|
||||
--site-logo-url: url("/img/markdig.png");
|
||||
}
|
||||
|
||||
/* make tocbot working with halfmoon */
|
||||
html, body, .page-wrapper, .content-wrapper {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.page-wrapper {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* disable any box around active headers with tocbot */
|
||||
h1:focus, h1:active,
|
||||
h2:focus, h2:active,
|
||||
h3:focus, h3:active,
|
||||
h4:focus, h4:active,
|
||||
h5:focus, h5:active,
|
||||
h6:focus, h6:active
|
||||
{
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
border-bottom-width: 2px;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-color: var(--lm-navbar-link-active-text-color);
|
||||
}
|
||||
|
||||
.main-row {
|
||||
min-height: 92vh;
|
||||
}
|
||||
|
||||
.nav-item.active .nav-link {
|
||||
color: var(--lm-navbar-link-active-text-color);
|
||||
background-color: var(--lm-navbar-link-active-bg-color);
|
||||
}
|
||||
|
||||
.site-logo {
|
||||
background: transparent var(--site-logo-url) no-repeat scroll 0px 0px / var(--site-logo-size) var(--site-logo-size);
|
||||
display: block;
|
||||
width: var(--site-logo-size);
|
||||
height: var(--site-logo-size);
|
||||
}
|
||||
|
||||
.in-this-article-nav {
|
||||
position: fixed;
|
||||
margin-right: 2rem;
|
||||
z-index: 20;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.4rem;
|
||||
}
|
||||
|
||||
.in-this-article-nav .title {
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
.content .is-active-link::before {
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.heading-anchor-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media (min-width: 1600px) {
|
||||
html {
|
||||
font-size: var(--base-html-font-size);
|
||||
}
|
||||
}
|
||||
@media (min-width: 1920px) {
|
||||
html {
|
||||
font-size: var(--base-html-font-size);
|
||||
}
|
||||
}
|
||||
|
||||
table.api-dotnet-inherit, table.api-dotnet-implements, table.api-dotnet-derived {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.api-dotnet-inherit td, .api-dotnet-implements td, .api-dotnet-derived td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.api-dotnet-inherit div:not(:first-child):before {
|
||||
content: " ᐅ ";
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
/*.api-dotnet-inherit td, .api-dotnet-inherit tr, .api-dotnet-implements td, .api-dotnet-implements tr, .api-dotnet-derived tr {
|
||||
display: flex;
|
||||
}
|
||||
*/
|
||||
|
||||
.api-dotnet-inherit div, .api-dotnet-implements div
|
||||
{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.api-dotnet-inherit td:not(:first-child), .api-dotnet-implements td:not(:first-child), .api-dotnet-derived td:not(:first-child) {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.api-dotnet-implements div:not(:first-child):before {
|
||||
content: ", ";
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.api-dotnet-parameter-list {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.api-dotnet-parameter-list dd {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
dl.api-dotnet-parameter-list {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
table.api-dotnet-members-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.api-dotnet-members-table p {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.api-dotnet-members-table tr td:first-child {
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
div.api-dotnet {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.api-dotnet-members-table tr:not(:first-child) {
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.api-dotnet-members-table td {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.api-dotnet-members-table tr:not(:first-child) {
|
||||
border-top: 1px solid #777;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
701
site/.lunet/css/prism.css
Normal file
701
site/.lunet/css/prism.css
Normal file
@@ -0,0 +1,701 @@
|
||||
/* PrismJS 1.15.0
|
||||
https://prismjs.com/download.html#themes=prism-coy&languages=markup+clike+c+csharp+cpp+fsharp+markdown&plugins=line-highlight+line-numbers+toolbar+show-language */
|
||||
/**
|
||||
* prism.js Coy theme for JavaScript, CoffeeScript, CSS and HTML
|
||||
* Based on https://github.com/tshedor/workshop-wp-theme (Example: http://workshop.kansan.com/category/sessions/basics or http://workshop.timshedor.com/category/sessions/basics);
|
||||
* @author Tim Shedor
|
||||
*/
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: #393A34;
|
||||
background: none;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
word-spacing: normal;
|
||||
word-break: break-word;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
position: relative;
|
||||
margin: .5em 0;
|
||||
overflow: visible;
|
||||
padding: 0;
|
||||
}
|
||||
pre[class*="language-"]>code {
|
||||
position: relative;
|
||||
border-left: 4px solid #358ccb;
|
||||
box-shadow: -1px 0px 0px 0px #358ccb, 0px 0px 0px 1px #dfdfdf;
|
||||
background-color: #fdfdfd;
|
||||
background-image: linear-gradient(transparent 50%, rgba(69, 142, 209, 0.04) 50%);
|
||||
background-size: 3em 3em;
|
||||
background-origin: content-box;
|
||||
background-attachment: local;
|
||||
}
|
||||
|
||||
code[class*="language"] {
|
||||
max-height: inherit;
|
||||
height: inherit;
|
||||
padding: 0 1em;
|
||||
display: block;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Margin bottom to accommodate shadow */
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background-color: #fdfdfd;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
position: relative;
|
||||
padding: .2em;
|
||||
border-radius: 0.3em;
|
||||
color: #c92c2c;
|
||||
border: 1px solid #dddddd;
|
||||
display: inline;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
pre[class*="language-"]:before,
|
||||
pre[class*="language-"]:after {
|
||||
content: '';
|
||||
z-index: -2;
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0.75em;
|
||||
left: 0.18em;
|
||||
width: 40%;
|
||||
height: 20%;
|
||||
max-height: 13em;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"]:after,
|
||||
pre[class*="language-"]:after {
|
||||
right: 0.75em;
|
||||
left: auto;
|
||||
-webkit-transform: rotate(2deg);
|
||||
-moz-transform: rotate(2deg);
|
||||
-ms-transform: rotate(2deg);
|
||||
-o-transform: rotate(2deg);
|
||||
transform: rotate(2deg);
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.block-comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: #008000; font-style: italic;
|
||||
}
|
||||
|
||||
.token.char,
|
||||
.token.string {
|
||||
color: #A31515;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #393A34; /* no highlight */
|
||||
}
|
||||
|
||||
.token.deleted {
|
||||
color: #c92c2c;
|
||||
}
|
||||
|
||||
.token.function-name,
|
||||
.token.function {
|
||||
color: #393A34;
|
||||
}
|
||||
|
||||
|
||||
.token.tag,
|
||||
.token.selector {
|
||||
color: #800000;
|
||||
}
|
||||
|
||||
.token.attr-name,
|
||||
.token.regex,
|
||||
.token.entity {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.token.builtin,
|
||||
.token.url,
|
||||
.token.symbol,
|
||||
.token.number,
|
||||
.token.boolean,
|
||||
.token.variable,
|
||||
.token.constant,
|
||||
.token.inserted {
|
||||
color: #36acaa;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.variable {
|
||||
color: #a67f59;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword,
|
||||
.token.class-name {
|
||||
color: #0000ff;
|
||||
}
|
||||
|
||||
.token.directive.tag .tag {
|
||||
background: #ffff00;
|
||||
color: #393A34;
|
||||
}
|
||||
|
||||
.token.class-name,
|
||||
.token.property {
|
||||
color: #2B91AF;
|
||||
}
|
||||
|
||||
.token.important {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #a67f59;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
pre[class*="language-"]:before,
|
||||
pre[class*="language-"]:after {
|
||||
bottom: 14px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Plugin styles */
|
||||
.token.tab:not(:empty):before,
|
||||
.token.cr:before,
|
||||
.token.lf:before {
|
||||
color: #e0d7d1;
|
||||
}
|
||||
|
||||
/* Plugin styles: Line Numbers */
|
||||
pre[class*="language-"].line-numbers.line-numbers {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
pre[class*="language-"].line-numbers.line-numbers code {
|
||||
padding-left: 3.8em;
|
||||
}
|
||||
|
||||
pre[class*="language-"].line-numbers.line-numbers .line-numbers-rows {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/* Plugin styles: Line Highlight */
|
||||
pre[class*="language-"][data-line] {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
pre[data-line] code {
|
||||
position: relative;
|
||||
padding-left: 4em;
|
||||
}
|
||||
pre .line-highlight {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
pre[data-line] {
|
||||
position: relative;
|
||||
padding: 1em 0 1em 3em;
|
||||
}
|
||||
|
||||
.line-highlight {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: inherit 0;
|
||||
margin-top: 1em; /* Same as .prism’s padding-top */
|
||||
|
||||
background: hsla(24, 20%, 50%,.08);
|
||||
background: linear-gradient(to right, hsla(24, 20%, 50%,.1) 70%, hsla(24, 20%, 50%,0));
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
line-height: inherit;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.line-highlight:before,
|
||||
.line-highlight[data-end]:after {
|
||||
content: attr(data-start);
|
||||
position: absolute;
|
||||
top: .4em;
|
||||
left: .6em;
|
||||
min-width: 1em;
|
||||
padding: 0 .5em;
|
||||
background-color: hsla(24, 20%, 50%,.4);
|
||||
color: hsl(24, 20%, 95%);
|
||||
font: bold 65%/1.5 sans-serif;
|
||||
text-align: center;
|
||||
vertical-align: .3em;
|
||||
border-radius: 999px;
|
||||
text-shadow: none;
|
||||
box-shadow: 0 1px white;
|
||||
}
|
||||
|
||||
.line-highlight[data-end]:after {
|
||||
content: attr(data-end);
|
||||
top: auto;
|
||||
bottom: .4em;
|
||||
}
|
||||
|
||||
.line-numbers .line-highlight:before,
|
||||
.line-numbers .line-highlight:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"].line-numbers {
|
||||
position: relative;
|
||||
padding-left: 3.8em;
|
||||
counter-reset: linenumber;
|
||||
}
|
||||
|
||||
pre[class*="language-"].line-numbers > code {
|
||||
position: relative;
|
||||
white-space: inherit;
|
||||
}
|
||||
|
||||
.line-numbers .line-numbers-rows {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
top: 0;
|
||||
font-size: 100%;
|
||||
left: -3.8em;
|
||||
width: 3em; /* works for line-numbers below 1000 lines */
|
||||
letter-spacing: -1px;
|
||||
border-right: 1px solid #a5a5a5;
|
||||
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
}
|
||||
|
||||
.line-numbers-rows > span {
|
||||
pointer-events: none;
|
||||
display: block;
|
||||
counter-increment: linenumber;
|
||||
}
|
||||
|
||||
.line-numbers-rows > span:before {
|
||||
content: counter(linenumber);
|
||||
color: #2B91AF;
|
||||
display: block;
|
||||
padding-right: 0.8em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.code-toolbar {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.code-toolbar > .toolbar {
|
||||
position: absolute;
|
||||
top: .3em;
|
||||
right: .2em;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
div.code-toolbar:hover > .toolbar {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
div.code-toolbar > .toolbar .toolbar-item {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.code-toolbar > .toolbar a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.code-toolbar > .toolbar button {
|
||||
background: none;
|
||||
border: 0;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
line-height: normal;
|
||||
overflow: visible;
|
||||
padding: 0;
|
||||
-webkit-user-select: none; /* for button */
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
}
|
||||
|
||||
div.code-toolbar > .toolbar a,
|
||||
div.code-toolbar > .toolbar button,
|
||||
div.code-toolbar > .toolbar span {
|
||||
color: #bbb;
|
||||
font-size: .8em;
|
||||
padding: 0 .5em;
|
||||
background: #f5f2f0;
|
||||
background: rgba(224, 224, 224, 0.2);
|
||||
box-shadow: 0 2px 0 0 rgba(0,0,0,0.2);
|
||||
border-radius: .5em;
|
||||
}
|
||||
|
||||
div.code-toolbar > .toolbar a:hover,
|
||||
div.code-toolbar > .toolbar a:focus,
|
||||
div.code-toolbar > .toolbar button:hover,
|
||||
div.code-toolbar > .toolbar button:focus,
|
||||
div.code-toolbar > .toolbar span:hover,
|
||||
div.code-toolbar > .toolbar span:focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
/* https://github.com/PrismJS/prism-themes/blob/master/themes/prism-vsc-dark-plus.css */
|
||||
pre[class*="language-"],
|
||||
code[class*="language-"] {
|
||||
color: #d4d4d4;
|
||||
font-size: 13px;
|
||||
text-shadow: none;
|
||||
font-family: Menlo, Monaco, Consolas, "Andale Mono", "Ubuntu Mono", "Courier New", monospace;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
word-spacing: normal;
|
||||
word-break: break-word;
|
||||
line-height: 1.5;
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"] {
|
||||
position: relative;
|
||||
margin: .5em 0;
|
||||
overflow: visible;
|
||||
background: #1e1e1e;
|
||||
}
|
||||
|
||||
pre[class*="language-"]>code {
|
||||
border-left: 4px solid #358ccb;
|
||||
}
|
||||
|
||||
code[class*="language"] {
|
||||
max-height: inherit;
|
||||
height: inherit;
|
||||
padding: 0.5em 1em;
|
||||
display: block;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection,
|
||||
code[class*="language-"]::selection,
|
||||
pre[class*="language-"] *::selection,
|
||||
code[class*="language-"] *::selection {
|
||||
text-shadow: none;
|
||||
background: #75a7ca;
|
||||
}
|
||||
|
||||
@media print {
|
||||
pre[class*="language-"],
|
||||
code[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em .3em;
|
||||
border-radius: .3em;
|
||||
color: #db4c69;
|
||||
background: #f9f2f4;
|
||||
}
|
||||
/*********************************************************
|
||||
* Tokens
|
||||
*/
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.doctype .token.doctype-tag {
|
||||
color: #569CD6;
|
||||
}
|
||||
|
||||
.token.doctype .token.name {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog {
|
||||
color: #6a9955;
|
||||
}
|
||||
|
||||
.token.punctuation,
|
||||
.language-html .language-css .token.punctuation,
|
||||
.language-html .language-javascript .token.punctuation {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.inserted,
|
||||
.token.unit {
|
||||
color: #b5cea8;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.deleted {
|
||||
color: #ce9178;
|
||||
}
|
||||
|
||||
.language-css .token.string.url {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
.token.operator.arrow {
|
||||
color: #569CD6;
|
||||
}
|
||||
|
||||
.token.atrule {
|
||||
color: #ce9178;
|
||||
}
|
||||
|
||||
.token.atrule .token.rule {
|
||||
color: #c586c0;
|
||||
}
|
||||
|
||||
.token.atrule .token.url {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
.token.atrule .token.url .token.function {
|
||||
color: #dcdcaa;
|
||||
}
|
||||
|
||||
.token.atrule .token.url .token.punctuation {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
.token.keyword {
|
||||
color: #569CD6;
|
||||
}
|
||||
|
||||
.token.keyword.module,
|
||||
.token.keyword.control-flow {
|
||||
color: #c586c0;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.function .token.maybe-class-name {
|
||||
color: #dcdcaa;
|
||||
}
|
||||
|
||||
.token.regex {
|
||||
color: #d16969;
|
||||
}
|
||||
|
||||
.token.important {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.constant {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
.token.class-name,
|
||||
.token.maybe-class-name {
|
||||
color: #4ec9b0;
|
||||
}
|
||||
|
||||
.token.console {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
.token.parameter {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
.token.interpolation {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
.token.punctuation.interpolation-punctuation {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.token.boolean {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.variable,
|
||||
.token.imports .token.maybe-class-name,
|
||||
.token.exports .token.maybe-class-name {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
.token.selector {
|
||||
color: #d7ba7d;
|
||||
}
|
||||
|
||||
.token.escape {
|
||||
color: #d7ba7d;
|
||||
}
|
||||
|
||||
.token.tag {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.token.tag .token.punctuation {
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.token.cdata {
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
.token.attr-name {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
.token.attr-value,
|
||||
.token.attr-value .token.punctuation {
|
||||
color: #ce9178;
|
||||
}
|
||||
|
||||
.token.attr-value .token.punctuation.attr-equals {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
color: #569cd6;
|
||||
}
|
||||
|
||||
.token.namespace {
|
||||
color: #4ec9b0;
|
||||
}
|
||||
/*********************************************************
|
||||
* Language Specific
|
||||
*/
|
||||
|
||||
pre[class*="language-javascript"],
|
||||
code[class*="language-javascript"],
|
||||
pre[class*="language-jsx"],
|
||||
code[class*="language-jsx"],
|
||||
pre[class*="language-typescript"],
|
||||
code[class*="language-typescript"],
|
||||
pre[class*="language-tsx"],
|
||||
code[class*="language-tsx"] {
|
||||
color: #9cdcfe;
|
||||
}
|
||||
|
||||
pre[class*="language-css"],
|
||||
code[class*="language-css"] {
|
||||
color: #ce9178;
|
||||
}
|
||||
|
||||
pre[class*="language-html"],
|
||||
code[class*="language-html"] {
|
||||
color: #d4d4d4;
|
||||
}
|
||||
|
||||
.language-regex .token.anchor {
|
||||
color: #dcdcaa;
|
||||
}
|
||||
|
||||
.language-html .token.punctuation {
|
||||
color: #808080;
|
||||
}
|
||||
/*********************************************************
|
||||
* Line highlighting
|
||||
*/
|
||||
pre[data-line] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
pre[class*="language-"] > code[class*="language-"] {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.line-highlight {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: inherit 0;
|
||||
margin-top: 1em;
|
||||
background: #f7ebc6;
|
||||
box-shadow: inset 5px 0 0 #f7d87c;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
line-height: inherit;
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
24
site/.lunet/js/xoofx.js
Normal file
24
site/.lunet/js/xoofx.js
Normal file
@@ -0,0 +1,24 @@
|
||||
anchors.add();
|
||||
var jstoc = document.getElementsByClassName("js-toc");
|
||||
if (jstoc.length > 0)
|
||||
{
|
||||
tocbot.init({
|
||||
// Which headings to grab inside of the contentSelector element.
|
||||
headingSelector: 'h2, h3, h4, h5',
|
||||
collapseDepth: 3,
|
||||
orderedList: true,
|
||||
hasInnerContainers: true,
|
||||
});
|
||||
}
|
||||
|
||||
(function () {
|
||||
const InitLunetTheme = function (e) {
|
||||
if (halfmoon.darkModeOn != e.matches) {
|
||||
halfmoon.toggleDarkMode();
|
||||
}
|
||||
}
|
||||
|
||||
let colorSchemeQueryList = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
InitLunetTheme(colorSchemeQueryList);
|
||||
colorSchemeQueryList.addListener(InitLunetTheme);
|
||||
})();
|
||||
65
site/.lunet/layouts/_default.sbn-html
Normal file
65
site/.lunet/layouts/_default.sbn-html
Normal file
@@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" itemscope itemtype="http://schema.org/WebPage" class="my-html-setup">
|
||||
<head>
|
||||
{{~ Head ~}}
|
||||
</head>
|
||||
<body class="with-custom-webkit-scrollbars with-custom-css-scrollbars">
|
||||
<div class="page-wrapper with-transitions">
|
||||
<div class="sticky-alerts"></div>
|
||||
<div class="content-wrapper bg-light-lm bg-dark-dm">
|
||||
<div class="container-xl bg-white-lm bg-dark-light-dm">
|
||||
<div class="row">
|
||||
<div class="col-xl-12">
|
||||
<nav class="navbar">
|
||||
<a class="site-logo navbar-brand" href="/"></a>
|
||||
<div id="navbarSupportedContent" class="collapse navbar-collapse justify-content-between">
|
||||
{{~ site.menu.home.render {
|
||||
depth: 1,
|
||||
kind: "nav",
|
||||
list_class: "w-100"
|
||||
}
|
||||
~}}
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row main-row">
|
||||
{{~ $main_content_col_size = 8 ~}}
|
||||
{{~ if page.nomenu; $main_content_col_size = $main_content_col_size + 2; end }}
|
||||
{{~ if page.notoc; $main_content_col_size = $main_content_col_size + 2; end }}
|
||||
{{~ if !page.nomenu ~}}
|
||||
<div class="col-xl-2 ">
|
||||
<div class="content">
|
||||
Menu
|
||||
</div>
|
||||
</div>
|
||||
{{~ end ~}}
|
||||
<div class="col-xl-{{ $main_content_col_size }} ">
|
||||
<div class="content js-toc-content">
|
||||
{{ content }}
|
||||
</div>
|
||||
</div>
|
||||
{{~ if !page.notoc ~}}
|
||||
<div class="col-xl-2 ">
|
||||
<div class="content">
|
||||
<div class="in-this-article-nav">
|
||||
<div class="title">In this article</div>
|
||||
<nav class="js-toc toc"></nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{~ end ~}}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xl-12">
|
||||
<nav class="navbar navbar-fixed-bottom justify-content-center">
|
||||
<footer>Copyright © 2009 - {{ date.now.year }}, Alexandre Mutel - Blog content licensed under the Creative Commons <a href="http://creativecommons.org/licenses/by/2.5/">CC BY 2.5</a> | Site powered by <a href="https://github.com/lunet-io/lunet">lunet</a></footer>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
5
site/.lunet/layouts/simple.sbn-html
Normal file
5
site/.lunet/layouts/simple.sbn-html
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
layout: _default
|
||||
---
|
||||
<h1 class="title">{{ page.title_html ?? page.title }}</h1>
|
||||
{{ content }}
|
||||
70
site/config.scriban
Normal file
70
site/config.scriban
Normal file
@@ -0,0 +1,70 @@
|
||||
# Site Settings
|
||||
author = "Alexandre Mutel"
|
||||
title = "markdig"
|
||||
description = "markdig website"
|
||||
|
||||
html.head.title = do; ret page.title + " | " + site.title; end
|
||||
|
||||
basepath = ""
|
||||
baseurl = baseurl ?? "https://markdig.net"
|
||||
|
||||
# Github repository
|
||||
github_user = "xoofx"
|
||||
github_repo_url = "https://github.com/lunet-io/markdig/"
|
||||
|
||||
with cards.twitter
|
||||
enable = true
|
||||
card = "summary_large_image"
|
||||
user = "markdig"
|
||||
image = "/images/twitter-banner.png"
|
||||
end
|
||||
|
||||
# Resources bundle
|
||||
with bundle
|
||||
fontawesome = resource "npm:font-awesome" "4.7.0"
|
||||
halfmoon = resource "npm:halfmoon" "1.1.0"
|
||||
tocbot = resource "npm:tocbot" "4.12.1"
|
||||
anchorjs = resource "npm:anchor-js" "4.2.2"
|
||||
prismjs = resource "npm:prismjs" "1.20.0"
|
||||
|
||||
# scss.includes.add fontawesome.path + "/scss"
|
||||
|
||||
# css files
|
||||
css halfmoon "/css/halfmoon-variables.min.css"
|
||||
css tocbot "/dist/tocbot.css"
|
||||
css fontawesome "/css/font-awesome.min.css"
|
||||
css "/css/prism.css"
|
||||
css "/css/main.css"
|
||||
|
||||
# js files
|
||||
js anchorjs "/anchor.min.js"
|
||||
js halfmoon "/js/halfmoon.min.js"
|
||||
js tocbot "/dist/tocbot.min.js"
|
||||
js prismjs "/prism.js"
|
||||
js prismjs "/components/prism-shell-session.min.js"
|
||||
js prismjs "/components/prism-clike.min.js"
|
||||
js prismjs "/components/prism-c.min.js"
|
||||
js prismjs "/components/prism-cpp.min.js"
|
||||
js prismjs "/components/prism-csharp.min.js"
|
||||
js "/js/prism-stark.js"
|
||||
js "/js/xoofx.js"
|
||||
|
||||
# copy font files
|
||||
content fontawesome "/fonts/fontawesome-webfont.*" "/fonts/"
|
||||
|
||||
# concatenate css/js files
|
||||
concat = true
|
||||
minify = true
|
||||
end
|
||||
|
||||
with attributes
|
||||
# match "/blog/**/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*.md" {
|
||||
# url: "/:section/:year/:month/:day/:title:output_ext"
|
||||
#}
|
||||
end
|
||||
|
||||
with api.dotnet
|
||||
title = "Markdig .NET API Reference"
|
||||
projects = ["../src/Markdig/Markdig.csproj"]
|
||||
properties = {TargetFramework: "netstandard2.0"}
|
||||
end
|
||||
BIN
site/img/markdig.png
Normal file
BIN
site/img/markdig.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
3
site/menu.yml
Normal file
3
site/menu.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
home:
|
||||
- {path: readme.md}
|
||||
- {path: api/readme.md, title: "Markdig API"}
|
||||
10
site/readme.md
Normal file
10
site/readme.md
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
layout: simple
|
||||
title: Home
|
||||
title_html: Hi and Welcome!
|
||||
---
|
||||
|
||||
Hello World!
|
||||
|
||||
Test: <xref:system.object>
|
||||
|
||||
8
site/system.md
Normal file
8
site/system.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
uid: system.object
|
||||
layout: simple
|
||||
title: Home
|
||||
title_html: Hi and Welcome!
|
||||
---
|
||||
|
||||
Hello World from system.object!
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net471</TargetFrameworks>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<IsPackable>false</IsPackable>
|
||||
@@ -8,29 +8,11 @@
|
||||
<ItemGroup>
|
||||
<None Remove="spec.md" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="CommonMarkNew, Version=0.1.0.0, Culture=neutral, PublicKeyToken=001ef8810438905d, processorArchitecture=MSIL">
|
||||
<HintPath>lib\CommonMarkNew.dll</HintPath>
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Aliases>newcmark</Aliases>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="MoonShine">
|
||||
<HintPath>lib\MoonShine.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MarkdownDeep">
|
||||
<HintPath>lib\MarkdownDeep.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="cmark.dll">
|
||||
<HintPath>cmark.dll</HintPath>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="libsundown.dll">
|
||||
<HintPath>libsundown.dll</HintPath>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="spec.md">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -39,9 +21,9 @@
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
||||
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.13.1" />
|
||||
<PackageReference Include="CommonMark.NET" Version="0.15.1" />
|
||||
<PackageReference Include="Markdown" Version="2.2.1" />
|
||||
<PackageReference Include="MarkdownSharp" Version="2.0.5" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="2.0.226801" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.74" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Markdig\Markdig.csproj" />
|
||||
|
||||
@@ -1,9 +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.
|
||||
extern alias newcmark;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using System.IO;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Configs;
|
||||
@@ -25,7 +23,7 @@ namespace Testamina.Markdig.Benchmarks
|
||||
}
|
||||
|
||||
//[Benchmark(Description = "TestMarkdig", OperationsPerInvoke = 4096)]
|
||||
[Benchmark]
|
||||
[Benchmark(Description = "markdig")]
|
||||
public void TestMarkdig()
|
||||
{
|
||||
//var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
|
||||
@@ -33,7 +31,7 @@ namespace Testamina.Markdig.Benchmarks
|
||||
//File.WriteAllText("spec.html", writer.ToString());
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[Benchmark(Description = "cmark")]
|
||||
public void TestCommonMarkCpp()
|
||||
{
|
||||
//var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
|
||||
@@ -41,7 +39,7 @@ namespace Testamina.Markdig.Benchmarks
|
||||
//File.WriteAllText("spec.html", writer.ToString());
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[Benchmark(Description = "CommonMark.NET")]
|
||||
public void TestCommonMarkNet()
|
||||
{
|
||||
////var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
|
||||
@@ -55,93 +53,25 @@ namespace Testamina.Markdig.Benchmarks
|
||||
//writer.ToString();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TestCommonMarkNetNew()
|
||||
{
|
||||
////var reader = new StreamReader(File.Open("spec.md", FileMode.Open));
|
||||
// var reader = new StringReader(text);
|
||||
//CommonMark.CommonMarkConverter.Parse(reader);
|
||||
//CommonMark.CommonMarkConverter.Parse(reader);
|
||||
//reader.Dispose();
|
||||
//var writer = new StringWriter();
|
||||
newcmark::CommonMark.CommonMarkConverter.Convert(text);
|
||||
//writer.Flush();
|
||||
//writer.ToString();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TestMarkdownDeep()
|
||||
{
|
||||
new MarkdownDeep.Markdown().Transform(text);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
[Benchmark(Description = "MarkdownSharp")]
|
||||
public void TestMarkdownSharp()
|
||||
{
|
||||
new MarkdownSharp.Markdown().Transform(text);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TestMoonshine()
|
||||
{
|
||||
Sundown.MoonShine.Markdownify(text);
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
bool markdig = args.Length == 0;
|
||||
bool simpleBench = false;
|
||||
var config = ManualConfig.Create(DefaultConfig.Instance);
|
||||
//var gcDiagnoser = new MemoryDiagnoser();
|
||||
//config.Add(new Job { Mode = Mode.SingleRun, LaunchCount = 2, WarmupCount = 2, IterationTime = 1024, TargetCount = 10 });
|
||||
//config.Add(new Job { Mode = Mode.Throughput, LaunchCount = 2, WarmupCount = 2, TargetCount = 10 });
|
||||
//config.Add(gcDiagnoser);
|
||||
|
||||
if (simpleBench)
|
||||
{
|
||||
var clock = Stopwatch.StartNew();
|
||||
var program = new Program();
|
||||
|
||||
GC.Collect(2, GCCollectionMode.Forced, true);
|
||||
|
||||
var gc0 = GC.CollectionCount(0);
|
||||
var gc1 = GC.CollectionCount(1);
|
||||
var gc2 = GC.CollectionCount(2);
|
||||
|
||||
const int count = 12*64;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (markdig)
|
||||
{
|
||||
program.TestMarkdig();
|
||||
}
|
||||
else
|
||||
{
|
||||
program.TestCommonMarkNetNew();
|
||||
}
|
||||
}
|
||||
clock.Stop();
|
||||
Console.WriteLine((markdig ? "MarkDig" : "CommonMark") + $" => time: {(double)clock.ElapsedMilliseconds/count}ms (total {clock.ElapsedMilliseconds}ms)");
|
||||
DumpGC(gc0, gc1, gc2);
|
||||
}
|
||||
else
|
||||
{
|
||||
//new TestMatchPerf().TestMatch();
|
||||
|
||||
var config = ManualConfig.Create(DefaultConfig.Instance);
|
||||
//var gcDiagnoser = new MemoryDiagnoser();
|
||||
//config.Add(new Job { Mode = Mode.SingleRun, LaunchCount = 2, WarmupCount = 2, IterationTime = 1024, TargetCount = 10 });
|
||||
//config.Add(new Job { Mode = Mode.Throughput, LaunchCount = 2, WarmupCount = 2, TargetCount = 10 });
|
||||
//config.Add(gcDiagnoser);
|
||||
|
||||
//var config = DefaultConfig.Instance;
|
||||
BenchmarkRunner.Run<Program>(config);
|
||||
//BenchmarkRunner.Run<TestDictionary>(config);
|
||||
//BenchmarkRunner.Run<TestMatchPerf>();
|
||||
//BenchmarkRunner.Run<TestStringPerf>();
|
||||
}
|
||||
}
|
||||
|
||||
private static void DumpGC(int gc0, int gc1, int gc2)
|
||||
{
|
||||
Console.WriteLine($"gc0: {GC.CollectionCount(0)-gc0}");
|
||||
Console.WriteLine($"gc1: {GC.CollectionCount(1)-gc1}");
|
||||
Console.WriteLine($"gc2: {GC.CollectionCount(2)-gc2}");
|
||||
//var config = DefaultConfig.Instance;
|
||||
BenchmarkRunner.Run<Program>(config);
|
||||
//BenchmarkRunner.Run<TestDictionary>(config);
|
||||
//BenchmarkRunner.Run<TestMatchPerf>();
|
||||
//BenchmarkRunner.Run<TestStringPerf>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -5,20 +5,45 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<IsPackable>false</IsPackable>
|
||||
<StartupObject>Markdig.Tests.Program</StartupObject>
|
||||
<SpecExecutable>$(MSBuildProjectDirectory)\..\SpecFileGen\bin\$(Configuration)\net6.0\SpecFileGen.dll</SpecExecutable>
|
||||
<SpecTimestamp>$(MSBuildProjectDirectory)\..\SpecFileGen\bin\$(Configuration)\net6.0\SpecFileGen.timestamp</SpecTimestamp>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.msbuild" Version="3.1.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Markdig\Markdig.csproj" />
|
||||
<ProjectReference Include="..\SpecFileGen\SpecFileGen.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ItemSpecExecutable Include="$(SpecExecutable)" />
|
||||
<InputSpecFiles Include="Specs\*.md" />
|
||||
<InputSpecFiles Include="NormalizeSpecs\*.md" />
|
||||
<InputSpecFiles Include="PlainTextSpecs\*.md" />
|
||||
<InputSpecFiles Include="RoundtripSpecs\*.md" />
|
||||
<InputSpecFiles Remove="Specs\readme.md" />
|
||||
<!-- Allow Visual Studio up-to-date check to verify that nothing has changed - https://github.com/dotnet/project-system/blob/main/docs/up-to-date-check.md -->
|
||||
<UpToDateCheckInput Include="@(InputSpecFiles)" />
|
||||
<OutputSpecFiles Include="@(InputSpecFiles->'%(RelativeDir)%(Filename).generated.cs')" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="GeneratedSpecsFile" BeforeTargets="BeforeCompile;CoreCompile" Inputs="@(ItemSpecExecutable);@(InputSpecFiles)" Outputs="@(ItemSpecExecutable->'%(RelativeDir)%(Filename).timestamp');@(InputSpecFiles->'%(RelativeDir)%(Filename).generated.cs')">
|
||||
<Message Importance="high" Text="Regenerating Specs Files" />
|
||||
<Exec Command="dotnet $(SpecExecutable)" />
|
||||
<WriteLinesToFile File="$(SpecTimestamp)" Lines="$([System.DateTime]::Now)" />
|
||||
<ItemGroup>
|
||||
<FileWrites Include="$(SpecTimestamp)" />
|
||||
<_GeneratedSpecsFile Include="Specs\*.generated.cs" />
|
||||
<_GeneratedSpecsFile Include="NormalizeSpecs\*.generated.cs" />
|
||||
<_GeneratedSpecsFile Include="PlainTextSpecs\*.generated.cs" />
|
||||
<_GeneratedSpecsFile Include="RoundtripSpecs\*.generated.cs" />
|
||||
<_GeneratedSpecsFile Remove="@(Compile)" />
|
||||
<Compile Include="@(_GeneratedSpecsFile)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -206,13 +206,10 @@ $$
|
||||
\end{align}
|
||||
</div>
|
||||
";
|
||||
Console.WriteLine("Math Expressions:\n");
|
||||
|
||||
//Console.WriteLine("Math Expressions:\n");
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
|
||||
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
Console.WriteLine(html);
|
||||
//Console.WriteLine(html);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -225,10 +222,16 @@ $\frac{n!}{k!(n-k)!} = \binom{n}{k}$
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
Console.WriteLine(html);
|
||||
|
||||
Assert.IsTrue(html.Contains("<p><span class=\"math\">\\("), "Leading bracket missing");
|
||||
Assert.IsTrue(html.Contains("\\)</span></p>"), "Trailing bracket missing");
|
||||
var test1 = html.Contains("<p><span class=\"math\">\\(");
|
||||
var test2 = html.Contains("\\)</span></p>");
|
||||
if (!test1 || !test2)
|
||||
{
|
||||
Console.WriteLine(html);
|
||||
}
|
||||
|
||||
Assert.IsTrue(test1, "Leading bracket missing");
|
||||
Assert.IsTrue(test2, "Trailing bracket missing");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -243,10 +246,15 @@ $$
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
Console.WriteLine(html);
|
||||
var test1 = html.Contains("<div class=\"math\">\n\\[");
|
||||
var test2 = html.Contains("\\]</div>");
|
||||
if (!test1 || !test2)
|
||||
{
|
||||
Console.WriteLine(html);
|
||||
}
|
||||
|
||||
Assert.IsTrue(html.Contains("<div class=\"math\">\n\\["), "Leading bracket missing");
|
||||
Assert.IsTrue(html.Contains("\\]</div>"), "Trailing bracket missing");
|
||||
Assert.IsTrue(test1, "Leading bracket missing");
|
||||
Assert.IsTrue(test2, "Trailing bracket missing");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -44,8 +44,7 @@ namespace Markdig.Tests.Specs.Normalize.Headings
|
||||
//
|
||||
// ###### Heading 6
|
||||
|
||||
Console.WriteLine("Example 1\nSection Headings\n");
|
||||
TestNormalize.TestSpec("# Heading 1\n\n## Heading 2\n\n### Heading 3\n\n#### Heading 4\n\n##### Heading 5\n\n###### Heading 6", "# Heading 1\n\n## Heading 2\n\n### Heading 3\n\n#### Heading 4\n\n##### Heading 5\n\n###### Heading 6", "");
|
||||
TestNormalize.TestSpec("# Heading 1\n\n## Heading 2\n\n### Heading 3\n\n#### Heading 4\n\n##### Heading 5\n\n###### Heading 6", "# Heading 1\n\n## Heading 2\n\n### Heading 3\n\n#### Heading 4\n\n##### Heading 5\n\n###### Heading 6", "", context: "Example 1\nSection Headings\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -64,8 +63,7 @@ namespace Markdig.Tests.Specs.Normalize.Headings
|
||||
//
|
||||
// Text after two newlines
|
||||
|
||||
Console.WriteLine("Example 2\nSection Headings\n");
|
||||
TestNormalize.TestSpec("###### Heading\n\nText after two newlines", "###### Heading\n\nText after two newlines", "");
|
||||
TestNormalize.TestSpec("###### Heading\n\nText after two newlines", "###### Heading\n\nText after two newlines", "", context: "Example 2\nSection Headings\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -85,8 +83,7 @@ namespace Markdig.Tests.Specs.Normalize.Headings
|
||||
//
|
||||
// Text after two newlines 1
|
||||
|
||||
Console.WriteLine("Example 3\nSection Headings\n");
|
||||
TestNormalize.TestSpec("Heading\n=======\n\nText after two newlines 1", "# Heading\n\nText after two newlines 1", "");
|
||||
TestNormalize.TestSpec("Heading\n=======\n\nText after two newlines 1", "# Heading\n\nText after two newlines 1", "", context: "Example 3\nSection Headings\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@ namespace Markdig.Tests.Specs.PlainText.Sample
|
||||
// Hello, world!
|
||||
//
|
||||
|
||||
Console.WriteLine("Example 1\nSection Sample plain text spec\n");
|
||||
TestPlainText.TestSpec("*Hello*, [world](http://example.com)!", "Hello, world!\n", "");
|
||||
TestPlainText.TestSpec("*Hello*, [world](http://example.com)!", "Hello, world!\n", "", context: "Example 1\nSection Sample plain text spec\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
25
src/Markdig.Tests/RoundtripSpecs/TestYamlFrontMatterBlock.cs
Normal file
25
src/Markdig.Tests/RoundtripSpecs/TestYamlFrontMatterBlock.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Markdig.Renderers.Roundtrip;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
using static Markdig.Tests.TestRoundtrip;
|
||||
|
||||
namespace Markdig.Tests.RoundtripSpecs
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestYamlFrontMatterBlock
|
||||
{
|
||||
[TestCase("---\nkey1: value1\nkey2: value2\n---\n\nContent\n")]
|
||||
[TestCase("No front matter")]
|
||||
[TestCase("Looks like front matter but actually is not\n---\nkey1: value1\nkey2: value2\n---")]
|
||||
public void FrontMatterBlockIsPreserved(string value)
|
||||
{
|
||||
RoundTrip(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,8 +34,7 @@ namespace Markdig.Tests.Specs.Abbreviations
|
||||
// Should be rendered as:
|
||||
// <p>Later in a text we are using <abbr title="Hypertext Markup Language">HTML</abbr> and it becomes an abbr tag <abbr title="Hypertext Markup Language">HTML</abbr></p>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Abbreviation\n");
|
||||
TestParser.TestSpec("*[HTML]: Hypertext Markup Language\n\nLater in a text we are using HTML and it becomes an abbr tag HTML", "<p>Later in a text we are using <abbr title=\"Hypertext Markup Language\">HTML</abbr> and it becomes an abbr tag <abbr title=\"Hypertext Markup Language\">HTML</abbr></p>", "abbreviations|advanced");
|
||||
TestParser.TestSpec("*[HTML]: Hypertext Markup Language\n\nLater in a text we are using HTML and it becomes an abbr tag HTML", "<p>Later in a text we are using <abbr title=\"Hypertext Markup Language\">HTML</abbr> and it becomes an abbr tag <abbr title=\"Hypertext Markup Language\">HTML</abbr></p>", "abbreviations|advanced", context: "Example 1\nSection Extensions / Abbreviation\n");
|
||||
}
|
||||
|
||||
// An abbreviation definition can be indented at most 3 spaces
|
||||
@@ -53,8 +52,7 @@ namespace Markdig.Tests.Specs.Abbreviations
|
||||
// <pre><code>*[This]: is not an abbreviation
|
||||
// </code></pre>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Abbreviation\n");
|
||||
TestParser.TestSpec("*[HTML]: Hypertext Markup Language\n *[This]: is not an abbreviation", "<pre><code>*[This]: is not an abbreviation\n</code></pre>", "abbreviations|advanced");
|
||||
TestParser.TestSpec("*[HTML]: Hypertext Markup Language\n *[This]: is not an abbreviation", "<pre><code>*[This]: is not an abbreviation\n</code></pre>", "abbreviations|advanced", context: "Example 2\nSection Extensions / Abbreviation\n");
|
||||
}
|
||||
|
||||
// An abbreviation may contain spaces:
|
||||
@@ -72,8 +70,7 @@ namespace Markdig.Tests.Specs.Abbreviations
|
||||
// Should be rendered as:
|
||||
// <p>This is a <abbr title="Super Hypertext Markup Language">SUPER HTML</abbr> document</p>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Abbreviation\n");
|
||||
TestParser.TestSpec("*[SUPER HTML]: Super Hypertext Markup Language\n\nThis is a SUPER HTML document ", "<p>This is a <abbr title=\"Super Hypertext Markup Language\">SUPER HTML</abbr> document</p>", "abbreviations|advanced");
|
||||
TestParser.TestSpec("*[SUPER HTML]: Super Hypertext Markup Language\n\nThis is a SUPER HTML document ", "<p>This is a <abbr title=\"Super Hypertext Markup Language\">SUPER HTML</abbr> document</p>", "abbreviations|advanced", context: "Example 3\nSection Extensions / Abbreviation\n");
|
||||
}
|
||||
|
||||
// Abbreviation may contain any unicode characters:
|
||||
@@ -91,8 +88,7 @@ namespace Markdig.Tests.Specs.Abbreviations
|
||||
// Should be rendered as:
|
||||
// <p>This is a <abbr title="Hypertext Markup Language">😃 HTML</abbr> document</p>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Abbreviation\n");
|
||||
TestParser.TestSpec("*[😃 HTML]: Hypertext Markup Language\n\nThis is a 😃 HTML document ", "<p>This is a <abbr title=\"Hypertext Markup Language\">😃 HTML</abbr> document</p>", "abbreviations|advanced");
|
||||
TestParser.TestSpec("*[😃 HTML]: Hypertext Markup Language\n\nThis is a 😃 HTML document ", "<p>This is a <abbr title=\"Hypertext Markup Language\">😃 HTML</abbr> document</p>", "abbreviations|advanced", context: "Example 4\nSection Extensions / Abbreviation\n");
|
||||
}
|
||||
|
||||
// Abbreviations may be similar:
|
||||
@@ -112,8 +108,7 @@ namespace Markdig.Tests.Specs.Abbreviations
|
||||
// Should be rendered as:
|
||||
// <p>We can abbreviate <abbr title="First">1A</abbr>, <abbr title="Second">1A1</abbr> and <abbr title="Third">1A2</abbr>!</p>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / Abbreviation\n");
|
||||
TestParser.TestSpec("*[1A]: First\n*[1A1]: Second\n*[1A2]: Third\n\nWe can abbreviate 1A, 1A1 and 1A2!", "<p>We can abbreviate <abbr title=\"First\">1A</abbr>, <abbr title=\"Second\">1A1</abbr> and <abbr title=\"Third\">1A2</abbr>!</p>", "abbreviations|advanced");
|
||||
TestParser.TestSpec("*[1A]: First\n*[1A1]: Second\n*[1A2]: Third\n\nWe can abbreviate 1A, 1A1 and 1A2!", "<p>We can abbreviate <abbr title=\"First\">1A</abbr>, <abbr title=\"Second\">1A1</abbr> and <abbr title=\"Third\">1A2</abbr>!</p>", "abbreviations|advanced", context: "Example 5\nSection Extensions / Abbreviation\n");
|
||||
}
|
||||
|
||||
// Abbreviations should match whole word only:
|
||||
@@ -131,8 +126,7 @@ namespace Markdig.Tests.Specs.Abbreviations
|
||||
// Should be rendered as:
|
||||
// <p>We should not abbreviate 1.1A or 11A!</p>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / Abbreviation\n");
|
||||
TestParser.TestSpec("*[1A]: First\n\nWe should not abbreviate 1.1A or 11A!", "<p>We should not abbreviate 1.1A or 11A!</p>", "abbreviations|advanced");
|
||||
TestParser.TestSpec("*[1A]: First\n\nWe should not abbreviate 1.1A or 11A!", "<p>We should not abbreviate 1.1A or 11A!</p>", "abbreviations|advanced", context: "Example 6\nSection Extensions / Abbreviation\n");
|
||||
}
|
||||
|
||||
// Abbreviations should match whole word only, even if the word is the entire content:
|
||||
@@ -150,8 +144,7 @@ namespace Markdig.Tests.Specs.Abbreviations
|
||||
// Should be rendered as:
|
||||
// <p>1.1A</p>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Extensions / Abbreviation\n");
|
||||
TestParser.TestSpec("*[1A]: First\n\n1.1A", "<p>1.1A</p>", "abbreviations|advanced");
|
||||
TestParser.TestSpec("*[1A]: First\n\n1.1A", "<p>1.1A</p>", "abbreviations|advanced", context: "Example 7\nSection Extensions / Abbreviation\n");
|
||||
}
|
||||
|
||||
// Abbreviations should match whole word only, even if there is another glossary term:
|
||||
@@ -170,8 +163,7 @@ namespace Markdig.Tests.Specs.Abbreviations
|
||||
// Should be rendered as:
|
||||
// <p><abbr title="Second">SCOM</abbr></p>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Extensions / Abbreviation\n");
|
||||
TestParser.TestSpec("*[SCO]: First\n*[SCOM]: Second\n\nSCOM", "<p><abbr title=\"Second\">SCOM</abbr></p>", "abbreviations|advanced");
|
||||
TestParser.TestSpec("*[SCO]: First\n*[SCOM]: Second\n\nSCOM", "<p><abbr title=\"Second\">SCOM</abbr></p>", "abbreviations|advanced", context: "Example 8\nSection Extensions / Abbreviation\n");
|
||||
}
|
||||
|
||||
// Abbreviations should only match when surrounded by whitespace:
|
||||
@@ -189,8 +181,7 @@ namespace Markdig.Tests.Specs.Abbreviations
|
||||
// Should be rendered as:
|
||||
// <p>PRAA</p>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Extensions / Abbreviation\n");
|
||||
TestParser.TestSpec("*[PR]: Pull Request\n\nPRAA", "<p>PRAA</p>", "abbreviations|advanced");
|
||||
TestParser.TestSpec("*[PR]: Pull Request\n\nPRAA", "<p>PRAA</p>", "abbreviations|advanced", context: "Example 9\nSection Extensions / Abbreviation\n");
|
||||
}
|
||||
|
||||
// Single character abbreviations should be matched
|
||||
@@ -208,8 +199,7 @@ namespace Markdig.Tests.Specs.Abbreviations
|
||||
// Should be rendered as:
|
||||
// <p><abbr title="Foo">A</abbr></p>
|
||||
|
||||
Console.WriteLine("Example 10\nSection Extensions / Abbreviation\n");
|
||||
TestParser.TestSpec("*[A]: Foo\n\nA", "<p><abbr title=\"Foo\">A</abbr></p>", "abbreviations|advanced");
|
||||
TestParser.TestSpec("*[A]: Foo\n\nA", "<p><abbr title=\"Foo\">A</abbr></p>", "abbreviations|advanced", context: "Example 10\nSection Extensions / Abbreviation\n");
|
||||
}
|
||||
|
||||
// The longest matching abbreviation should be used
|
||||
@@ -228,8 +218,7 @@ namespace Markdig.Tests.Specs.Abbreviations
|
||||
// Should be rendered as:
|
||||
// <p><abbr title="foo">Foo</abbr> B</p>
|
||||
|
||||
Console.WriteLine("Example 11\nSection Extensions / Abbreviation\n");
|
||||
TestParser.TestSpec("*[Foo]: foo\n*[Foo Bar]: foobar\n\nFoo B", "<p><abbr title=\"foo\">Foo</abbr> B</p>", "abbreviations|advanced");
|
||||
TestParser.TestSpec("*[Foo]: foo\n*[Foo Bar]: foobar\n\nFoo B", "<p><abbr title=\"foo\">Foo</abbr> B</p>", "abbreviations|advanced", context: "Example 11\nSection Extensions / Abbreviation\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
|
||||
// Should be rendered as:
|
||||
// <h1 id="this-is-a-heading">This is a heading</h1>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Heading Auto Identifiers\n");
|
||||
TestParser.TestSpec("# This is a heading", "<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced");
|
||||
TestParser.TestSpec("# This is a heading", "<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced", context: "Example 1\nSection Extensions / Heading Auto Identifiers\n");
|
||||
}
|
||||
|
||||
// Only punctuation `-`, `_` and `.` is kept, all other non letter characters are discarded.
|
||||
@@ -49,8 +48,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
|
||||
// Should be rendered as:
|
||||
// <h1 id="this-is-a-heading_with.and">This - is a &@! heading _ with . and ! -</h1>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Heading Auto Identifiers\n");
|
||||
TestParser.TestSpec("# This - is a &@! heading _ with . and ! -", "<h1 id=\"this-is-a-heading_with.and\">This - is a &@! heading _ with . and ! -</h1>", "autoidentifiers|advanced");
|
||||
TestParser.TestSpec("# This - is a &@! heading _ with . and ! -", "<h1 id=\"this-is-a-heading_with.and\">This - is a &@! heading _ with . and ! -</h1>", "autoidentifiers|advanced", context: "Example 2\nSection Extensions / Heading Auto Identifiers\n");
|
||||
}
|
||||
|
||||
// Formatting (emphasis) are also discarded:
|
||||
@@ -66,8 +64,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
|
||||
// Should be rendered as:
|
||||
// <h1 id="this-is-a-heading">This is a <em>heading</em></h1>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Heading Auto Identifiers\n");
|
||||
TestParser.TestSpec("# This is a *heading*", "<h1 id=\"this-is-a-heading\">This is a <em>heading</em></h1>", "autoidentifiers|advanced");
|
||||
TestParser.TestSpec("# This is a *heading*", "<h1 id=\"this-is-a-heading\">This is a <em>heading</em></h1>", "autoidentifiers|advanced", context: "Example 3\nSection Extensions / Heading Auto Identifiers\n");
|
||||
}
|
||||
|
||||
// Links are also removed:
|
||||
@@ -83,8 +80,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
|
||||
// Should be rendered as:
|
||||
// <h1 id="this-is-a-heading">This is a <a href="/url">heading</a></h1>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Heading Auto Identifiers\n");
|
||||
TestParser.TestSpec("# This is a [heading](/url)", "<h1 id=\"this-is-a-heading\">This is a <a href=\"/url\">heading</a></h1>", "autoidentifiers|advanced");
|
||||
TestParser.TestSpec("# This is a [heading](/url)", "<h1 id=\"this-is-a-heading\">This is a <a href=\"/url\">heading</a></h1>", "autoidentifiers|advanced", context: "Example 4\nSection Extensions / Heading Auto Identifiers\n");
|
||||
}
|
||||
|
||||
// If multiple heading have the same text, -1, -2...-n will be postfix to the header id.
|
||||
@@ -102,8 +98,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
|
||||
// <h1 id="this-is-a-heading">This is a heading</h1>
|
||||
// <h1 id="this-is-a-heading-1">This is a heading</h1>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / Heading Auto Identifiers\n");
|
||||
TestParser.TestSpec("# This is a heading\n# This is a heading", "<h1 id=\"this-is-a-heading\">This is a heading</h1>\n<h1 id=\"this-is-a-heading-1\">This is a heading</h1>", "autoidentifiers|advanced");
|
||||
TestParser.TestSpec("# This is a heading\n# This is a heading", "<h1 id=\"this-is-a-heading\">This is a heading</h1>\n<h1 id=\"this-is-a-heading-1\">This is a heading</h1>", "autoidentifiers|advanced", context: "Example 5\nSection Extensions / Heading Auto Identifiers\n");
|
||||
}
|
||||
|
||||
// The heading Id will start on the first letter character of the heading, all previous characters will be discarded:
|
||||
@@ -119,8 +114,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
|
||||
// Should be rendered as:
|
||||
// <h1 id="this-is-a-heading">1.0 This is a heading</h1>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / Heading Auto Identifiers\n");
|
||||
TestParser.TestSpec("# 1.0 This is a heading", "<h1 id=\"this-is-a-heading\">1.0 This is a heading</h1>", "autoidentifiers|advanced");
|
||||
TestParser.TestSpec("# 1.0 This is a heading", "<h1 id=\"this-is-a-heading\">1.0 This is a heading</h1>", "autoidentifiers|advanced", context: "Example 6\nSection Extensions / Heading Auto Identifiers\n");
|
||||
}
|
||||
|
||||
// If the heading is all stripped by the previous rules, the id `section` will be used instead:
|
||||
@@ -138,8 +132,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
|
||||
// <h1 id="section">1.0 & ^ % *</h1>
|
||||
// <h1 id="section-1">1.0 & ^ % *</h1>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Extensions / Heading Auto Identifiers\n");
|
||||
TestParser.TestSpec("# 1.0 & ^ % *\n# 1.0 & ^ % *", "<h1 id=\"section\">1.0 & ^ % *</h1>\n<h1 id=\"section-1\">1.0 & ^ % *</h1>", "autoidentifiers|advanced");
|
||||
TestParser.TestSpec("# 1.0 & ^ % *\n# 1.0 & ^ % *", "<h1 id=\"section\">1.0 & ^ % *</h1>\n<h1 id=\"section-1\">1.0 & ^ % *</h1>", "autoidentifiers|advanced", context: "Example 7\nSection Extensions / Heading Auto Identifiers\n");
|
||||
}
|
||||
|
||||
// When the options "AutoLink" is setup, it is possible to link to an existing heading by using the
|
||||
@@ -158,8 +151,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
|
||||
// <h1 id="this-is-a-heading">This is a heading</h1>
|
||||
// <p><a href="#this-is-a-heading">This is a heading</a></p>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Extensions / Heading Auto Identifiers\n");
|
||||
TestParser.TestSpec("# This is a heading\n[This is a heading]", "<h1 id=\"this-is-a-heading\">This is a heading</h1>\n<p><a href=\"#this-is-a-heading\">This is a heading</a></p>", "autoidentifiers|advanced");
|
||||
TestParser.TestSpec("# This is a heading\n[This is a heading]", "<h1 id=\"this-is-a-heading\">This is a heading</h1>\n<p><a href=\"#this-is-a-heading\">This is a heading</a></p>", "autoidentifiers|advanced", context: "Example 8\nSection Extensions / Heading Auto Identifiers\n");
|
||||
}
|
||||
|
||||
// Links before the heading are also working:
|
||||
@@ -177,8 +169,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
|
||||
// <p><a href="#this-is-a-heading">This is a heading</a></p>
|
||||
// <h1 id="this-is-a-heading">This is a heading</h1>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Extensions / Heading Auto Identifiers\n");
|
||||
TestParser.TestSpec("[This is a heading]\n# This is a heading", "<p><a href=\"#this-is-a-heading\">This is a heading</a></p>\n<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced");
|
||||
TestParser.TestSpec("[This is a heading]\n# This is a heading", "<p><a href=\"#this-is-a-heading\">This is a heading</a></p>\n<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced", context: "Example 9\nSection Extensions / Heading Auto Identifiers\n");
|
||||
}
|
||||
|
||||
// The text of the link can be changed:
|
||||
@@ -196,8 +187,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
|
||||
// <p><a href="#this-is-a-heading">With a new text</a></p>
|
||||
// <h1 id="this-is-a-heading">This is a heading</h1>
|
||||
|
||||
Console.WriteLine("Example 10\nSection Extensions / Heading Auto Identifiers\n");
|
||||
TestParser.TestSpec("[With a new text][This is a heading]\n# This is a heading", "<p><a href=\"#this-is-a-heading\">With a new text</a></p>\n<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced");
|
||||
TestParser.TestSpec("[With a new text][This is a heading]\n# This is a heading", "<p><a href=\"#this-is-a-heading\">With a new text</a></p>\n<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced", context: "Example 10\nSection Extensions / Heading Auto Identifiers\n");
|
||||
}
|
||||
|
||||
// An autoidentifier should not conflict with an existing link:
|
||||
@@ -216,8 +206,7 @@ namespace Markdig.Tests.Specs.AutoIdentifiers
|
||||
// <p><img src="./scenario.png" alt="scenario image" /></p>
|
||||
// <h2 id="scenario">Scenario</h2>
|
||||
|
||||
Console.WriteLine("Example 11\nSection Extensions / Heading Auto Identifiers\n");
|
||||
TestParser.TestSpec("![scenario image][scenario]\n## Scenario\n[scenario]: ./scenario.png", "<p><img src=\"./scenario.png\" alt=\"scenario image\" /></p>\n<h2 id=\"scenario\">Scenario</h2>", "autoidentifiers|advanced");
|
||||
TestParser.TestSpec("![scenario image][scenario]\n## Scenario\n[scenario]: ./scenario.png", "<p><img src=\"./scenario.png\" alt=\"scenario image\" /></p>\n<h2 id=\"scenario\">Scenario</h2>", "autoidentifiers|advanced", context: "Example 11\nSection Extensions / Heading Auto Identifiers\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// And a <a href="tel:+1555123456">+1555123456</a>
|
||||
// And a plain <a href="http://www.google.com">www.google.com</a></p>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / AutoLinks\n");
|
||||
TestParser.TestSpec("This is a http://www.google.com URL and https://www.google.com\nThis is a ftp://test.com\nAnd a mailto:email@toto.com\nAnd a tel:+1555123456\nAnd a plain www.google.com", "<p>This is a <a href=\"http://www.google.com\">http://www.google.com</a> URL and <a href=\"https://www.google.com\">https://www.google.com</a>\nThis is a <a href=\"ftp://test.com\">ftp://test.com</a>\nAnd a <a href=\"mailto:email@toto.com\">email@toto.com</a>\nAnd a <a href=\"tel:+1555123456\">+1555123456</a>\nAnd a plain <a href=\"http://www.google.com\">www.google.com</a></p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("This is a http://www.google.com URL and https://www.google.com\nThis is a ftp://test.com\nAnd a mailto:email@toto.com\nAnd a tel:+1555123456\nAnd a plain www.google.com", "<p>This is a <a href=\"http://www.google.com\">http://www.google.com</a> URL and <a href=\"https://www.google.com\">https://www.google.com</a>\nThis is a <a href=\"ftp://test.com\">ftp://test.com</a>\nAnd a <a href=\"mailto:email@toto.com\">email@toto.com</a>\nAnd a <a href=\"tel:+1555123456\">+1555123456</a>\nAnd a plain <a href=\"http://www.google.com\">www.google.com</a></p>", "autolinks|advanced", context: "Example 1\nSection Extensions / AutoLinks\n");
|
||||
}
|
||||
|
||||
// But incomplete links will not be matched:
|
||||
@@ -69,8 +68,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// And not a plain www. or a www.x
|
||||
// And not a tel:</p>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / AutoLinks\n");
|
||||
TestParser.TestSpec("This is not a http:/www.google.com URL and https:/www.google.com\nThis is not a ftp:/test.com\nAnd not a mailto:emailtoto.com\nAnd not a plain www. or a www.x \nAnd not a tel:", "<p>This is not a http:/www.google.com URL and https:/www.google.com\nThis is not a ftp:/test.com\nAnd not a mailto:emailtoto.com\nAnd not a plain www. or a www.x\nAnd not a tel:</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("This is not a http:/www.google.com URL and https:/www.google.com\nThis is not a ftp:/test.com\nAnd not a mailto:emailtoto.com\nAnd not a plain www. or a www.x \nAnd not a tel:", "<p>This is not a http:/www.google.com URL and https:/www.google.com\nThis is not a ftp:/test.com\nAnd not a mailto:emailtoto.com\nAnd not a plain www. or a www.x\nAnd not a tel:</p>", "autolinks|advanced", context: "Example 2\nSection Extensions / AutoLinks\n");
|
||||
}
|
||||
|
||||
// Previous character must be a punctuation or a valid space (tab, space, new line):
|
||||
@@ -86,8 +84,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is not a nhttp://www.google.com URL but this is (<a href="https://www.google.com">https://www.google.com</a>)</p>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / AutoLinks\n");
|
||||
TestParser.TestSpec("This is not a nhttp://www.google.com URL but this is (https://www.google.com)", "<p>This is not a nhttp://www.google.com URL but this is (<a href=\"https://www.google.com\">https://www.google.com</a>)</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("This is not a nhttp://www.google.com URL but this is (https://www.google.com)", "<p>This is not a nhttp://www.google.com URL but this is (<a href=\"https://www.google.com\">https://www.google.com</a>)</p>", "autolinks|advanced", context: "Example 3\nSection Extensions / AutoLinks\n");
|
||||
}
|
||||
|
||||
// An autolink should not interfere with an `<a>` HTML inline:
|
||||
@@ -103,8 +100,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is an HTML <a href="http://www.google.com">http://www.google.com</a> link</p>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / AutoLinks\n");
|
||||
TestParser.TestSpec("This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link", "<p>This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link", "<p>This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link</p>", "autolinks|advanced", context: "Example 4\nSection Extensions / AutoLinks\n");
|
||||
}
|
||||
|
||||
// or even within emphasis:
|
||||
@@ -120,8 +116,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is an HTML <a href="http://www.google.com"> <strong>http://www.google.com</strong> </a> link</p>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / AutoLinks\n");
|
||||
TestParser.TestSpec("This is an HTML <a href=\"http://www.google.com\"> **http://www.google.com** </a> link", "<p>This is an HTML <a href=\"http://www.google.com\"> <strong>http://www.google.com</strong> </a> link</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("This is an HTML <a href=\"http://www.google.com\"> **http://www.google.com** </a> link", "<p>This is an HTML <a href=\"http://www.google.com\"> <strong>http://www.google.com</strong> </a> link</p>", "autolinks|advanced", context: "Example 5\nSection Extensions / AutoLinks\n");
|
||||
}
|
||||
|
||||
// An autolink should not interfere with a markdown link:
|
||||
@@ -137,8 +132,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is an HTML <a href="http://www.google.com">http://www.google.com</a> link</p>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / AutoLinks\n");
|
||||
TestParser.TestSpec("This is an HTML [http://www.google.com](http://www.google.com) link", "<p>This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("This is an HTML [http://www.google.com](http://www.google.com) link", "<p>This is an HTML <a href=\"http://www.google.com\">http://www.google.com</a> link</p>", "autolinks|advanced", context: "Example 6\nSection Extensions / AutoLinks\n");
|
||||
}
|
||||
|
||||
// A link embraced by pending emphasis should let the emphasis takes precedence if characters are placed at the end of the matched link:
|
||||
@@ -154,8 +148,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p>Check <strong><a href="http://www.a.com">http://www.a.com</a></strong> or <strong><a href="http://www.b.com">http://www.b.com</a></strong></p>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Extensions / AutoLinks\n");
|
||||
TestParser.TestSpec("Check **http://www.a.com** or __http://www.b.com__", "<p>Check <strong><a href=\"http://www.a.com\">http://www.a.com</a></strong> or <strong><a href=\"http://www.b.com\">http://www.b.com</a></strong></p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("Check **http://www.a.com** or __http://www.b.com__", "<p>Check <strong><a href=\"http://www.a.com\">http://www.a.com</a></strong> or <strong><a href=\"http://www.b.com\">http://www.b.com</a></strong></p>", "autolinks|advanced", context: "Example 7\nSection Extensions / AutoLinks\n");
|
||||
}
|
||||
|
||||
// It is not mentioned by the spec, but empty emails won't be matched (only a subset of [RFC2368](https://tools.ietf.org/html/rfc2368) is supported by auto links):
|
||||
@@ -171,8 +164,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p><a href="mailto:email@test.com">email@test.com</a> is okay, but mailto:@test.com is not</p>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Extensions / AutoLinks\n");
|
||||
TestParser.TestSpec("mailto:email@test.com is okay, but mailto:@test.com is not", "<p><a href=\"mailto:email@test.com\">email@test.com</a> is okay, but mailto:@test.com is not</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("mailto:email@test.com is okay, but mailto:@test.com is not", "<p><a href=\"mailto:email@test.com\">email@test.com</a> is okay, but mailto:@test.com is not</p>", "autolinks|advanced", context: "Example 8\nSection Extensions / AutoLinks\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,8 +186,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p><a href="http://www.commonmark.org">www.commonmark.org</a></p>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
TestParser.TestSpec("www.commonmark.org", "<p><a href=\"http://www.commonmark.org\">www.commonmark.org</a></p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("www.commonmark.org", "<p><a href=\"http://www.commonmark.org\">www.commonmark.org</a></p>", "autolinks|advanced", context: "Example 9\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -210,8 +201,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p>Visit <a href="http://www.commonmark.org/help">www.commonmark.org/help</a> for more information.</p>
|
||||
|
||||
Console.WriteLine("Example 10\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
TestParser.TestSpec("Visit www.commonmark.org/help for more information.", "<p>Visit <a href=\"http://www.commonmark.org/help\">www.commonmark.org/help</a> for more information.</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("Visit www.commonmark.org/help for more information.", "<p>Visit <a href=\"http://www.commonmark.org/help\">www.commonmark.org/help</a> for more information.</p>", "autolinks|advanced", context: "Example 10\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -229,8 +219,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// <p>Visit <a href="http://www.commonmark.org">www.commonmark.org</a>.</p>
|
||||
// <p>Visit <a href="http://www.commonmark.org/a.b">www.commonmark.org/a.b</a>.</p>
|
||||
|
||||
Console.WriteLine("Example 11\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
TestParser.TestSpec("Visit www.commonmark.org.\n\nVisit www.commonmark.org/a.b.", "<p>Visit <a href=\"http://www.commonmark.org\">www.commonmark.org</a>.</p>\n<p>Visit <a href=\"http://www.commonmark.org/a.b\">www.commonmark.org/a.b</a>.</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("Visit www.commonmark.org.\n\nVisit www.commonmark.org/a.b.", "<p>Visit <a href=\"http://www.commonmark.org\">www.commonmark.org</a>.</p>\n<p>Visit <a href=\"http://www.commonmark.org/a.b\">www.commonmark.org/a.b</a>.</p>", "autolinks|advanced", context: "Example 11\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -248,8 +237,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// <p><a href="http://www.google.com/search?q=Markup+(business)">www.google.com/search?q=Markup+(business)</a></p>
|
||||
// <p>(<a href="http://www.google.com/search?q=Markup+(business)">www.google.com/search?q=Markup+(business)</a>)</p>
|
||||
|
||||
Console.WriteLine("Example 12\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
TestParser.TestSpec("www.google.com/search?q=Markup+(business)\n\n(www.google.com/search?q=Markup+(business))", "<p><a href=\"http://www.google.com/search?q=Markup+(business)\">www.google.com/search?q=Markup+(business)</a></p>\n<p>(<a href=\"http://www.google.com/search?q=Markup+(business)\">www.google.com/search?q=Markup+(business)</a>)</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("www.google.com/search?q=Markup+(business)\n\n(www.google.com/search?q=Markup+(business))", "<p><a href=\"http://www.google.com/search?q=Markup+(business)\">www.google.com/search?q=Markup+(business)</a></p>\n<p>(<a href=\"http://www.google.com/search?q=Markup+(business)\">www.google.com/search?q=Markup+(business)</a>)</p>", "autolinks|advanced", context: "Example 12\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -267,8 +255,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// <p><a href="http://www.google.com/search?q=commonmark&hl=en">www.google.com/search?q=commonmark&hl=en</a></p>
|
||||
// <p><a href="http://www.google.com/search?q=commonmark">www.google.com/search?q=commonmark</a>&hl;</p>
|
||||
|
||||
Console.WriteLine("Example 13\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
TestParser.TestSpec("www.google.com/search?q=commonmark&hl=en\n\nwww.google.com/search?q=commonmark&hl;", "<p><a href=\"http://www.google.com/search?q=commonmark&hl=en\">www.google.com/search?q=commonmark&hl=en</a></p>\n<p><a href=\"http://www.google.com/search?q=commonmark\">www.google.com/search?q=commonmark</a>&hl;</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("www.google.com/search?q=commonmark&hl=en\n\nwww.google.com/search?q=commonmark&hl;", "<p><a href=\"http://www.google.com/search?q=commonmark&hl=en\">www.google.com/search?q=commonmark&hl=en</a></p>\n<p><a href=\"http://www.google.com/search?q=commonmark\">www.google.com/search?q=commonmark</a>&hl;</p>", "autolinks|advanced", context: "Example 13\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -283,8 +270,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p><a href="http://www.commonmark.org/he">www.commonmark.org/he</a><lp</p>
|
||||
|
||||
Console.WriteLine("Example 14\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
TestParser.TestSpec("www.commonmark.org/he<lp", "<p><a href=\"http://www.commonmark.org/he\">www.commonmark.org/he</a><lp</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("www.commonmark.org/he<lp", "<p><a href=\"http://www.commonmark.org/he\">www.commonmark.org/he</a><lp</p>", "autolinks|advanced", context: "Example 14\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -305,8 +291,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// <p>(Visit <a href="https://encrypted.google.com/search?q=Markup+(business)">https://encrypted.google.com/search?q=Markup+(business)</a>)</p>
|
||||
// <p>Anonymous FTP is available at <a href="ftp://foo.bar.baz">ftp://foo.bar.baz</a>.</p>
|
||||
|
||||
Console.WriteLine("Example 15\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
TestParser.TestSpec("http://commonmark.org\n\n(Visit https://encrypted.google.com/search?q=Markup+(business))\n\nAnonymous FTP is available at ftp://foo.bar.baz.", "<p><a href=\"http://commonmark.org\">http://commonmark.org</a></p>\n<p>(Visit <a href=\"https://encrypted.google.com/search?q=Markup+(business)\">https://encrypted.google.com/search?q=Markup+(business)</a>)</p>\n<p>Anonymous FTP is available at <a href=\"ftp://foo.bar.baz\">ftp://foo.bar.baz</a>.</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("http://commonmark.org\n\n(Visit https://encrypted.google.com/search?q=Markup+(business))\n\nAnonymous FTP is available at ftp://foo.bar.baz.", "<p><a href=\"http://commonmark.org\">http://commonmark.org</a></p>\n<p>(Visit <a href=\"https://encrypted.google.com/search?q=Markup+(business)\">https://encrypted.google.com/search?q=Markup+(business)</a>)</p>\n<p>Anonymous FTP is available at <a href=\"ftp://foo.bar.baz\">ftp://foo.bar.baz</a>.</p>", "autolinks|advanced", context: "Example 15\nSection Extensions / AutoLinks / GFM Support\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,8 +331,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// mailto:email@test.
|
||||
// mailto:email@.test</p>
|
||||
|
||||
Console.WriteLine("Example 16\nSection Extensions / AutoLinks / Valid Domain Tests\n");
|
||||
TestParser.TestSpec("www..\nwww..com\nhttp://test.\nhttp://.test\nhttp://.\nhttp://..\nftp://test.\nftp://.test\nmailto:email@test.\nmailto:email@.test", "<p>www..\nwww..com\nhttp://test.\nhttp://.test\nhttp://.\nhttp://..\nftp://test.\nftp://.test\nmailto:email@test.\nmailto:email@.test</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("www..\nwww..com\nhttp://test.\nhttp://.test\nhttp://.\nhttp://..\nftp://test.\nftp://.test\nmailto:email@test.\nmailto:email@.test", "<p>www..\nwww..com\nhttp://test.\nhttp://.test\nhttp://.\nhttp://..\nftp://test.\nftp://.test\nmailto:email@test.\nmailto:email@.test</p>", "autolinks|advanced", context: "Example 16\nSection Extensions / AutoLinks / Valid Domain Tests\n");
|
||||
}
|
||||
|
||||
// Domain names with too few segments won't be matched
|
||||
@@ -371,8 +355,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// ftp://test
|
||||
// mailto:email@test</p>
|
||||
|
||||
Console.WriteLine("Example 17\nSection Extensions / AutoLinks / Valid Domain Tests\n");
|
||||
TestParser.TestSpec("www\nwww.com\nhttp://test\nftp://test\nmailto:email@test", "<p>www\nwww.com\nhttp://test\nftp://test\nmailto:email@test</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("www\nwww.com\nhttp://test\nftp://test\nmailto:email@test", "<p>www\nwww.com\nhttp://test\nftp://test\nmailto:email@test</p>", "autolinks|advanced", context: "Example 17\nSection Extensions / AutoLinks / Valid Domain Tests\n");
|
||||
}
|
||||
|
||||
// Domain names that contain an underscores in the last two segments won't be matched
|
||||
@@ -400,8 +383,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// <p><a href="ftp://test_.foo.bar">ftp://test_.foo.bar</a> is okay, but ftp://test.fo_o is not</p>
|
||||
// <p><a href="mailto:email@_test.foo.bar">email@_test.foo.bar</a> is okay, but mailto:email@_test.foo is not</p>
|
||||
|
||||
Console.WriteLine("Example 18\nSection Extensions / AutoLinks / Valid Domain Tests\n");
|
||||
TestParser.TestSpec("www._test.foo.bar is okay, but www._test.foo is not\n\nhttp://te_st.foo.bar is okay, as is http://test.foo_.bar.foo\n\nBut http://te_st.foo, http://test.foo_.bar and http://test._foo are not\n\nftp://test_.foo.bar is okay, but ftp://test.fo_o is not\n\nmailto:email@_test.foo.bar is okay, but mailto:email@_test.foo is not", "<p><a href=\"http://www._test.foo.bar\">www._test.foo.bar</a> is okay, but www._test.foo is not</p>\n<p><a href=\"http://te_st.foo.bar\">http://te_st.foo.bar</a> is okay, as is <a href=\"http://test.foo_.bar.foo\">http://test.foo_.bar.foo</a></p>\n<p>But http://te_st.foo, http://test.foo_.bar and http://test._foo are not</p>\n<p><a href=\"ftp://test_.foo.bar\">ftp://test_.foo.bar</a> is okay, but ftp://test.fo_o is not</p>\n<p><a href=\"mailto:email@_test.foo.bar\">email@_test.foo.bar</a> is okay, but mailto:email@_test.foo is not</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("www._test.foo.bar is okay, but www._test.foo is not\n\nhttp://te_st.foo.bar is okay, as is http://test.foo_.bar.foo\n\nBut http://te_st.foo, http://test.foo_.bar and http://test._foo are not\n\nftp://test_.foo.bar is okay, but ftp://test.fo_o is not\n\nmailto:email@_test.foo.bar is okay, but mailto:email@_test.foo is not", "<p><a href=\"http://www._test.foo.bar\">www._test.foo.bar</a> is okay, but www._test.foo is not</p>\n<p><a href=\"http://te_st.foo.bar\">http://te_st.foo.bar</a> is okay, as is <a href=\"http://test.foo_.bar.foo\">http://test.foo_.bar.foo</a></p>\n<p>But http://te_st.foo, http://test.foo_.bar and http://test._foo are not</p>\n<p><a href=\"ftp://test_.foo.bar\">ftp://test_.foo.bar</a> is okay, but ftp://test.fo_o is not</p>\n<p><a href=\"mailto:email@_test.foo.bar\">email@_test.foo.bar</a> is okay, but mailto:email@_test.foo is not</p>", "autolinks|advanced", context: "Example 18\nSection Extensions / AutoLinks / Valid Domain Tests\n");
|
||||
}
|
||||
|
||||
// Domain names that contain invalid characters (not AlphaNumberic, -, _ or .) won't be matched
|
||||
@@ -417,8 +399,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p>https://[your-domain]/api</p>
|
||||
|
||||
Console.WriteLine("Example 19\nSection Extensions / AutoLinks / Valid Domain Tests\n");
|
||||
TestParser.TestSpec("https://[your-domain]/api", "<p>https://[your-domain]/api</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("https://[your-domain]/api", "<p>https://[your-domain]/api</p>", "autolinks|advanced", context: "Example 19\nSection Extensions / AutoLinks / Valid Domain Tests\n");
|
||||
}
|
||||
|
||||
// Domain names followed by ?, : or # instead of / are matched
|
||||
@@ -446,8 +427,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// <p><a href="https://github.com">https://github.com</a>:</p>
|
||||
// <p><a href="https://github.com:443">https://github.com:443</a></p>
|
||||
|
||||
Console.WriteLine("Example 20\nSection Extensions / AutoLinks / Valid Domain Tests\n");
|
||||
TestParser.TestSpec("https://github.com?\n\nhttps://github.com?a\n\nhttps://github.com#a\n\nhttps://github.com:\n\nhttps://github.com:443", "<p><a href=\"https://github.com\">https://github.com</a>?</p>\n<p><a href=\"https://github.com?a\">https://github.com?a</a></p>\n<p><a href=\"https://github.com#a\">https://github.com#a</a></p>\n<p><a href=\"https://github.com\">https://github.com</a>:</p>\n<p><a href=\"https://github.com:443\">https://github.com:443</a></p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("https://github.com?\n\nhttps://github.com?a\n\nhttps://github.com#a\n\nhttps://github.com:\n\nhttps://github.com:443", "<p><a href=\"https://github.com\">https://github.com</a>?</p>\n<p><a href=\"https://github.com?a\">https://github.com?a</a></p>\n<p><a href=\"https://github.com#a\">https://github.com#a</a></p>\n<p><a href=\"https://github.com\">https://github.com</a>:</p>\n<p><a href=\"https://github.com:443\">https://github.com:443</a></p>", "autolinks|advanced", context: "Example 20\nSection Extensions / AutoLinks / Valid Domain Tests\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,8 +458,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// <p><a href="http://abc.net#%E2%98%83">http://abc.net#☃</a></p>
|
||||
// <p><a href="http://abc.net/foo#%E2%98%83">http://abc.net/foo#☃</a></p>
|
||||
|
||||
Console.WriteLine("Example 21\nSection Extensions / AutoLinks / Unicode support\n");
|
||||
TestParser.TestSpec("http://abc.net/☃\n\nhttp://abc.net?☃\n\nhttp://abc.net#☃\n\nhttp://abc.net/foo#☃", "<p><a href=\"http://abc.net/%E2%98%83\">http://abc.net/☃</a></p>\n<p><a href=\"http://abc.net?%E2%98%83\">http://abc.net?☃</a></p>\n<p><a href=\"http://abc.net#%E2%98%83\">http://abc.net#☃</a></p>\n<p><a href=\"http://abc.net/foo#%E2%98%83\">http://abc.net/foo#☃</a></p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("http://abc.net/☃\n\nhttp://abc.net?☃\n\nhttp://abc.net#☃\n\nhttp://abc.net/foo#☃", "<p><a href=\"http://abc.net/%E2%98%83\">http://abc.net/☃</a></p>\n<p><a href=\"http://abc.net?%E2%98%83\">http://abc.net?☃</a></p>\n<p><a href=\"http://abc.net#%E2%98%83\">http://abc.net#☃</a></p>\n<p><a href=\"http://abc.net/foo#%E2%98%83\">http://abc.net/foo#☃</a></p>", "autolinks|advanced", context: "Example 21\nSection Extensions / AutoLinks / Unicode support\n");
|
||||
}
|
||||
|
||||
// Unicode characters in the FQDN are matched and IDNA encoded
|
||||
@@ -495,8 +474,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p><a href="http://xn--n3h.net?%E2%98%83">http://☃.net?☃</a></p>
|
||||
|
||||
Console.WriteLine("Example 22\nSection Extensions / AutoLinks / Unicode support\n");
|
||||
TestParser.TestSpec("http://☃.net?☃", "<p><a href=\"http://xn--n3h.net?%E2%98%83\">http://☃.net?☃</a></p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("http://☃.net?☃", "<p><a href=\"http://xn--n3h.net?%E2%98%83\">http://☃.net?☃</a></p>", "autolinks|advanced", context: "Example 22\nSection Extensions / AutoLinks / Unicode support\n");
|
||||
}
|
||||
|
||||
// Same goes for regular autolinks
|
||||
@@ -521,8 +499,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// <p><a href="http://abc.net#%E2%98%83">http://abc.net#☃</a></p>
|
||||
// <p><a href="http://abc.net/foo#%E2%98%83">http://abc.net/foo#☃</a></p>
|
||||
|
||||
Console.WriteLine("Example 23\nSection Extensions / AutoLinks / Unicode support\n");
|
||||
TestParser.TestSpec("<http://abc.net/☃>\n\n<http://abc.net?☃>\n\n<http://abc.net#☃>\n\n<http://abc.net/foo#☃>", "<p><a href=\"http://abc.net/%E2%98%83\">http://abc.net/☃</a></p>\n<p><a href=\"http://abc.net?%E2%98%83\">http://abc.net?☃</a></p>\n<p><a href=\"http://abc.net#%E2%98%83\">http://abc.net#☃</a></p>\n<p><a href=\"http://abc.net/foo#%E2%98%83\">http://abc.net/foo#☃</a></p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("<http://abc.net/☃>\n\n<http://abc.net?☃>\n\n<http://abc.net#☃>\n\n<http://abc.net/foo#☃>", "<p><a href=\"http://abc.net/%E2%98%83\">http://abc.net/☃</a></p>\n<p><a href=\"http://abc.net?%E2%98%83\">http://abc.net?☃</a></p>\n<p><a href=\"http://abc.net#%E2%98%83\">http://abc.net#☃</a></p>\n<p><a href=\"http://abc.net/foo#%E2%98%83\">http://abc.net/foo#☃</a></p>", "autolinks|advanced", context: "Example 23\nSection Extensions / AutoLinks / Unicode support\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -537,8 +514,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p><a href="http://xn--n3h.net?%E2%98%83">http://☃.net?☃</a></p>
|
||||
|
||||
Console.WriteLine("Example 24\nSection Extensions / AutoLinks / Unicode support\n");
|
||||
TestParser.TestSpec("<http://☃.net?☃>", "<p><a href=\"http://xn--n3h.net?%E2%98%83\">http://☃.net?☃</a></p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("<http://☃.net?☃>", "<p><a href=\"http://xn--n3h.net?%E2%98%83\">http://☃.net?☃</a></p>", "autolinks|advanced", context: "Example 24\nSection Extensions / AutoLinks / Unicode support\n");
|
||||
}
|
||||
|
||||
// It also complies with CommonMark's vision of priority.
|
||||
@@ -555,8 +531,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// Should be rendered as:
|
||||
// <p><a href="http://xn--fo-gka.bar.%60baz">http://foö.bar.`baz</a>`</p>
|
||||
|
||||
Console.WriteLine("Example 25\nSection Extensions / AutoLinks / Unicode support\n");
|
||||
TestParser.TestSpec("<http://foö.bar.`baz>`", "<p><a href=\"http://xn--fo-gka.bar.%60baz\">http://foö.bar.`baz</a>`</p>", "autolinks|advanced");
|
||||
TestParser.TestSpec("<http://foö.bar.`baz>`", "<p><a href=\"http://xn--fo-gka.bar.%60baz\">http://foö.bar.`baz</a>`</p>", "autolinks|advanced", context: "Example 25\nSection Extensions / AutoLinks / Unicode support\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,7 @@ namespace Markdig.Tests.Specs.Bootstrap
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Bootstrap\n");
|
||||
TestParser.TestSpec("Name | Value\n-----| -----\nAbc | 16", "<table class=\"table\">\n<thead>\n<tr>\n<th>Name</th>\n<th>Value</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Abc</td>\n<td>16</td>\n</tr>\n</tbody>\n</table>", "bootstrap+pipetables+figures+attributes");
|
||||
TestParser.TestSpec("Name | Value\n-----| -----\nAbc | 16", "<table class=\"table\">\n<thead>\n<tr>\n<th>Name</th>\n<th>Value</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Abc</td>\n<td>16</td>\n</tr>\n</tbody>\n</table>", "bootstrap+pipetables+figures+attributes", context: "Example 1\nSection Extensions / Bootstrap\n");
|
||||
}
|
||||
|
||||
// Adds bootstrap `.blockquote` class to `<blockquote>`:
|
||||
@@ -64,8 +63,7 @@ namespace Markdig.Tests.Specs.Bootstrap
|
||||
// <p>This is a blockquote</p>
|
||||
// </blockquote>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Bootstrap\n");
|
||||
TestParser.TestSpec("> This is a blockquote", "<blockquote class=\"blockquote\">\n<p>This is a blockquote</p>\n</blockquote>", "bootstrap+pipetables+figures+attributes");
|
||||
TestParser.TestSpec("> This is a blockquote", "<blockquote class=\"blockquote\">\n<p>This is a blockquote</p>\n</blockquote>", "bootstrap+pipetables+figures+attributes", context: "Example 2\nSection Extensions / Bootstrap\n");
|
||||
}
|
||||
|
||||
// Adds bootstrap `.figure` class to `<figure>` and `.figure-caption` to `<figcaption>`
|
||||
@@ -86,8 +84,7 @@ namespace Markdig.Tests.Specs.Bootstrap
|
||||
// <figcaption class="figure-caption">This is the caption</figcaption>
|
||||
// </figure>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Bootstrap\n");
|
||||
TestParser.TestSpec("^^^\nThis is a text in a caption\n^^^ This is the caption", "<figure class=\"figure\">\n<p>This is a text in a caption</p>\n<figcaption class=\"figure-caption\">This is the caption</figcaption>\n</figure>", "bootstrap+pipetables+figures+attributes");
|
||||
TestParser.TestSpec("^^^\nThis is a text in a caption\n^^^ This is the caption", "<figure class=\"figure\">\n<p>This is a text in a caption</p>\n<figcaption class=\"figure-caption\">This is the caption</figcaption>\n</figure>", "bootstrap+pipetables+figures+attributes", context: "Example 3\nSection Extensions / Bootstrap\n");
|
||||
}
|
||||
|
||||
// Adds the `.img-fluid` class to all image links `<img>`
|
||||
@@ -103,8 +100,7 @@ namespace Markdig.Tests.Specs.Bootstrap
|
||||
// Should be rendered as:
|
||||
// <p><img src="/url" class="img-fluid" alt="Image Link" /></p>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Bootstrap\n");
|
||||
TestParser.TestSpec("", "<p><img src=\"/url\" class=\"img-fluid\" alt=\"Image Link\" /></p>", "bootstrap+pipetables+figures+attributes");
|
||||
TestParser.TestSpec("", "<p><img src=\"/url\" class=\"img-fluid\" alt=\"Image Link\" /></p>", "bootstrap+pipetables+figures+attributes", context: "Example 4\nSection Extensions / Bootstrap\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -33,8 +33,7 @@ namespace Markdig.Tests.Specs.CustomContainers
|
||||
// <div class="spoiler"><p>This is a <em>spoiler</em></p>
|
||||
// </div>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Custom Container\n");
|
||||
TestParser.TestSpec(":::spoiler\nThis is a *spoiler*\n:::", "<div class=\"spoiler\"><p>This is a <em>spoiler</em></p>\n</div>", "customcontainers+attributes|advanced");
|
||||
TestParser.TestSpec(":::spoiler\nThis is a *spoiler*\n:::", "<div class=\"spoiler\"><p>This is a <em>spoiler</em></p>\n</div>", "customcontainers+attributes|advanced", context: "Example 1\nSection Extensions / Custom Container\n");
|
||||
}
|
||||
|
||||
// The text following the opened custom container is optional:
|
||||
@@ -53,8 +52,7 @@ namespace Markdig.Tests.Specs.CustomContainers
|
||||
// <div><p>This is a regular div</p>
|
||||
// </div>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Custom Container\n");
|
||||
TestParser.TestSpec(":::\nThis is a regular div\n:::", "<div><p>This is a regular div</p>\n</div>", "customcontainers+attributes|advanced");
|
||||
TestParser.TestSpec(":::\nThis is a regular div\n:::", "<div><p>This is a regular div</p>\n</div>", "customcontainers+attributes|advanced", context: "Example 2\nSection Extensions / Custom Container\n");
|
||||
}
|
||||
|
||||
// Like for fenced code block, you can use more than 3 `:` characters as long as the closing has the same number of characters:
|
||||
@@ -73,8 +71,7 @@ namespace Markdig.Tests.Specs.CustomContainers
|
||||
// <div class="spoiler"><p>This is a spoiler</p>
|
||||
// </div>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Custom Container\n");
|
||||
TestParser.TestSpec("::::::::::::spoiler\nThis is a spoiler\n::::::::::::", "<div class=\"spoiler\"><p>This is a spoiler</p>\n</div>", "customcontainers+attributes|advanced");
|
||||
TestParser.TestSpec("::::::::::::spoiler\nThis is a spoiler\n::::::::::::", "<div class=\"spoiler\"><p>This is a spoiler</p>\n</div>", "customcontainers+attributes|advanced", context: "Example 3\nSection Extensions / Custom Container\n");
|
||||
}
|
||||
|
||||
// Like for fenced code block, a custom container can span over multiple empty lines in a list block:
|
||||
@@ -106,8 +103,7 @@ namespace Markdig.Tests.Specs.CustomContainers
|
||||
// <li>A second item in the list</li>
|
||||
// </ul>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Custom Container\n");
|
||||
TestParser.TestSpec("- This is a list\n :::spoiler\n This is a spoiler\n - item1\n - item2\n :::\n- A second item in the list", "<ul>\n<li>This is a list\n<div class=\"spoiler\">This is a spoiler\n<ul>\n<li>item1</li>\n<li>item2</li>\n</ul>\n</div>\n</li>\n<li>A second item in the list</li>\n</ul>", "customcontainers+attributes|advanced");
|
||||
TestParser.TestSpec("- This is a list\n :::spoiler\n This is a spoiler\n - item1\n - item2\n :::\n- A second item in the list", "<ul>\n<li>This is a list\n<div class=\"spoiler\">This is a spoiler\n<ul>\n<li>item1</li>\n<li>item2</li>\n</ul>\n</div>\n</li>\n<li>A second item in the list</li>\n</ul>", "customcontainers+attributes|advanced", context: "Example 4\nSection Extensions / Custom Container\n");
|
||||
}
|
||||
|
||||
// Attributes extension is also supported for Custom Container, as long as the Attributes extension is activated after the CustomContainer extension (`.UseCustomContainer().UseAttributes()`)
|
||||
@@ -126,8 +122,7 @@ namespace Markdig.Tests.Specs.CustomContainers
|
||||
// <div id="myspoiler" class="spoiler" myprop="yes"><p>This is a spoiler</p>
|
||||
// </div>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / Custom Container\n");
|
||||
TestParser.TestSpec(":::spoiler {#myspoiler myprop=yes}\nThis is a spoiler\n:::", "<div id=\"myspoiler\" class=\"spoiler\" myprop=\"yes\"><p>This is a spoiler</p>\n</div>", "customcontainers+attributes|advanced");
|
||||
TestParser.TestSpec(":::spoiler {#myspoiler myprop=yes}\nThis is a spoiler\n:::", "<div id=\"myspoiler\" class=\"spoiler\" myprop=\"yes\"><p>This is a spoiler</p>\n</div>", "customcontainers+attributes|advanced", context: "Example 5\nSection Extensions / Custom Container\n");
|
||||
}
|
||||
|
||||
// The content of a custom container can contain any blocks:
|
||||
@@ -146,8 +141,7 @@ namespace Markdig.Tests.Specs.CustomContainers
|
||||
// <div class="mycontainer"><p>This is a raw spoiler</p>
|
||||
// </div>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / Custom Container\n");
|
||||
TestParser.TestSpec(":::mycontainer\n<p>This is a raw spoiler</p>\n:::", "<div class=\"mycontainer\"><p>This is a raw spoiler</p>\n</div>", "customcontainers+attributes|advanced");
|
||||
TestParser.TestSpec(":::mycontainer\n<p>This is a raw spoiler</p>\n:::", "<div class=\"mycontainer\"><p>This is a raw spoiler</p>\n</div>", "customcontainers+attributes|advanced", context: "Example 6\nSection Extensions / Custom Container\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,8 +163,7 @@ namespace Markdig.Tests.Specs.CustomContainers
|
||||
// Should be rendered as:
|
||||
// <p>This is a text <span>with special emphasis</span></p>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Extensions / Inline Custom Container \n");
|
||||
TestParser.TestSpec("This is a text ::with special emphasis::", "<p>This is a text <span>with special emphasis</span></p>", "customcontainers+attributes|advanced");
|
||||
TestParser.TestSpec("This is a text ::with special emphasis::", "<p>This is a text <span>with special emphasis</span></p>", "customcontainers+attributes|advanced", context: "Example 7\nSection Extensions / Inline Custom Container \n");
|
||||
}
|
||||
|
||||
// Any other emphasis inline can be used within this emphasis inline container:
|
||||
@@ -186,8 +179,7 @@ namespace Markdig.Tests.Specs.CustomContainers
|
||||
// Should be rendered as:
|
||||
// <p>This is a text <span>with special <em>emphasis</em></span></p>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Extensions / Inline Custom Container \n");
|
||||
TestParser.TestSpec("This is a text ::with special *emphasis*::", "<p>This is a text <span>with special <em>emphasis</em></span></p>", "customcontainers+attributes|advanced");
|
||||
TestParser.TestSpec("This is a text ::with special *emphasis*::", "<p>This is a text <span>with special <em>emphasis</em></span></p>", "customcontainers+attributes|advanced", context: "Example 8\nSection Extensions / Inline Custom Container \n");
|
||||
}
|
||||
|
||||
// Attributes can be attached to a inline custom container:
|
||||
@@ -203,8 +195,7 @@ namespace Markdig.Tests.Specs.CustomContainers
|
||||
// Should be rendered as:
|
||||
// <p>This is a text <span id="myId" class="myemphasis">with special emphasis</span></p>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Extensions / Inline Custom Container \n");
|
||||
TestParser.TestSpec("This is a text ::with special emphasis::{#myId .myemphasis}", "<p>This is a text <span id=\"myId\" class=\"myemphasis\">with special emphasis</span></p>", "customcontainers+attributes|advanced");
|
||||
TestParser.TestSpec("This is a text ::with special emphasis::{#myId .myemphasis}", "<p>This is a text <span id=\"myId\" class=\"myemphasis\">with special emphasis</span></p>", "customcontainers+attributes|advanced", context: "Example 9\nSection Extensions / Inline Custom Container \n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,8 +71,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
|
||||
// <dd>This is another definition for term2</dd>
|
||||
// </dl>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Definition lists\n");
|
||||
TestParser.TestSpec("\nTerm 1\n: This is a definition item\n With a paragraph\n > This is a block quote\n\n - This is a list\n - with an item2\n\n ```java\n Test\n\n\n ```\n\n And a last line\n: This ia another definition item\n\nTerm2\nTerm3 *with some inline*\n: This is another definition for term2", "<dl>\n<dt>Term 1</dt>\n<dd><p>This is a definition item\nWith a paragraph</p>\n<blockquote>\n<p>This is a block quote</p>\n</blockquote>\n<ul>\n<li>This is a list</li>\n<li>with an item2</li>\n</ul>\n<pre><code class=\"language-java\">Test\n\n\n</code></pre>\n<p>And a last line</p>\n</dd>\n<dd>This ia another definition item</dd>\n<dt>Term2</dt>\n<dt>Term3 <em>with some inline</em></dt>\n<dd>This is another definition for term2</dd>\n</dl>", "definitionlists+attributes|advanced");
|
||||
TestParser.TestSpec("\nTerm 1\n: This is a definition item\n With a paragraph\n > This is a block quote\n\n - This is a list\n - with an item2\n\n ```java\n Test\n\n\n ```\n\n And a last line\n: This ia another definition item\n\nTerm2\nTerm3 *with some inline*\n: This is another definition for term2", "<dl>\n<dt>Term 1</dt>\n<dd><p>This is a definition item\nWith a paragraph</p>\n<blockquote>\n<p>This is a block quote</p>\n</blockquote>\n<ul>\n<li>This is a list</li>\n<li>with an item2</li>\n</ul>\n<pre><code class=\"language-java\">Test\n\n\n</code></pre>\n<p>And a last line</p>\n</dd>\n<dd>This ia another definition item</dd>\n<dt>Term2</dt>\n<dt>Term3 <em>with some inline</em></dt>\n<dd>This is another definition for term2</dd>\n</dl>", "definitionlists+attributes|advanced", context: "Example 1\nSection Extensions / Definition lists\n");
|
||||
}
|
||||
|
||||
// A definition term can be followed at most by one blank line. Lazy continuations are supported for definitions:
|
||||
@@ -99,8 +98,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
|
||||
// </dd>
|
||||
// </dl>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Definition lists\n");
|
||||
TestParser.TestSpec("Term 1\n\n: Definition\nwith lazy continuation.\n\n Second paragraph of the definition.", "<dl>\n<dt>Term 1</dt>\n<dd><p>Definition\nwith lazy continuation.</p>\n<p>Second paragraph of the definition.</p>\n</dd>\n</dl>", "definitionlists+attributes|advanced");
|
||||
TestParser.TestSpec("Term 1\n\n: Definition\nwith lazy continuation.\n\n Second paragraph of the definition.", "<dl>\n<dt>Term 1</dt>\n<dd><p>Definition\nwith lazy continuation.</p>\n<p>Second paragraph of the definition.</p>\n</dd>\n</dl>", "definitionlists+attributes|advanced", context: "Example 2\nSection Extensions / Definition lists\n");
|
||||
}
|
||||
|
||||
// The definition must be indented to 4 characters including the `:`.
|
||||
@@ -118,8 +116,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
|
||||
// <p>Term 1
|
||||
// : Invalid with less than 3 characters</p>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Definition lists\n");
|
||||
TestParser.TestSpec("Term 1\n: Invalid with less than 3 characters", "<p>Term 1\n: Invalid with less than 3 characters</p>", "definitionlists+attributes|advanced");
|
||||
TestParser.TestSpec("Term 1\n: Invalid with less than 3 characters", "<p>Term 1\n: Invalid with less than 3 characters</p>", "definitionlists+attributes|advanced", context: "Example 3\nSection Extensions / Definition lists\n");
|
||||
}
|
||||
|
||||
// The `:` can be indented up to 3 spaces:
|
||||
@@ -139,8 +136,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
|
||||
// <dd>Valid even if <code>:</code> starts at most 3 spaces</dd>
|
||||
// </dl>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Definition lists\n");
|
||||
TestParser.TestSpec("Term 1\n : Valid even if `:` starts at most 3 spaces", "<dl>\n<dt>Term 1</dt>\n<dd>Valid even if <code>:</code> starts at most 3 spaces</dd>\n</dl>", "definitionlists+attributes|advanced");
|
||||
TestParser.TestSpec("Term 1\n : Valid even if `:` starts at most 3 spaces", "<dl>\n<dt>Term 1</dt>\n<dd>Valid even if <code>:</code> starts at most 3 spaces</dd>\n</dl>", "definitionlists+attributes|advanced", context: "Example 4\nSection Extensions / Definition lists\n");
|
||||
}
|
||||
|
||||
// But more than 3 spaces before `:` will trigger an indented code block:
|
||||
@@ -160,8 +156,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
|
||||
// <pre><code>: Not valid
|
||||
// </code></pre>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / Definition lists\n");
|
||||
TestParser.TestSpec("Term 1\n\n : Not valid", "<p>Term 1</p>\n<pre><code>: Not valid\n</code></pre>", "definitionlists+attributes|advanced");
|
||||
TestParser.TestSpec("Term 1\n\n : Not valid", "<p>Term 1</p>\n<pre><code>: Not valid\n</code></pre>", "definitionlists+attributes|advanced", context: "Example 5\nSection Extensions / Definition lists\n");
|
||||
}
|
||||
|
||||
// Definition lists can be nested inside list items
|
||||
@@ -194,8 +189,7 @@ namespace Markdig.Tests.Specs.DefinitionLists
|
||||
// </dl></li>
|
||||
// </ol>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / Definition lists\n");
|
||||
TestParser.TestSpec("1. First\n \n2. Second\n \n Term 1\n : Definition\n \n Term 2\n : Second Definition", "<ol>\n<li><p>First</p></li>\n<li><p>Second</p>\n<dl>\n<dt>Term 1</dt>\n<dd>Definition</dd>\n<dt>Term 2</dt>\n<dd>Second Definition</dd>\n</dl></li>\n</ol>", "definitionlists+attributes|advanced");
|
||||
TestParser.TestSpec("1. First\n \n2. Second\n \n Term 1\n : Definition\n \n Term 2\n : Second Definition", "<ol>\n<li><p>First</p></li>\n<li><p>Second</p>\n<dl>\n<dt>Term 1</dt>\n<dd>Definition</dd>\n<dt>Term 2</dt>\n<dd>Second Definition</dd>\n</dl></li>\n</ol>", "definitionlists+attributes|advanced", context: "Example 6\nSection Extensions / Definition lists\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,7 @@ namespace Markdig.Tests.Specs.Diagrams
|
||||
// C-->D;
|
||||
// </div>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Mermaid diagrams\n");
|
||||
TestParser.TestSpec("```mermaid\ngraph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n```", "<div class=\"mermaid\">graph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n</div>", "diagrams|advanced");
|
||||
TestParser.TestSpec("```mermaid\ngraph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n```", "<div class=\"mermaid\">graph TD;\n A-->B;\n A-->C;\n B-->D;\n C-->D;\n</div>", "diagrams|advanced", context: "Example 1\nSection Extensions / Mermaid diagrams\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +84,7 @@ namespace Markdig.Tests.Specs.Diagrams
|
||||
// ]
|
||||
// </div>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / nomnoml diagrams\n");
|
||||
TestParser.TestSpec("```nomnoml\n[example|\n propertyA: Int\n propertyB: string\n|\n methodA()\n methodB()\n|\n [subA]--[subB]\n [subA]-:>[sub C]\n]\n```", "<div class=\"nomnoml\">[example|\n propertyA: Int\n propertyB: string\n|\n methodA()\n methodB()\n|\n [subA]--[subB]\n [subA]-:>[sub C]\n]\n</div>", "diagrams|advanced");
|
||||
TestParser.TestSpec("```nomnoml\n[example|\n propertyA: Int\n propertyB: string\n|\n methodA()\n methodB()\n|\n [subA]--[subB]\n [subA]-:>[sub C]\n]\n```", "<div class=\"nomnoml\">[example|\n propertyA: Int\n propertyB: string\n|\n methodA()\n methodB()\n|\n [subA]--[subB]\n [subA]-:>[sub C]\n]\n</div>", "diagrams|advanced", context: "Example 2\nSection Extensions / nomnoml diagrams\n");
|
||||
}
|
||||
// TODO: Add other text diagram languages
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ namespace Markdig.Tests.Specs.Emoji
|
||||
// Should be rendered as:
|
||||
// <p>This is a test with a 😃 and a 😠 smiley</p>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Emoji\n");
|
||||
TestParser.TestSpec("This is a test with a :) and a :angry: smiley", "<p>This is a test with a 😃 and a 😠 smiley</p>", "emojis|advanced+emojis");
|
||||
TestParser.TestSpec("This is a test with a :) and a :angry: smiley", "<p>This is a test with a 😃 and a 😠 smiley</p>", "emojis|advanced+emojis", context: "Example 1\nSection Extensions / Emoji\n");
|
||||
}
|
||||
|
||||
// An emoji needs to be preceded by a space:
|
||||
@@ -47,8 +46,7 @@ namespace Markdig.Tests.Specs.Emoji
|
||||
// Should be rendered as:
|
||||
// <p>These are not:) an emoji with a:) x:angry:x</p>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Emoji\n");
|
||||
TestParser.TestSpec("These are not:) an emoji with a:) x:angry:x", "<p>These are not:) an emoji with a:) x:angry:x</p>", "emojis|advanced+emojis");
|
||||
TestParser.TestSpec("These are not:) an emoji with a:) x:angry:x", "<p>These are not:) an emoji with a:) x:angry:x</p>", "emojis|advanced+emojis", context: "Example 2\nSection Extensions / Emoji\n");
|
||||
}
|
||||
|
||||
// Emojis can be followed by close punctuation (or any other characters):
|
||||
@@ -64,8 +62,7 @@ namespace Markdig.Tests.Specs.Emoji
|
||||
// Should be rendered as:
|
||||
// <p>We all need 😃, it makes us 💪. (and 👌).</p>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Emoji\n");
|
||||
TestParser.TestSpec("We all need :), it makes us :muscle:. (and :ok_hand:).", "<p>We all need 😃, it makes us 💪. (and 👌).</p>", "emojis|advanced+emojis");
|
||||
TestParser.TestSpec("We all need :), it makes us :muscle:. (and :ok_hand:).", "<p>We all need 😃, it makes us 💪. (and 👌).</p>", "emojis|advanced+emojis", context: "Example 3\nSection Extensions / Emoji\n");
|
||||
}
|
||||
|
||||
// Sentences can end with emojis:
|
||||
@@ -83,8 +80,7 @@ namespace Markdig.Tests.Specs.Emoji
|
||||
// <p>This is a sentence 👌
|
||||
// and keeps going to the next line 😃</p>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Emoji\n");
|
||||
TestParser.TestSpec("This is a sentence :ok_hand:\nand keeps going to the next line :)", "<p>This is a sentence 👌\nand keeps going to the next line 😃</p>", "emojis|advanced+emojis");
|
||||
TestParser.TestSpec("This is a sentence :ok_hand:\nand keeps going to the next line :)", "<p>This is a sentence 👌\nand keeps going to the next line 😃</p>", "emojis|advanced+emojis", context: "Example 4\nSection Extensions / Emoji\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ namespace Markdig.Tests.Specs.EmphasisExtra
|
||||
// Should be rendered as:
|
||||
// <p>The following text <del>is deleted</del></p>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Strikethrough\n");
|
||||
TestParser.TestSpec("The following text ~~is deleted~~", "<p>The following text <del>is deleted</del></p>", "emphasisextras|advanced");
|
||||
TestParser.TestSpec("The following text ~~is deleted~~", "<p>The following text <del>is deleted</del></p>", "emphasisextras|advanced", context: "Example 1\nSection Extensions / Strikethrough\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +52,7 @@ namespace Markdig.Tests.Specs.EmphasisExtra
|
||||
// Should be rendered as:
|
||||
// <p>H<sub>2</sub>O is a liquid. 2<sup>10</sup> is 1024</p>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Superscript and Subscript\n");
|
||||
TestParser.TestSpec("H~2~O is a liquid. 2^10^ is 1024", "<p>H<sub>2</sub>O is a liquid. 2<sup>10</sup> is 1024</p>", "emphasisextras|advanced");
|
||||
TestParser.TestSpec("H~2~O is a liquid. 2^10^ is 1024", "<p>H<sub>2</sub>O is a liquid. 2<sup>10</sup> is 1024</p>", "emphasisextras|advanced", context: "Example 2\nSection Extensions / Superscript and Subscript\n");
|
||||
}
|
||||
|
||||
// Certain punctuation characters are exempted from the rule forbidding them within inline delimiters
|
||||
@@ -73,8 +71,7 @@ namespace Markdig.Tests.Specs.EmphasisExtra
|
||||
// <p>One quintillionth can be expressed as 10<sup>-18</sup></p>
|
||||
// <p>Daggers<sup>†</sup> and double-daggers<sup>‡</sup> can be used to denote notes.</p>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Superscript and Subscript\n");
|
||||
TestParser.TestSpec("One quintillionth can be expressed as 10^-18^\n\nDaggers^†^ and double-daggers^‡^ can be used to denote notes.", "<p>One quintillionth can be expressed as 10<sup>-18</sup></p>\n<p>Daggers<sup>†</sup> and double-daggers<sup>‡</sup> can be used to denote notes.</p>", "emphasisextras|advanced");
|
||||
TestParser.TestSpec("One quintillionth can be expressed as 10^-18^\n\nDaggers^†^ and double-daggers^‡^ can be used to denote notes.", "<p>One quintillionth can be expressed as 10<sup>-18</sup></p>\n<p>Daggers<sup>†</sup> and double-daggers<sup>‡</sup> can be used to denote notes.</p>", "emphasisextras|advanced", context: "Example 3\nSection Extensions / Superscript and Subscript\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,8 +93,7 @@ namespace Markdig.Tests.Specs.EmphasisExtra
|
||||
// Should be rendered as:
|
||||
// <p><ins>Inserted text</ins></p>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Inserted\n");
|
||||
TestParser.TestSpec("++Inserted text++", "<p><ins>Inserted text</ins></p>", "emphasisextras|advanced");
|
||||
TestParser.TestSpec("++Inserted text++", "<p><ins>Inserted text</ins></p>", "emphasisextras|advanced", context: "Example 4\nSection Extensions / Inserted\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +115,7 @@ namespace Markdig.Tests.Specs.EmphasisExtra
|
||||
// Should be rendered as:
|
||||
// <p><mark>Marked text</mark></p>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / Marked\n");
|
||||
TestParser.TestSpec("==Marked text==", "<p><mark>Marked text</mark></p>", "emphasisextras|advanced");
|
||||
TestParser.TestSpec("==Marked text==", "<p><mark>Marked text</mark></p>", "emphasisextras|advanced", context: "Example 5\nSection Extensions / Marked\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,8 +139,7 @@ namespace Markdig.Tests.Specs.EmphasisExtra
|
||||
// This is text MyBrand<sup>®</sup> and MyTrademark<sup>TM</sup>
|
||||
// This is text MyBrand<sub>®</sub> and MyCopyright<sup>©</sup></p>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / Emphasis on Html Entities\n");
|
||||
TestParser.TestSpec("This is text MyBrand ^®^ and MyTrademark ^™^\nThis is text MyBrand^®^ and MyTrademark^™^\nThis is text MyBrand~®~ and MyCopyright^©^", "<p>This is text MyBrand <sup>®</sup> and MyTrademark <sup>TM</sup>\nThis is text MyBrand<sup>®</sup> and MyTrademark<sup>TM</sup>\nThis is text MyBrand<sub>®</sub> and MyCopyright<sup>©</sup></p>", "emphasisextras|advanced");
|
||||
TestParser.TestSpec("This is text MyBrand ^®^ and MyTrademark ^™^\nThis is text MyBrand^®^ and MyTrademark^™^\nThis is text MyBrand~®~ and MyCopyright^©^", "<p>This is text MyBrand <sup>®</sup> and MyTrademark <sup>TM</sup>\nThis is text MyBrand<sup>®</sup> and MyTrademark<sup>TM</sup>\nThis is text MyBrand<sub>®</sub> and MyCopyright<sup>©</sup></p>", "emphasisextras|advanced", context: "Example 6\nSection Extensions / Emphasis on Html Entities\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,7 @@ namespace Markdig.Tests.Specs.FiguresFootersAndCites
|
||||
// <figcaption>This is a <em>caption</em></figcaption>
|
||||
// </figure>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Figures\n");
|
||||
TestParser.TestSpec("^^^\nThis is a figure\n^^^ This is a *caption*", "<figure>\n<p>This is a figure</p>\n<figcaption>This is a <em>caption</em></figcaption>\n</figure>", "figures+footers+citations|advanced");
|
||||
TestParser.TestSpec("^^^\nThis is a figure\n^^^ This is a *caption*", "<figure>\n<p>This is a figure</p>\n<figcaption>This is a <em>caption</em></figcaption>\n</figure>", "figures+footers+citations|advanced", context: "Example 1\nSection Extensions / Figures\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,8 +59,7 @@ namespace Markdig.Tests.Specs.FiguresFootersAndCites
|
||||
// <footer>This is a footer
|
||||
// multi-line</footer>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Footers\n");
|
||||
TestParser.TestSpec("^^ This is a footer\n^^ multi-line", "<footer>This is a footer\nmulti-line</footer>", "figures+footers+citations|advanced");
|
||||
TestParser.TestSpec("^^ This is a footer\n^^ multi-line", "<footer>This is a footer\nmulti-line</footer>", "figures+footers+citations|advanced", context: "Example 2\nSection Extensions / Footers\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,8 +81,7 @@ namespace Markdig.Tests.Specs.FiguresFootersAndCites
|
||||
// Should be rendered as:
|
||||
// <p>This is a <cite>citation of someone</cite></p>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Cite\n");
|
||||
TestParser.TestSpec("This is a \"\"citation of someone\"\"", "<p>This is a <cite>citation of someone</cite></p>", "figures+footers+citations|advanced");
|
||||
TestParser.TestSpec("This is a \"\"citation of someone\"\"", "<p>This is a <cite>citation of someone</cite></p>", "figures+footers+citations|advanced", context: "Example 3\nSection Extensions / Cite\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +79,7 @@ namespace Markdig.Tests.Specs.Footnotes
|
||||
// </ol>
|
||||
// </div>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Footnotes\n");
|
||||
TestParser.TestSpec("Here is a footnote reference,[^1] and another.[^longnote]\n\nThis is another reference to [^1]\n\n[^1]: Here is the footnote.\n\nAnd another reference to [^longnote]\n\n[^longnote]: Here's one with multiple blocks.\n\n Subsequent paragraphs are indented to show that they\nbelong to the previous footnote.\n\n > This is a block quote\n > Inside a footnote\n\n { some.code }\n\n The whole paragraph can be indented, or just the first\n line. In this way, multi-paragraph footnotes work like\n multi-paragraph list items.\n\nThis paragraph won't be part of the note, because it\nisn't indented.", "<p>Here is a footnote reference,<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a> and another.<a id=\"fnref:3\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a></p>\n<p>This is another reference to <a id=\"fnref:2\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a></p>\n<p>And another reference to <a id=\"fnref:4\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a></p>\n<p>This paragraph won't be part of the note, because it\nisn't indented.</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Here is the footnote.<a href=\"#fnref:1\" class=\"footnote-back-ref\">↩</a><a href=\"#fnref:2\" class=\"footnote-back-ref\">↩</a></p>\n</li>\n<li id=\"fn:2\">\n<p>Here's one with multiple blocks.</p>\n<p>Subsequent paragraphs are indented to show that they\nbelong to the previous footnote.</p>\n<blockquote>\n<p>This is a block quote\nInside a footnote</p>\n</blockquote>\n<pre><code>{ some.code }\n</code></pre>\n<p>The whole paragraph can be indented, or just the first\nline. In this way, multi-paragraph footnotes work like\nmulti-paragraph list items.<a href=\"#fnref:3\" class=\"footnote-back-ref\">↩</a><a href=\"#fnref:4\" class=\"footnote-back-ref\">↩</a></p>\n</li>\n</ol>\n</div>", "footnotes|advanced");
|
||||
TestParser.TestSpec("Here is a footnote reference,[^1] and another.[^longnote]\n\nThis is another reference to [^1]\n\n[^1]: Here is the footnote.\n\nAnd another reference to [^longnote]\n\n[^longnote]: Here's one with multiple blocks.\n\n Subsequent paragraphs are indented to show that they\nbelong to the previous footnote.\n\n > This is a block quote\n > Inside a footnote\n\n { some.code }\n\n The whole paragraph can be indented, or just the first\n line. In this way, multi-paragraph footnotes work like\n multi-paragraph list items.\n\nThis paragraph won't be part of the note, because it\nisn't indented.", "<p>Here is a footnote reference,<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a> and another.<a id=\"fnref:3\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a></p>\n<p>This is another reference to <a id=\"fnref:2\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a></p>\n<p>And another reference to <a id=\"fnref:4\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a></p>\n<p>This paragraph won't be part of the note, because it\nisn't indented.</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Here is the footnote.<a href=\"#fnref:1\" class=\"footnote-back-ref\">↩</a><a href=\"#fnref:2\" class=\"footnote-back-ref\">↩</a></p>\n</li>\n<li id=\"fn:2\">\n<p>Here's one with multiple blocks.</p>\n<p>Subsequent paragraphs are indented to show that they\nbelong to the previous footnote.</p>\n<blockquote>\n<p>This is a block quote\nInside a footnote</p>\n</blockquote>\n<pre><code>{ some.code }\n</code></pre>\n<p>The whole paragraph can be indented, or just the first\nline. In this way, multi-paragraph footnotes work like\nmulti-paragraph list items.<a href=\"#fnref:3\" class=\"footnote-back-ref\">↩</a><a href=\"#fnref:4\" class=\"footnote-back-ref\">↩</a></p>\n</li>\n</ol>\n</div>", "footnotes|advanced", context: "Example 1\nSection Extensions / Footnotes\n");
|
||||
}
|
||||
|
||||
// Check with multiple consecutive footnotes:
|
||||
@@ -120,8 +119,7 @@ namespace Markdig.Tests.Specs.Footnotes
|
||||
// </ol>
|
||||
// </div>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Footnotes\n");
|
||||
TestParser.TestSpec("Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].\n\n[^1]: Footnote 1 text\n\n[^2]: Footnote 2 text\n\na\n\n[^3]: Footnote 3 text\n\n[^4]: Footnote 4 text", "<p>Here is a footnote<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a>. And another one<a id=\"fnref:2\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a>. And a third one<a id=\"fnref:3\" href=\"#fn:3\" class=\"footnote-ref\"><sup>3</sup></a>. And a fourth<a id=\"fnref:4\" href=\"#fn:4\" class=\"footnote-ref\"><sup>4</sup></a>.</p>\n<p>a</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Footnote 1 text<a href=\"#fnref:1\" class=\"footnote-back-ref\">↩</a></p></li>\n<li id=\"fn:2\">\n<p>Footnote 2 text<a href=\"#fnref:2\" class=\"footnote-back-ref\">↩</a></p></li>\n<li id=\"fn:3\">\n<p>Footnote 3 text<a href=\"#fnref:3\" class=\"footnote-back-ref\">↩</a></p></li>\n<li id=\"fn:4\">\n<p>Footnote 4 text<a href=\"#fnref:4\" class=\"footnote-back-ref\">↩</a></p></li>\n</ol>\n</div>", "footnotes|advanced");
|
||||
TestParser.TestSpec("Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].\n\n[^1]: Footnote 1 text\n\n[^2]: Footnote 2 text\n\na\n\n[^3]: Footnote 3 text\n\n[^4]: Footnote 4 text", "<p>Here is a footnote<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a>. And another one<a id=\"fnref:2\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a>. And a third one<a id=\"fnref:3\" href=\"#fn:3\" class=\"footnote-ref\"><sup>3</sup></a>. And a fourth<a id=\"fnref:4\" href=\"#fn:4\" class=\"footnote-ref\"><sup>4</sup></a>.</p>\n<p>a</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Footnote 1 text<a href=\"#fnref:1\" class=\"footnote-back-ref\">↩</a></p></li>\n<li id=\"fn:2\">\n<p>Footnote 2 text<a href=\"#fnref:2\" class=\"footnote-back-ref\">↩</a></p></li>\n<li id=\"fn:3\">\n<p>Footnote 3 text<a href=\"#fnref:3\" class=\"footnote-back-ref\">↩</a></p></li>\n<li id=\"fn:4\">\n<p>Footnote 4 text<a href=\"#fnref:4\" class=\"footnote-back-ref\">↩</a></p></li>\n</ol>\n</div>", "footnotes|advanced", context: "Example 2\nSection Extensions / Footnotes\n");
|
||||
}
|
||||
|
||||
// Another test with consecutive footnotes without a blank line separator:
|
||||
@@ -155,8 +153,7 @@ namespace Markdig.Tests.Specs.Footnotes
|
||||
// </ol>
|
||||
// </div>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Footnotes\n");
|
||||
TestParser.TestSpec("Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].\n\n[^1]: Footnote 1 text\n[^2]: Footnote 2 text\n[^3]: Footnote 3 text\n[^4]: Footnote 4 text", "<p>Here is a footnote<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a>. And another one<a id=\"fnref:2\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a>. And a third one<a id=\"fnref:3\" href=\"#fn:3\" class=\"footnote-ref\"><sup>3</sup></a>. And a fourth<a id=\"fnref:4\" href=\"#fn:4\" class=\"footnote-ref\"><sup>4</sup></a>.</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Footnote 1 text<a href=\"#fnref:1\" class=\"footnote-back-ref\">↩</a></p></li>\n<li id=\"fn:2\">\n<p>Footnote 2 text<a href=\"#fnref:2\" class=\"footnote-back-ref\">↩</a></p></li>\n<li id=\"fn:3\">\n<p>Footnote 3 text<a href=\"#fnref:3\" class=\"footnote-back-ref\">↩</a></p></li>\n<li id=\"fn:4\">\n<p>Footnote 4 text<a href=\"#fnref:4\" class=\"footnote-back-ref\">↩</a></p></li>\n</ol>\n</div>", "footnotes|advanced");
|
||||
TestParser.TestSpec("Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].\n\n[^1]: Footnote 1 text\n[^2]: Footnote 2 text\n[^3]: Footnote 3 text\n[^4]: Footnote 4 text", "<p>Here is a footnote<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a>. And another one<a id=\"fnref:2\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a>. And a third one<a id=\"fnref:3\" href=\"#fn:3\" class=\"footnote-ref\"><sup>3</sup></a>. And a fourth<a id=\"fnref:4\" href=\"#fn:4\" class=\"footnote-ref\"><sup>4</sup></a>.</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Footnote 1 text<a href=\"#fnref:1\" class=\"footnote-back-ref\">↩</a></p></li>\n<li id=\"fn:2\">\n<p>Footnote 2 text<a href=\"#fnref:2\" class=\"footnote-back-ref\">↩</a></p></li>\n<li id=\"fn:3\">\n<p>Footnote 3 text<a href=\"#fnref:3\" class=\"footnote-back-ref\">↩</a></p></li>\n<li id=\"fn:4\">\n<p>Footnote 4 text<a href=\"#fnref:4\" class=\"footnote-back-ref\">↩</a></p></li>\n</ol>\n</div>", "footnotes|advanced", context: "Example 3\nSection Extensions / Footnotes\n");
|
||||
}
|
||||
|
||||
// A footnote link inside a list should work as well:
|
||||
@@ -185,8 +182,7 @@ namespace Markdig.Tests.Specs.Footnotes
|
||||
// </ol>
|
||||
// </div>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Footnotes\n");
|
||||
TestParser.TestSpec("- abc\n- def[^1]\n\n[^1]: Here is the footnote.", "<ul>\n<li>abc</li>\n<li>def<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a></li>\n</ul>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Here is the footnote.<a href=\"#fnref:1\" class=\"footnote-back-ref\">↩</a></p></li>\n</ol>\n</div>", "footnotes|advanced");
|
||||
TestParser.TestSpec("- abc\n- def[^1]\n\n[^1]: Here is the footnote.", "<ul>\n<li>abc</li>\n<li>def<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a></li>\n</ul>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Here is the footnote.<a href=\"#fnref:1\" class=\"footnote-back-ref\">↩</a></p></li>\n</ol>\n</div>", "footnotes|advanced", context: "Example 4\nSection Extensions / Footnotes\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +54,7 @@ namespace Markdig.Tests.Specs.GenericAttributes
|
||||
// <h2 id="heading-link2">This is a heading</h2>
|
||||
// <p id="myparagraph" attached-bool-property="" attached-bool-property2="">This is a paragraph with an attached attributes </p>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Generic Attributes\n");
|
||||
TestParser.TestSpec("# This is a heading with an an attribute{#heading-link}\n\n# This is a heading # {#heading-link2}\n\n[This is a link](http://google.com){#a-link .myclass data-lang=fr data-value=\"This is a value\"}\n\nThis is a heading{#heading-link2}\n-----------------\n\nThis is a paragraph with an attached attributes {#myparagraph attached-bool-property attached-bool-property2}", "<h1 id=\"heading-link\">This is a heading with an an attribute</h1>\n<h1 id=\"heading-link2\">This is a heading</h1>\n<p><a href=\"http://google.com\" id=\"a-link\" class=\"myclass\" data-lang=\"fr\" data-value=\"This is a value\">This is a link</a></p>\n<h2 id=\"heading-link2\">This is a heading</h2>\n<p id=\"myparagraph\" attached-bool-property=\"\" attached-bool-property2=\"\">This is a paragraph with an attached attributes </p>", "attributes|advanced");
|
||||
TestParser.TestSpec("# This is a heading with an an attribute{#heading-link}\n\n# This is a heading # {#heading-link2}\n\n[This is a link](http://google.com){#a-link .myclass data-lang=fr data-value=\"This is a value\"}\n\nThis is a heading{#heading-link2}\n-----------------\n\nThis is a paragraph with an attached attributes {#myparagraph attached-bool-property attached-bool-property2}", "<h1 id=\"heading-link\">This is a heading with an an attribute</h1>\n<h1 id=\"heading-link2\">This is a heading</h1>\n<p><a href=\"http://google.com\" id=\"a-link\" class=\"myclass\" data-lang=\"fr\" data-value=\"This is a value\">This is a link</a></p>\n<h2 id=\"heading-link2\">This is a heading</h2>\n<p id=\"myparagraph\" attached-bool-property=\"\" attached-bool-property2=\"\">This is a paragraph with an attached attributes </p>", "attributes|advanced", context: "Example 1\nSection Extensions / Generic Attributes\n");
|
||||
}
|
||||
|
||||
// The following shows that attributes can be attached to the next block if they are used inside a single line just preceding the block (and preceded by a blank line or beginning of a block container):
|
||||
@@ -75,8 +74,7 @@ namespace Markdig.Tests.Specs.GenericAttributes
|
||||
// <pre><code id="fenced-id" class="fenced-class">This is a fenced with attached attributes
|
||||
// </code></pre>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Generic Attributes\n");
|
||||
TestParser.TestSpec("{#fenced-id .fenced-class}\n~~~\nThis is a fenced with attached attributes\n~~~ ", "<pre><code id=\"fenced-id\" class=\"fenced-class\">This is a fenced with attached attributes\n</code></pre>", "attributes|advanced");
|
||||
TestParser.TestSpec("{#fenced-id .fenced-class}\n~~~\nThis is a fenced with attached attributes\n~~~ ", "<pre><code id=\"fenced-id\" class=\"fenced-class\">This is a fenced with attached attributes\n</code></pre>", "attributes|advanced", context: "Example 2\nSection Extensions / Generic Attributes\n");
|
||||
}
|
||||
|
||||
// Attribute values can be one character long
|
||||
@@ -98,8 +96,7 @@ namespace Markdig.Tests.Specs.GenericAttributes
|
||||
// <p><a href="url" data-x="1">Foo</a></p>
|
||||
// <p><a href="url" data-x="11">Foo</a></p>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Generic Attributes\n");
|
||||
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");
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +56,7 @@ namespace Markdig.Tests.Specs.Globalization
|
||||
// -- نەزانراو</p>
|
||||
// </blockquote>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Globalization\n");
|
||||
TestParser.TestSpec("# Fruits\nIn botany, a [fruit](https://en.wikipedia.org/wiki/Fruit) is the seed-bearing structure in flowering plants (also known as angiosperms) formed from the ovary after flowering.\n\n> Fruits are good for health\n-- Anonymous\n\n# میوە\n[میوە](https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95) یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو\n\n> میوە بۆ تەندروستی باشە\n-- نەزانراو", "<h1 id=\"fruits\">Fruits</h1>\n<p>In botany, a <a href=\"https://en.wikipedia.org/wiki/Fruit\">fruit</a> is the seed-bearing structure in flowering plants (also known as angiosperms) formed from the ovary after flowering.</p>\n<blockquote>\n<p>Fruits are good for health\n-- Anonymous</p>\n</blockquote>\n<h1 id=\"section\" dir=\"rtl\">میوە</h1>\n<p dir=\"rtl\"><a href=\"https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95\" dir=\"rtl\">میوە</a> یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو</p>\n<blockquote dir=\"rtl\">\n<p dir=\"rtl\">میوە بۆ تەندروستی باشە\n-- نەزانراو</p>\n</blockquote>", "globalization+advanced+emojis");
|
||||
TestParser.TestSpec("# Fruits\nIn botany, a [fruit](https://en.wikipedia.org/wiki/Fruit) is the seed-bearing structure in flowering plants (also known as angiosperms) formed from the ovary after flowering.\n\n> Fruits are good for health\n-- Anonymous\n\n# میوە\n[میوە](https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95) یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو\n\n> میوە بۆ تەندروستی باشە\n-- نەزانراو", "<h1 id=\"fruits\">Fruits</h1>\n<p>In botany, a <a href=\"https://en.wikipedia.org/wiki/Fruit\">fruit</a> is the seed-bearing structure in flowering plants (also known as angiosperms) formed from the ovary after flowering.</p>\n<blockquote>\n<p>Fruits are good for health\n-- Anonymous</p>\n</blockquote>\n<h1 id=\"section\" dir=\"rtl\">میوە</h1>\n<p dir=\"rtl\"><a href=\"https://ckb.wikipedia.org/wiki/%D9%85%DB%8C%D9%88%DB%95\" dir=\"rtl\">میوە</a> یان مێوە بەروبوومی ڕوەکیە کە ڕوەکەکان ھەڵیان ئەگرن وەک بەرگێک بۆ تۆوەکانیان، بە زۆری جیادەکرێتەوە بە شیرینی یان ترشی لە تامدا و بە بوونی بڕێکی زۆر ئاو</p>\n<blockquote dir=\"rtl\">\n<p dir=\"rtl\">میوە بۆ تەندروستی باشە\n-- نەزانراو</p>\n</blockquote>", "globalization+advanced+emojis", context: "Example 1\nSection Extensions / Globalization\n");
|
||||
}
|
||||
|
||||
// Lists:
|
||||
@@ -160,8 +159,7 @@ namespace Markdig.Tests.Specs.Globalization
|
||||
// <li class="task-list-item"><input disabled="disabled" type="checkbox" /> هەنجیر</li>
|
||||
// </ul>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Globalization\n");
|
||||
TestParser.TestSpec("## Types of fruits\n- Berries\n - Strawberry\n - kiwifruit\n- Citrus\n - Orange\n - Lemon\n\n## Examples of fruits :yum:\n1. Apple\n2. Banana\n3. Orange\n\n## Grocery List\n- [X] Watermelon\n- [X] Apricot\n- [ ] Fig \n\n## نموونەی میوە :yum:\n1. ? سێو\n2. 5 مۆز \n3. پرتەقاڵ\n\n## جۆرەکانی میوە\n- توو\n - فڕاولە\n - کیوی\n- مزرەمەنی\n - پڕتەقاڵ\n - لیمۆ\n\n## لیستی کڕین\n- [X] شووتی\n- [X] قەیسی\n- [ ] هەنجیر", "<h2 id=\"types-of-fruits\">Types of fruits</h2>\n<ul>\n<li>Berries\n<ul>\n<li>Strawberry</li>\n<li>kiwifruit</li>\n</ul>\n</li>\n<li>Citrus\n<ul>\n<li>Orange</li>\n<li>Lemon</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"examples-of-fruits\">Examples of fruits 😋</h2>\n<ol>\n<li>Apple</li>\n<li>Banana</li>\n<li>Orange</li>\n</ol>\n<h2 id=\"grocery-list\">Grocery List</h2>\n<ul class=\"contains-task-list\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> Watermelon</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> Apricot</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> Fig</li>\n</ul>\n<h2 id=\"section\" dir=\"rtl\">نموونەی میوە 😋</h2>\n<ol dir=\"rtl\">\n<li>? سێو</li>\n<li>5 مۆز</li>\n<li> پرتەقاڵ</li>\n</ol>\n<h2 id=\"section-1\" dir=\"rtl\">جۆرەکانی میوە</h2>\n<ul dir=\"rtl\">\n<li>توو\n<ul dir=\"rtl\">\n<li>فڕاولە</li>\n<li>کیوی</li>\n</ul>\n</li>\n<li>مزرەمەنی\n<ul dir=\"rtl\">\n<li>پڕتەقاڵ</li>\n<li>لیمۆ</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"section-2\" dir=\"rtl\">لیستی کڕین</h2>\n<ul class=\"contains-task-list\" dir=\"rtl\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> شووتی</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> قەیسی</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> هەنجیر</li>\n</ul>", "globalization+advanced+emojis");
|
||||
TestParser.TestSpec("## Types of fruits\n- Berries\n - Strawberry\n - kiwifruit\n- Citrus\n - Orange\n - Lemon\n\n## Examples of fruits :yum:\n1. Apple\n2. Banana\n3. Orange\n\n## Grocery List\n- [X] Watermelon\n- [X] Apricot\n- [ ] Fig \n\n## نموونەی میوە :yum:\n1. ? سێو\n2. 5 مۆز \n3. پرتەقاڵ\n\n## جۆرەکانی میوە\n- توو\n - فڕاولە\n - کیوی\n- مزرەمەنی\n - پڕتەقاڵ\n - لیمۆ\n\n## لیستی کڕین\n- [X] شووتی\n- [X] قەیسی\n- [ ] هەنجیر", "<h2 id=\"types-of-fruits\">Types of fruits</h2>\n<ul>\n<li>Berries\n<ul>\n<li>Strawberry</li>\n<li>kiwifruit</li>\n</ul>\n</li>\n<li>Citrus\n<ul>\n<li>Orange</li>\n<li>Lemon</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"examples-of-fruits\">Examples of fruits 😋</h2>\n<ol>\n<li>Apple</li>\n<li>Banana</li>\n<li>Orange</li>\n</ol>\n<h2 id=\"grocery-list\">Grocery List</h2>\n<ul class=\"contains-task-list\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> Watermelon</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> Apricot</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> Fig</li>\n</ul>\n<h2 id=\"section\" dir=\"rtl\">نموونەی میوە 😋</h2>\n<ol dir=\"rtl\">\n<li>? سێو</li>\n<li>5 مۆز</li>\n<li> پرتەقاڵ</li>\n</ol>\n<h2 id=\"section-1\" dir=\"rtl\">جۆرەکانی میوە</h2>\n<ul dir=\"rtl\">\n<li>توو\n<ul dir=\"rtl\">\n<li>فڕاولە</li>\n<li>کیوی</li>\n</ul>\n</li>\n<li>مزرەمەنی\n<ul dir=\"rtl\">\n<li>پڕتەقاڵ</li>\n<li>لیمۆ</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"section-2\" dir=\"rtl\">لیستی کڕین</h2>\n<ul class=\"contains-task-list\" dir=\"rtl\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> شووتی</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> قەیسی</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> هەنجیر</li>\n</ul>", "globalization+advanced+emojis", context: "Example 2\nSection Extensions / Globalization\n");
|
||||
}
|
||||
|
||||
// Tables:
|
||||
@@ -226,8 +224,29 @@ namespace Markdig.Tests.Specs.Globalization
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Globalization\n");
|
||||
TestParser.TestSpec("Nutrition |Apple | Oranges\n--|-- | --\nCalories|52|47\nSugar|10g|9g\n\n پێکهاتە |سێو | پڕتەقاڵ\n--|-- | --\nکالۆری|٥٢|٤٧\nشەکر| ١٠گ|٩گ", "<table>\n<thead>\n<tr>\n<th>Nutrition</th>\n<th>Apple</th>\n<th>Oranges</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Calories</td>\n<td>52</td>\n<td>47</td>\n</tr>\n<tr>\n<td>Sugar</td>\n<td>10g</td>\n<td>9g</td>\n</tr>\n</tbody>\n</table>\n<table dir=\"rtl\" align=\"right\">\n<thead>\n<tr>\n<th>پێکهاتە</th>\n<th>سێو</th>\n<th>پڕتەقاڵ</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>کالۆری</td>\n<td>٥٢</td>\n<td>٤٧</td>\n</tr>\n<tr>\n<td>شەکر</td>\n<td>١٠گ</td>\n<td>٩گ</td>\n</tr>\n</tbody>\n</table>", "globalization+advanced+emojis");
|
||||
TestParser.TestSpec("Nutrition |Apple | Oranges\n--|-- | --\nCalories|52|47\nSugar|10g|9g\n\n پێکهاتە |سێو | پڕتەقاڵ\n--|-- | --\nکالۆری|٥٢|٤٧\nشەکر| ١٠گ|٩گ", "<table>\n<thead>\n<tr>\n<th>Nutrition</th>\n<th>Apple</th>\n<th>Oranges</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Calories</td>\n<td>52</td>\n<td>47</td>\n</tr>\n<tr>\n<td>Sugar</td>\n<td>10g</td>\n<td>9g</td>\n</tr>\n</tbody>\n</table>\n<table dir=\"rtl\" align=\"right\">\n<thead>\n<tr>\n<th>پێکهاتە</th>\n<th>سێو</th>\n<th>پڕتەقاڵ</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>کالۆری</td>\n<td>٥٢</td>\n<td>٤٧</td>\n</tr>\n<tr>\n<td>شەکر</td>\n<td>١٠گ</td>\n<td>٩گ</td>\n</tr>\n</tbody>\n</table>", "globalization+advanced+emojis", context: "Example 3\nSection Extensions / Globalization\n");
|
||||
}
|
||||
|
||||
// But if text starts with LTR characters, no attributes are added.
|
||||
[Test]
|
||||
public void ExtensionsGlobalization_Example004()
|
||||
{
|
||||
// Example 4
|
||||
// Section: Extensions / Globalization
|
||||
//
|
||||
// The following Markdown:
|
||||
// Foo میوە
|
||||
//
|
||||
// میوە bar
|
||||
//
|
||||
// Baz میوە
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>Foo میوە</p>
|
||||
// <p dir="rtl">میوە bar</p>
|
||||
// <p>Baz میوە</p>
|
||||
|
||||
TestParser.TestSpec("Foo میوە\n\nمیوە bar\n\nBaz میوە", "<p>Foo میوە</p>\n<p dir=\"rtl\">میوە bar</p>\n<p>Baz میوە</p>", "globalization+advanced+emojis", context: "Example 4\nSection Extensions / Globalization\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,4 +186,18 @@ Sugar|10g|9g
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
But if text starts with LTR characters, no attributes are added.
|
||||
|
||||
```````````````````````````````` example
|
||||
Foo میوە
|
||||
|
||||
میوە bar
|
||||
|
||||
Baz میوە
|
||||
.
|
||||
<p>Foo میوە</p>
|
||||
<p dir="rtl">میوە bar</p>
|
||||
<p>Baz میوە</p>
|
||||
````````````````````````````````
|
||||
@@ -71,8 +71,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </tbody>
|
||||
// </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", context: "Example 1\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// The following is not a valid row separator
|
||||
@@ -90,8 +89,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// <p>|-----xxx----+---------+
|
||||
// | This is | not a table</p>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("|-----xxx----+---------+\n| This is | not a table", "<p>|-----xxx----+---------+\n| This is | not a table</p>", "gridtables|advanced");
|
||||
TestParser.TestSpec("|-----xxx----+---------+\n| This is | not a table", "<p>|-----xxx----+---------+\n| This is | not a table</p>", "gridtables|advanced", context: "Example 2\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// **Rule #2**
|
||||
@@ -133,8 +131,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </tbody>
|
||||
// </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", context: "Example 3\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// A row header is separated using `+========+` instead of `+---------+`:
|
||||
@@ -161,8 +158,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </thead>
|
||||
// </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", context: "Example 4\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// The last column separator `|` may be omitted:
|
||||
@@ -188,8 +184,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </tbody>
|
||||
// </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", context: "Example 5\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// The respective width of the columns are calculated from the ratio between the total size of the first table row without counting the `+`: `+----+--------+----+` would be divided between:
|
||||
@@ -226,8 +221,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </tbody>
|
||||
// </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", context: "Example 6\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// Alignment might be specified on the first row using the character `:`:
|
||||
@@ -256,8 +250,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </tbody>
|
||||
// </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", context: "Example 7\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// A grid table may have cells spanning both columns and rows:
|
||||
@@ -300,8 +293,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </tbody>
|
||||
// </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", context: "Example 8\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// A grid table may have cells with both colspan and rowspan:
|
||||
@@ -343,8 +335,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </tbody>
|
||||
// </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", context: "Example 9\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// A grid table may not have irregularly shaped cells:
|
||||
@@ -372,8 +363,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// | DDDDD | E |
|
||||
// +---+---+---+</p>
|
||||
|
||||
Console.WriteLine("Example 10\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+ A +---+ B +\n| A | C | B |\n+---+---+---+\n| DDDDD | E |\n+---+---+---+", "<p>+---+---+---+\n| AAAAA | B |\n+ A +---+ B +\n| A | C | B |\n+---+---+---+\n| DDDDD | E |\n+---+---+---+</p>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+ A +---+ B +\n| A | C | B |\n+---+---+---+\n| DDDDD | E |\n+---+---+---+", "<p>+---+---+---+\n| AAAAA | B |\n+ A +---+ B +\n| A | C | B |\n+---+---+---+\n| DDDDD | E |\n+---+---+---+</p>", "gridtables|advanced", context: "Example 10\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
|
||||
// An empty `+` on a line should result in a simple empty list output:
|
||||
@@ -391,8 +381,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// <li></li>
|
||||
// </ul>
|
||||
|
||||
Console.WriteLine("Example 11\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+", "<ul>\n<li></li>\n</ul>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+", "<ul>\n<li></li>\n</ul>", "gridtables|advanced", context: "Example 11\nSection Extensions / Grid Table\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@ namespace Markdig.Tests.Specs.HardlineBreaks
|
||||
// <p>This is a paragraph<br />
|
||||
// with a break inside</p>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Hardline break\n");
|
||||
TestParser.TestSpec("This is a paragraph\nwith a break inside", "<p>This is a paragraph<br />\nwith a break inside</p>", "hardlinebreak|advanced+hardlinebreak");
|
||||
TestParser.TestSpec("This is a paragraph\nwith a break inside", "<p>This is a paragraph<br />\nwith a break inside</p>", "hardlinebreak|advanced+hardlinebreak", context: "Example 1\nSection Extensions / Hardline break\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,8 +42,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is a <a href="http://your.company.abc/browse/ABCD-123" target="_blank">ABCD-123</a> issue</p>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a ABCD-123 issue", "<p>This is a <a href=\"http://your.company.abc/browse/ABCD-123\" target=\"_blank\">ABCD-123</a> issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a ABCD-123 issue", "<p>This is a <a href=\"http://your.company.abc/browse/ABCD-123\" target=\"_blank\">ABCD-123</a> issue</p>", "jiralinks", context: "Example 1\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -58,8 +57,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is a <a href="http://your.company.abc/browse/ABC4-123" target="_blank">ABC4-123</a> issue</p>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a ABC4-123 issue", "<p>This is a <a href=\"http://your.company.abc/browse/ABC4-123\" target=\"_blank\">ABC4-123</a> issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a ABC4-123 issue", "<p>This is a <a href=\"http://your.company.abc/browse/ABC4-123\" target=\"_blank\">ABC4-123</a> issue</p>", "jiralinks", context: "Example 2\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -74,8 +72,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is a <a href="http://your.company.abc/browse/ABC45-123" target="_blank">ABC45-123</a> issue</p>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a ABC45-123 issue", "<p>This is a <a href=\"http://your.company.abc/browse/ABC45-123\" target=\"_blank\">ABC45-123</a> issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a ABC45-123 issue", "<p>This is a <a href=\"http://your.company.abc/browse/ABC45-123\" target=\"_blank\">ABC45-123</a> issue</p>", "jiralinks", context: "Example 3\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -90,8 +87,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is a <a href="http://your.company.abc/browse/KIRA-1" target="_blank">KIRA-1</a> issue</p>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a KIRA-1 issue", "<p>This is a <a href=\"http://your.company.abc/browse/KIRA-1\" target=\"_blank\">KIRA-1</a> issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a KIRA-1 issue", "<p>This is a <a href=\"http://your.company.abc/browse/KIRA-1\" target=\"_blank\">KIRA-1</a> issue</p>", "jiralinks", context: "Example 4\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -106,8 +102,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is a <a href="http://your.company.abc/browse/Z-1" target="_blank">Z-1</a> issue</p>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a Z-1 issue", "<p>This is a <a href=\"http://your.company.abc/browse/Z-1\" target=\"_blank\">Z-1</a> issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a Z-1 issue", "<p>This is a <a href=\"http://your.company.abc/browse/Z-1\" target=\"_blank\">Z-1</a> issue</p>", "jiralinks", context: "Example 5\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
// These are also valid links with `(` and `)`:
|
||||
@@ -123,8 +118,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is a (<a href="http://your.company.abc/browse/ABCD-123" target="_blank">ABCD-123</a>) issue</p>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a (ABCD-123) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/ABCD-123\" target=\"_blank\">ABCD-123</a>) issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a (ABCD-123) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/ABCD-123\" target=\"_blank\">ABCD-123</a>) issue</p>", "jiralinks", context: "Example 6\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -139,8 +133,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is a (<a href="http://your.company.abc/browse/ABC4-123" target="_blank">ABC4-123</a>) issue</p>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a (ABC4-123) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/ABC4-123\" target=\"_blank\">ABC4-123</a>) issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a (ABC4-123) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/ABC4-123\" target=\"_blank\">ABC4-123</a>) issue</p>", "jiralinks", context: "Example 7\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -155,8 +148,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is a (<a href="http://your.company.abc/browse/KIRA-1" target="_blank">KIRA-1</a>) issue</p>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a (KIRA-1) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/KIRA-1\" target=\"_blank\">KIRA-1</a>) issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a (KIRA-1) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/KIRA-1\" target=\"_blank\">KIRA-1</a>) issue</p>", "jiralinks", context: "Example 8\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -171,8 +163,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is a (<a href="http://your.company.abc/browse/Z-1" target="_blank">Z-1</a>) issue</p>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is a (Z-1) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/Z-1\" target=\"_blank\">Z-1</a>) issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is a (Z-1) issue", "<p>This is a (<a href=\"http://your.company.abc/browse/Z-1\" target=\"_blank\">Z-1</a>) issue</p>", "jiralinks", context: "Example 9\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
// These are not valid links:
|
||||
@@ -188,8 +179,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is not aJIRA-123 issue</p>
|
||||
|
||||
Console.WriteLine("Example 10\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is not aJIRA-123 issue", "<p>This is not aJIRA-123 issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is not aJIRA-123 issue", "<p>This is not aJIRA-123 issue</p>", "jiralinks", context: "Example 10\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -204,8 +194,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is not 4JIRA-123 issue</p>
|
||||
|
||||
Console.WriteLine("Example 11\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is not 4JIRA-123 issue", "<p>This is not 4JIRA-123 issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is not 4JIRA-123 issue", "<p>This is not 4JIRA-123 issue</p>", "jiralinks", context: "Example 11\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -220,8 +209,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is not JIRA-123a issue</p>
|
||||
|
||||
Console.WriteLine("Example 12\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is not JIRA-123a issue", "<p>This is not JIRA-123a issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is not JIRA-123a issue", "<p>This is not JIRA-123a issue</p>", "jiralinks", context: "Example 12\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -236,8 +224,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is not JIRA- issue</p>
|
||||
|
||||
Console.WriteLine("Example 13\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is not JIRA- issue", "<p>This is not JIRA- issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is not JIRA- issue", "<p>This is not JIRA- issue</p>", "jiralinks", context: "Example 13\nSection Jira Links\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -252,8 +239,7 @@ namespace Markdig.Tests.Specs.JiraLinks
|
||||
// Should be rendered as:
|
||||
// <p>This is not JIR4- issue</p>
|
||||
|
||||
Console.WriteLine("Example 14\nSection Jira Links\n");
|
||||
TestParser.TestSpec("This is not JIR4- issue", "<p>This is not JIR4- issue</p>", "jiralinks");
|
||||
TestParser.TestSpec("This is not JIR4- issue", "<p>This is not JIR4- issue</p>", "jiralinks", context: "Example 14\nSection Jira Links\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,7 @@ namespace Markdig.Tests.Specs.ListExtras
|
||||
// <li>Last item</li>
|
||||
// </ol>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Ordered list with alpha letter\n");
|
||||
TestParser.TestSpec("a. First item\nb. Second item\nc. Last item", "<ol type=\"a\">\n<li>First item</li>\n<li>Second item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced");
|
||||
TestParser.TestSpec("a. First item\nb. Second item\nc. Last item", "<ol type=\"a\">\n<li>First item</li>\n<li>Second item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced", context: "Example 1\nSection Extensions / Ordered list with alpha letter\n");
|
||||
}
|
||||
|
||||
// It works also for uppercase alpha:
|
||||
@@ -59,8 +58,7 @@ namespace Markdig.Tests.Specs.ListExtras
|
||||
// <li>Last item</li>
|
||||
// </ol>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Ordered list with alpha letter\n");
|
||||
TestParser.TestSpec("A. First item\nB. Second item\nC. Last item", "<ol type=\"A\">\n<li>First item</li>\n<li>Second item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced");
|
||||
TestParser.TestSpec("A. First item\nB. Second item\nC. Last item", "<ol type=\"A\">\n<li>First item</li>\n<li>Second item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced", context: "Example 2\nSection Extensions / Ordered list with alpha letter\n");
|
||||
}
|
||||
|
||||
// Like for numbered list, a list can start with a different letter
|
||||
@@ -80,8 +78,7 @@ namespace Markdig.Tests.Specs.ListExtras
|
||||
// <li>Second item</li>
|
||||
// </ol>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Ordered list with alpha letter\n");
|
||||
TestParser.TestSpec("b. First item\nc. Second item", "<ol type=\"a\" start=\"2\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "listextras|advanced");
|
||||
TestParser.TestSpec("b. First item\nc. Second item", "<ol type=\"a\" start=\"2\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "listextras|advanced", context: "Example 3\nSection Extensions / Ordered list with alpha letter\n");
|
||||
}
|
||||
|
||||
// A different type of list will break the existing list:
|
||||
@@ -105,8 +102,7 @@ namespace Markdig.Tests.Specs.ListExtras
|
||||
// <li>First item2</li>
|
||||
// </ol>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Ordered list with alpha letter\n");
|
||||
TestParser.TestSpec("a. First item1\nb. Second item\nA. First item2", "<ol type=\"a\">\n<li>First item1</li>\n<li>Second item</li>\n</ol>\n<ol type=\"A\">\n<li>First item2</li>\n</ol>", "listextras|advanced");
|
||||
TestParser.TestSpec("a. First item1\nb. Second item\nA. First item2", "<ol type=\"a\">\n<li>First item1</li>\n<li>Second item</li>\n</ol>\n<ol type=\"A\">\n<li>First item2</li>\n</ol>", "listextras|advanced", context: "Example 4\nSection Extensions / Ordered list with alpha letter\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,8 +132,7 @@ namespace Markdig.Tests.Specs.ListExtras
|
||||
// <li>Last item</li>
|
||||
// </ol>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / Ordered list with roman letter\n");
|
||||
TestParser.TestSpec("i. First item\nii. Second item\niii. Third item\niv. Last item", "<ol type=\"i\">\n<li>First item</li>\n<li>Second item</li>\n<li>Third item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced");
|
||||
TestParser.TestSpec("i. First item\nii. Second item\niii. Third item\niv. Last item", "<ol type=\"i\">\n<li>First item</li>\n<li>Second item</li>\n<li>Third item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced", context: "Example 5\nSection Extensions / Ordered list with roman letter\n");
|
||||
}
|
||||
|
||||
// It works also for uppercase alpha:
|
||||
@@ -161,8 +156,7 @@ namespace Markdig.Tests.Specs.ListExtras
|
||||
// <li>Last item</li>
|
||||
// </ol>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / Ordered list with roman letter\n");
|
||||
TestParser.TestSpec("I. First item\nII. Second item\nIII. Third item\nIV. Last item", "<ol type=\"I\">\n<li>First item</li>\n<li>Second item</li>\n<li>Third item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced");
|
||||
TestParser.TestSpec("I. First item\nII. Second item\nIII. Third item\nIV. Last item", "<ol type=\"I\">\n<li>First item</li>\n<li>Second item</li>\n<li>Third item</li>\n<li>Last item</li>\n</ol>", "listextras|advanced", context: "Example 6\nSection Extensions / Ordered list with roman letter\n");
|
||||
}
|
||||
|
||||
// Like for numbered list, a list can start with a different letter
|
||||
@@ -182,8 +176,7 @@ namespace Markdig.Tests.Specs.ListExtras
|
||||
// <li>Second item</li>
|
||||
// </ol>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Extensions / Ordered list with roman letter\n");
|
||||
TestParser.TestSpec("ii. First item\niii. Second item", "<ol type=\"i\" start=\"2\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "listextras|advanced");
|
||||
TestParser.TestSpec("ii. First item\niii. Second item", "<ol type=\"i\" start=\"2\">\n<li>First item</li>\n<li>Second item</li>\n</ol>", "listextras|advanced", context: "Example 7\nSection Extensions / Ordered list with roman letter\n");
|
||||
}
|
||||
|
||||
// Lists can be restarted, specifying the start point.
|
||||
@@ -209,8 +202,7 @@ namespace Markdig.Tests.Specs.ListExtras
|
||||
// <li>Second item</li>
|
||||
// </ol>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Extensions / Ordered list with roman letter\n");
|
||||
TestParser.TestSpec("1. First item\n\nSome text\n\n2. Second item", "<ol>\n<li>First item</li>\n</ol>\n<p>Some text</p>\n<ol start=\"2\">\n<li>Second item</li>\n</ol>", "listextras|advanced");
|
||||
TestParser.TestSpec("1. First item\n\nSome text\n\n2. Second item", "<ol>\n<li>First item</li>\n</ol>\n<p>Some text</p>\n<ol start=\"2\">\n<li>Second item</li>\n</ol>", "listextras|advanced", context: "Example 8\nSection Extensions / Ordered list with roman letter\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math block\)</span></p>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $math block$", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is a $math block$", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced", context: "Example 1\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// Or by `$$...$$` embracing it by:
|
||||
@@ -47,8 +46,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math block\)</span></p>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $$math block$$", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is a $$math block$$", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced", context: "Example 2\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// Newlines inside an inline math are not allowed:
|
||||
@@ -66,8 +64,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// <p>This is not a $$math
|
||||
// block$$ and? this is a <span class="math">\(math block\)</span></p>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is not a $$math \nblock$$ and? this is a $$math block$$", "<p>This is not a $$math\nblock$$ and? this is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is not a $$math \nblock$$ and? this is a $$math block$$", "<p>This is not a $$math\nblock$$ and? this is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced", context: "Example 3\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -84,8 +81,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// <p>This is not a $math
|
||||
// block$ and? this is a <span class="math">\(math block\)</span></p>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is not a $math \nblock$ and? this is a $math block$", "<p>This is not a $math\nblock$ and? this is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is not a $math \nblock$ and? this is a $math block$", "<p>This is not a $math\nblock$ and? this is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced", context: "Example 4\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// An opening `$` can be followed by a space if the closing is also preceded by a space `$`:
|
||||
@@ -101,8 +97,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math block\)</span></p>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $ math block $", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is a $ math block $", "<p>This is a <span class=\"math\">\\(math block\\)</span></p>", "mathematics|advanced", context: "Example 5\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -117,8 +112,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math block\)</span> after</p>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $ math block $ after", "<p>This is a <span class=\"math\">\\(math block\\)</span> after</p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is a $ math block $ after", "<p>This is a <span class=\"math\">\\(math block\\)</span> after</p>", "mathematics|advanced", context: "Example 6\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -133,8 +127,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math block\)</span> after</p>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $$ math block $$ after", "<p>This is a <span class=\"math\">\\(math block\\)</span> after</p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is a $$ math block $$ after", "<p>This is a <span class=\"math\">\\(math block\\)</span> after</p>", "mathematics|advanced", context: "Example 7\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -149,8 +142,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is a not $ math block$ because there is not a whitespace before the closing</p>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a not $ math block$ because there is not a whitespace before the closing", "<p>This is a not $ math block$ because there is not a whitespace before the closing</p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is a not $ math block$ because there is not a whitespace before the closing", "<p>This is a not $ math block$ because there is not a whitespace before the closing</p>", "mathematics|advanced", context: "Example 8\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// For the opening `$` it requires a space or a punctuation before (but cannot be used within a word):
|
||||
@@ -166,8 +158,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is not a m$ath block$</p>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is not a m$ath block$", "<p>This is not a m$ath block$</p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is not a m$ath block$", "<p>This is not a m$ath block$</p>", "mathematics|advanced", context: "Example 9\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// For the closing `$` it requires a space after or a punctuation (but cannot be preceded by a space and cannot be used within a word):
|
||||
@@ -183,8 +174,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is not a $math bloc$k</p>
|
||||
|
||||
Console.WriteLine("Example 10\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is not a $math bloc$k", "<p>This is not a $math bloc$k</p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is not a $math bloc$k", "<p>This is not a $math bloc$k</p>", "mathematics|advanced", context: "Example 10\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// For the closing `$` it requires a space after or a punctuation (but cannot be preceded by a space and cannot be used within a word):
|
||||
@@ -200,8 +190,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is should not match a 16$ or a $15</p>
|
||||
|
||||
Console.WriteLine("Example 11\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is should not match a 16$ or a $15", "<p>This is should not match a 16$ or a $15</p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is should not match a 16$ or a $15", "<p>This is should not match a 16$ or a $15</p>", "mathematics|advanced", context: "Example 11\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// A `$` can be escaped between a math inline block by using the escape `\\`
|
||||
@@ -217,8 +206,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math \$ block\)</span></p>
|
||||
|
||||
Console.WriteLine("Example 12\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $math \\$ block$", "<p>This is a <span class=\"math\">\\(math \\$ block\\)</span></p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is a $math \\$ block$", "<p>This is a <span class=\"math\">\\(math \\$ block\\)</span></p>", "mathematics|advanced", context: "Example 12\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// At most, two `$` will be matched for the opening and closing:
|
||||
@@ -234,8 +222,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\($math block$\)</span></p>
|
||||
|
||||
Console.WriteLine("Example 13\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $$$math block$$$", "<p>This is a <span class=\"math\">\\($math block$\\)</span></p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is a $$$math block$$$", "<p>This is a <span class=\"math\">\\($math block$\\)</span></p>", "mathematics|advanced", context: "Example 13\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// Regular text can come both before and after the math inline
|
||||
@@ -251,8 +238,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is a <span class="math">\(math block\)</span> with text on both sides.</p>
|
||||
|
||||
Console.WriteLine("Example 14\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is a $math block$ with text on both sides.", "<p>This is a <span class=\"math\">\\(math block\\)</span> with text on both sides.</p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is a $math block$ with text on both sides.", "<p>This is a <span class=\"math\">\\(math block\\)</span> with text on both sides.</p>", "mathematics|advanced", context: "Example 14\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// A mathematic block takes precedence over standard emphasis `*` `_`:
|
||||
@@ -268,8 +254,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p>This is *a <span class="math">\(math* block\)</span></p>
|
||||
|
||||
Console.WriteLine("Example 15\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("This is *a $math* block$", "<p>This is *a <span class=\"math\">\\(math* block\\)</span></p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("This is *a $math* block$", "<p>This is *a <span class=\"math\">\\(math* block\\)</span></p>", "mathematics|advanced", context: "Example 15\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
|
||||
// An opening $$ at the beginning of a line should not be interpreted as a Math block:
|
||||
@@ -285,8 +270,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// Should be rendered as:
|
||||
// <p><span class="math">\(math\)</span> starting at a line</p>
|
||||
|
||||
Console.WriteLine("Example 16\nSection Extensions / Math Inline\n");
|
||||
TestParser.TestSpec("$$ math $$ starting at a line", "<p><span class=\"math\">\\(math\\)</span> starting at a line</p>", "mathematics|advanced");
|
||||
TestParser.TestSpec("$$ math $$ starting at a line", "<p><span class=\"math\">\\(math\\)</span> starting at a line</p>", "mathematics|advanced", context: "Example 16\nSection Extensions / Math Inline\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,8 +304,7 @@ namespace Markdig.Tests.Specs.Math
|
||||
// \end{equation}
|
||||
// \]</div>
|
||||
|
||||
Console.WriteLine("Example 17\nSection Extensions / Math Block\n");
|
||||
TestParser.TestSpec("$$\n\\begin{equation}\n \\int_0^\\infty \\frac{x^3}{e^x-1}\\,dx = \\frac{\\pi^4}{15}\n \\label{eq:sample}\n\\end{equation}\n$$", "<div class=\"math\">\n\\[\n\\begin{equation}\n \\int_0^\\infty \\frac{x^3}{e^x-1}\\,dx = \\frac{\\pi^4}{15}\n \\label{eq:sample}\n\\end{equation}\n\\]</div>", "mathematics|advanced");
|
||||
TestParser.TestSpec("$$\n\\begin{equation}\n \\int_0^\\infty \\frac{x^3}{e^x-1}\\,dx = \\frac{\\pi^4}{15}\n \\label{eq:sample}\n\\end{equation}\n$$", "<div class=\"math\">\n\\[\n\\begin{equation}\n \\int_0^\\infty \\frac{x^3}{e^x-1}\\,dx = \\frac{\\pi^4}{15}\n \\label{eq:sample}\n\\end{equation}\n\\]</div>", "mathematics|advanced", context: "Example 17\nSection Extensions / Math Block\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,8 +57,7 @@ namespace Markdig.Tests.Specs.Media
|
||||
// <p><iframe src="https://music.yandex.ru/iframe/#track/4402274/411845/" class="yandex" width="500" height="281" frameborder="0"></iframe></p>
|
||||
// <p><iframe src="https://ok.ru/videoembed/26870090463" class="odnoklassniki" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Media links\n");
|
||||
TestParser.TestSpec("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100&rel=0\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed?listType=playlist&list=PLC77007E23FF423C6\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://player.vimeo.com/video/8607834\" class=\"vimeo\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"https://sample.com/video.mp4\"></source></video></p>\n<p><iframe src=\"https://music.yandex.ru/iframe/#track/4402274/411845/\" class=\"yandex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n<p><iframe src=\"https://ok.ru/videoembed/26870090463\" class=\"odnoklassniki\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>", "medialinks|advanced+medialinks");
|
||||
TestParser.TestSpec("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100&rel=0\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed?listType=playlist&list=PLC77007E23FF423C6\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://player.vimeo.com/video/8607834\" class=\"vimeo\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"https://sample.com/video.mp4\"></source></video></p>\n<p><iframe src=\"https://music.yandex.ru/iframe/#track/4402274/411845/\" class=\"yandex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n<p><iframe src=\"https://ok.ru/videoembed/26870090463\" class=\"odnoklassniki\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>", "medialinks|advanced+medialinks", context: "Example 1\nSection Extensions / Media links\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ namespace Markdig.Tests.Specs.NoHtml
|
||||
// Should be rendered as:
|
||||
// <p>this is some text</td></tr></p>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / NoHTML\n");
|
||||
TestParser.TestSpec("this is some text</td></tr>", "<p>this is some text</td></tr></p>", "nohtml");
|
||||
TestParser.TestSpec("this is some text</td></tr>", "<p>this is some text</td></tr></p>", "nohtml", context: "Example 1\nSection Extensions / NoHTML\n");
|
||||
}
|
||||
|
||||
// For Block HTML:
|
||||
@@ -51,8 +50,7 @@ namespace Markdig.Tests.Specs.NoHtml
|
||||
// this is some text
|
||||
// </div></p>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / NoHTML\n");
|
||||
TestParser.TestSpec("<div>\nthis is some text\n</div>", "<p><div>\nthis is some text\n</div></p>", "nohtml");
|
||||
TestParser.TestSpec("<div>\nthis is some text\n</div>", "<p><div>\nthis is some text\n</div></p>", "nohtml", context: "Example 2\nSection Extensions / NoHTML\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n-- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a | b\n-- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 1\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// The following is also considered as a table, even if the second line starts like a list:
|
||||
@@ -90,8 +89,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a | b\n- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 2\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// A pipe table with only one header row is allowed:
|
||||
@@ -115,8 +113,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </thead>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "gfm-pipetables", context: "Example 3\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// After a row separator header, they will be interpreted as plain column:
|
||||
@@ -147,8 +144,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n-- | --\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>--</td>\n<td>--</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a | b\n-- | --\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>--</td>\n<td>--</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 4\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// But if a table doesn't start with a column delimiter, it is not interpreted as a table, even if following lines have a column delimiter
|
||||
@@ -168,8 +164,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// c | d
|
||||
// e | f</p>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a b\nc | d\ne | f", "<p>a b\nc | d\ne | f</p>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a b\nc | d\ne | f", "<p>a b\nc | d\ne | f</p>", "gfm-pipetables", context: "Example 5\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// If a line doesn't have a column delimiter `|` the table is not detected
|
||||
@@ -187,8 +182,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// <p>a | b
|
||||
// c no d</p>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\nc no d", "<p>a | b\nc no d</p>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a | b\nc no d", "<p>a | b\nc no d</p>", "gfm-pipetables", context: "Example 6\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// If a row contains more columns than the header row, the extra columns will be ignored:
|
||||
@@ -229,8 +223,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a | b \n-- | --\n0 | 1 | 2\n3 | 4\n5 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>3</td>\n<td>4</td>\n</tr>\n<tr>\n<td>5</td>\n<td></td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a | b \n-- | --\n0 | 1 | 2\n3 | 4\n5 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>3</td>\n<td>4</td>\n</tr>\n<tr>\n<td>5</td>\n<td></td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 7\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #2**
|
||||
@@ -265,8 +258,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a | b |\n-- | --\n0 | 1 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a | b |\n-- | --\n0 | 1 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 8\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #4**
|
||||
@@ -308,8 +300,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec(" a | b |\n-- | --\n| 0 | 1\n| 2 | 3 |\n 4 | 5 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n<tr>\n<td>4</td>\n<td>5</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec(" a | b |\n-- | --\n| 0 | 1\n| 2 | 3 |\n 4 | 5 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n<tr>\n<td>4</td>\n<td>5</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 9\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// A pipe may be present at both the beginning/ending of each line:
|
||||
@@ -340,8 +331,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 10\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("|a|b|\n|-|-|\n|0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("|a|b|\n|-|-|\n|0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 10\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// Or may be omitted on one side:
|
||||
@@ -372,8 +362,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 11\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a|b|\n-|-|\n0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a|b|\n-|-|\n0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 11\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -403,8 +392,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 12\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("|a|b\n|-|-\n|0|1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("|a|b\n|-|-\n|0|1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 12\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// Single column table can be declared with lines starting only by a column delimiter:
|
||||
@@ -437,8 +425,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 13\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("| a\n| --\n| b\n| c ", "<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>b</td>\n</tr>\n<tr>\n<td>c</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("| a\n| --\n| b\n| c ", "<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>b</td>\n</tr>\n<tr>\n<td>c</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 13\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #5**
|
||||
@@ -482,8 +469,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 14\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec(" a | b \n-------|-------\n 0 | 1 \n 2 | 3 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec(" a | b \n-------|-------\n 0 | 1 \n 2 | 3 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 14\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// The text alignment is defined by default to be center for header and left for cells. If the left alignment is applied, it will force the column heading to be left aligned.
|
||||
@@ -524,8 +510,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 15\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec(" a | b | c \n:------|:-------:| ----:\n 0 | 1 | 2 \n 3 | 4 | 5 ", "<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">a</th>\n<th style=\"text-align: center;\">b</th>\n<th style=\"text-align: right;\">c</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\">0</td>\n<td style=\"text-align: center;\">1</td>\n<td style=\"text-align: right;\">2</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">3</td>\n<td style=\"text-align: center;\">4</td>\n<td style=\"text-align: right;\">5</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec(" a | b | c \n:------|:-------:| ----:\n 0 | 1 | 2 \n 3 | 4 | 5 ", "<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">a</th>\n<th style=\"text-align: center;\">b</th>\n<th style=\"text-align: right;\">c</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\">0</td>\n<td style=\"text-align: center;\">1</td>\n<td style=\"text-align: right;\">2</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">3</td>\n<td style=\"text-align: center;\">4</td>\n<td style=\"text-align: right;\">5</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 15\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// Test alignment with starting and ending pipes:
|
||||
@@ -558,8 +543,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 16\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("| abc | def | ghi |\n|:---:|-----|----:|\n| 1 | 2 | 3 |", "<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">abc</th>\n<th>def</th>\n<th style=\"text-align: right;\">ghi</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\">1</td>\n<td>2</td>\n<td style=\"text-align: right;\">3</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("| abc | def | ghi |\n|:---:|-----|----:|\n| 1 | 2 | 3 |", "<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">abc</th>\n<th>def</th>\n<th style=\"text-align: right;\">ghi</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\">1</td>\n<td>2</td>\n<td style=\"text-align: right;\">3</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 16\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// The following example shows a non matching header column separator:
|
||||
@@ -581,8 +565,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// 0 | 1
|
||||
// 2 | 3</p>
|
||||
|
||||
Console.WriteLine("Example 17\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec(" a | b\n-------|---x---\n 0 | 1\n 2 | 3 ", "<p>a | b\n-------|---x---\n0 | 1\n2 | 3</p> ", "gfm-pipetables");
|
||||
TestParser.TestSpec(" a | b\n-------|---x---\n 0 | 1\n 2 | 3 ", "<p>a | b\n-------|---x---\n0 | 1\n2 | 3</p> ", "gfm-pipetables", context: "Example 17\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #6**
|
||||
@@ -620,8 +603,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 18\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec(" *a* | b\n----- |-----\n 0 | _1_\n _2 | 3* ", "<table>\n<thead>\n<tr>\n<th><em>a</em></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td><em>1</em></td>\n</tr>\n<tr>\n<td>_2</td>\n<td>3*</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec(" *a* | b\n----- |-----\n 0 | _1_\n _2 | 3* ", "<table>\n<thead>\n<tr>\n<th><em>a</em></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td><em>1</em></td>\n</tr>\n<tr>\n<td>_2</td>\n<td>3*</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 18\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #7**
|
||||
@@ -640,8 +622,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// Should be rendered as:
|
||||
// <p>a | b <code>0 |</code></p>
|
||||
|
||||
Console.WriteLine("Example 19\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a | b `\n0 | ` ", "<p>a | b <code>0 |</code></p> ", "gfm-pipetables");
|
||||
TestParser.TestSpec("a | b `\n0 | ` ", "<p>a | b <code>0 |</code></p> ", "gfm-pipetables", context: "Example 19\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #8**
|
||||
@@ -674,8 +655,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 20\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a <a href=\"\" title=\"|\"></a> | b\n-- | --\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a <a href=\"\" title=\"|\"></a></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a <a href=\"\" title=\"|\"></a> | b\n-- | --\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a <a href=\"\" title=\"|\"></a></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 20\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #9**
|
||||
@@ -708,8 +688,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 21\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n-- | --\n[This is a link with a | inside the label](http://google.com) | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"http://google.com\">This is a link with a | inside the label</a></td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a | b\n-- | --\n[This is a link with a | inside the label](http://google.com) | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"http://google.com\">This is a link with a | inside the label</a></td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 21\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #10**
|
||||
@@ -735,8 +714,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </thead>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 22\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "gfm-pipetables", context: "Example 22\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -760,8 +738,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </thead>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 23\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("|a|b|c\n|---|---|---|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n<th>c</th>\n</tr>\n</thead>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("|a|b|c\n|---|---|---|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n<th>c</th>\n</tr>\n</thead>\n</table>", "gfm-pipetables", context: "Example 23\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Tests**
|
||||
@@ -809,8 +786,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 24\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("| abc | def | \n|---|---|\n| cde| ddd| \n| eee| fff|\n| fff | fffff | \n|gggg | ffff | ", "<table>\n<thead>\n<tr>\n<th>abc</th>\n<th>def</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>cde</td>\n<td>ddd</td>\n</tr>\n<tr>\n<td>eee</td>\n<td>fff</td>\n</tr>\n<tr>\n<td>fff</td>\n<td>fffff</td>\n</tr>\n<tr>\n<td>gggg</td>\n<td>ffff</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("| abc | def | \n|---|---|\n| cde| ddd| \n| eee| fff|\n| fff | fffff | \n|gggg | ffff | ", "<table>\n<thead>\n<tr>\n<th>abc</th>\n<th>def</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>cde</td>\n<td>ddd</td>\n</tr>\n<tr>\n<td>eee</td>\n<td>fff</td>\n</tr>\n<tr>\n<td>fff</td>\n<td>fffff</td>\n</tr>\n<tr>\n<td>gggg</td>\n<td>ffff</td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 24\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Normalized columns count**
|
||||
@@ -849,8 +825,7 @@ namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 25\nSection Extensions / Gfm Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n-- | - \n0 | 1 | 2\n3 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>3</td>\n<td></td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables");
|
||||
TestParser.TestSpec("a | b\n-- | - \n0 | 1 | 2\n3 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>3</td>\n<td></td>\n</tr>\n</tbody>\n</table>", "gfm-pipetables", context: "Example 25\nSection Extensions / Gfm Pipe Table\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +56,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n-- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("a | b\n-- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 1\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// The following is also considered as a table, even if the second line starts like a list:
|
||||
@@ -88,8 +87,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("a | b\n- | -\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 2\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// A pipe table with only one header row is allowed:
|
||||
@@ -113,8 +111,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </thead>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "pipetables|advanced", context: "Example 3\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// After a row separator header, they will be interpreted as plain column:
|
||||
@@ -145,8 +142,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n-- | --\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>--</td>\n<td>--</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("a | b\n-- | --\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>--</td>\n<td>--</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 4\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// But if a table doesn't start with a column delimiter, it is not interpreted as a table, even if following lines have a column delimiter
|
||||
@@ -166,8 +162,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// c | d
|
||||
// e | f</p>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a b\nc | d\ne | f", "<p>a b\nc | d\ne | f</p>", "pipetables|advanced");
|
||||
TestParser.TestSpec("a b\nc | d\ne | f", "<p>a b\nc | d\ne | f</p>", "pipetables|advanced", context: "Example 5\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// If a line doesn't have a column delimiter `|` the table is not detected
|
||||
@@ -185,8 +180,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// <p>a | b
|
||||
// c no d</p>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\nc no d", "<p>a | b\nc no d</p>", "pipetables|advanced");
|
||||
TestParser.TestSpec("a | b\nc no d", "<p>a | b\nc no d</p>", "pipetables|advanced", context: "Example 6\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// If a row contains more column than the header row, it will still be added as a column:
|
||||
@@ -231,8 +225,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a | b \n-- | --\n0 | 1 | 2\n3 | 4\n5 |", "<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<tr>\n<td>3</td>\n<td>4</td>\n<td></td>\n</tr>\n<tr>\n<td>5</td>\n<td></td>\n<td></td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("a | b \n-- | --\n0 | 1 | 2\n3 | 4\n5 |", "<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<tr>\n<td>3</td>\n<td>4</td>\n<td></td>\n</tr>\n<tr>\n<td>5</td>\n<td></td>\n<td></td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 7\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #2**
|
||||
@@ -267,8 +260,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a | b |\n-- | --\n0 | 1 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("a | b |\n-- | --\n0 | 1 |", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 8\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #4**
|
||||
@@ -310,8 +302,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec(" a | b |\n-- | --\n| 0 | 1\n| 2 | 3 |\n 4 | 5 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n<tr>\n<td>4</td>\n<td>5</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec(" a | b |\n-- | --\n| 0 | 1\n| 2 | 3 |\n 4 | 5 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n<tr>\n<td>4</td>\n<td>5</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 9\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// A pipe may be present at both the beginning/ending of each line:
|
||||
@@ -342,8 +333,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 10\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("|a|b|\n|-|-|\n|0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("|a|b|\n|-|-|\n|0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 10\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// Or may be omitted on one side:
|
||||
@@ -374,8 +364,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 11\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a|b|\n-|-|\n0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("a|b|\n-|-|\n0|1|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 11\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -405,8 +394,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 12\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("|a|b\n|-|-\n|0|1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("|a|b\n|-|-\n|0|1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 12\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// Single column table can be declared with lines starting only by a column delimiter:
|
||||
@@ -439,8 +427,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 13\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("| a\n| --\n| b\n| c ", "<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>b</td>\n</tr>\n<tr>\n<td>c</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("| a\n| --\n| b\n| c ", "<table>\n<thead>\n<tr>\n<th>a</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>b</td>\n</tr>\n<tr>\n<td>c</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 13\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #5**
|
||||
@@ -484,8 +471,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 14\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec(" a | b \n-------|-------\n 0 | 1 \n 2 | 3 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec(" a | b \n-------|-------\n 0 | 1 \n 2 | 3 ", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n<tr>\n<td>2</td>\n<td>3</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 14\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// The text alignment is defined by default to be center for header and left for cells. If the left alignment is applied, it will force the column heading to be left aligned.
|
||||
@@ -526,8 +512,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 15\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec(" a | b | c \n:------|:-------:| ----:\n 0 | 1 | 2 \n 3 | 4 | 5 ", "<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">a</th>\n<th style=\"text-align: center;\">b</th>\n<th style=\"text-align: right;\">c</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\">0</td>\n<td style=\"text-align: center;\">1</td>\n<td style=\"text-align: right;\">2</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">3</td>\n<td style=\"text-align: center;\">4</td>\n<td style=\"text-align: right;\">5</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec(" a | b | c \n:------|:-------:| ----:\n 0 | 1 | 2 \n 3 | 4 | 5 ", "<table>\n<thead>\n<tr>\n<th style=\"text-align: left;\">a</th>\n<th style=\"text-align: center;\">b</th>\n<th style=\"text-align: right;\">c</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: left;\">0</td>\n<td style=\"text-align: center;\">1</td>\n<td style=\"text-align: right;\">2</td>\n</tr>\n<tr>\n<td style=\"text-align: left;\">3</td>\n<td style=\"text-align: center;\">4</td>\n<td style=\"text-align: right;\">5</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 15\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// Test alignment with starting and ending pipes:
|
||||
@@ -560,8 +545,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 16\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("| abc | def | ghi |\n|:---:|-----|----:|\n| 1 | 2 | 3 |", "<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">abc</th>\n<th>def</th>\n<th style=\"text-align: right;\">ghi</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\">1</td>\n<td>2</td>\n<td style=\"text-align: right;\">3</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("| abc | def | ghi |\n|:---:|-----|----:|\n| 1 | 2 | 3 |", "<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">abc</th>\n<th>def</th>\n<th style=\"text-align: right;\">ghi</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\">1</td>\n<td>2</td>\n<td style=\"text-align: right;\">3</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 16\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// The following example shows a non matching header column separator:
|
||||
@@ -583,8 +567,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// 0 | 1
|
||||
// 2 | 3</p>
|
||||
|
||||
Console.WriteLine("Example 17\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec(" a | b\n-------|---x---\n 0 | 1\n 2 | 3 ", "<p>a | b\n-------|---x---\n0 | 1\n2 | 3</p> ", "pipetables|advanced");
|
||||
TestParser.TestSpec(" a | b\n-------|---x---\n 0 | 1\n 2 | 3 ", "<p>a | b\n-------|---x---\n0 | 1\n2 | 3</p> ", "pipetables|advanced", context: "Example 17\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #6**
|
||||
@@ -622,8 +605,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 18\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec(" *a* | b\n----- |-----\n 0 | _1_\n _2 | 3* ", "<table>\n<thead>\n<tr>\n<th><em>a</em></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td><em>1</em></td>\n</tr>\n<tr>\n<td>_2</td>\n<td>3*</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec(" *a* | b\n----- |-----\n 0 | _1_\n _2 | 3* ", "<table>\n<thead>\n<tr>\n<th><em>a</em></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td><em>1</em></td>\n</tr>\n<tr>\n<td>_2</td>\n<td>3*</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 18\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #7**
|
||||
@@ -642,8 +624,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// Should be rendered as:
|
||||
// <p>a | b <code>0 |</code></p>
|
||||
|
||||
Console.WriteLine("Example 19\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a | b `\n0 | ` ", "<p>a | b <code>0 |</code></p> ", "pipetables|advanced");
|
||||
TestParser.TestSpec("a | b `\n0 | ` ", "<p>a | b <code>0 |</code></p> ", "pipetables|advanced", context: "Example 19\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #8**
|
||||
@@ -676,8 +657,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 20\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a <a href=\"\" title=\"|\"></a> | b\n-- | --\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a <a href=\"\" title=\"|\"></a></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("a <a href=\"\" title=\"|\"></a> | b\n-- | --\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a <a href=\"\" title=\"|\"></a></th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 20\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #9**
|
||||
@@ -710,8 +690,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 21\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n-- | --\n[This is a link with a | inside the label](http://google.com) | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"http://google.com\">This is a link with a | inside the label</a></td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("a | b\n-- | --\n[This is a link with a | inside the label](http://google.com) | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><a href=\"http://google.com\">This is a link with a | inside the label</a></td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 21\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Rule #10**
|
||||
@@ -737,8 +716,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </thead>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 22\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("a | b\n-- | --", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n</table>", "pipetables|advanced", context: "Example 22\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -762,8 +740,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </thead>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 23\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("|a|b|c\n|---|---|---|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n<th>c</th>\n</tr>\n</thead>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("|a|b|c\n|---|---|---|", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n<th>c</th>\n</tr>\n</thead>\n</table>", "pipetables|advanced", context: "Example 23\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Tests**
|
||||
@@ -811,8 +788,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 24\nSection Extensions / Pipe Table\n");
|
||||
TestParser.TestSpec("| abc | def | \n|---|---|\n| cde| ddd| \n| eee| fff|\n| fff | fffff | \n|gggg | ffff | ", "<table>\n<thead>\n<tr>\n<th>abc</th>\n<th>def</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>cde</td>\n<td>ddd</td>\n</tr>\n<tr>\n<td>eee</td>\n<td>fff</td>\n</tr>\n<tr>\n<td>fff</td>\n<td>fffff</td>\n</tr>\n<tr>\n<td>gggg</td>\n<td>ffff</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced");
|
||||
TestParser.TestSpec("| abc | def | \n|---|---|\n| cde| ddd| \n| eee| fff|\n| fff | fffff | \n|gggg | ffff | ", "<table>\n<thead>\n<tr>\n<th>abc</th>\n<th>def</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>cde</td>\n<td>ddd</td>\n</tr>\n<tr>\n<td>eee</td>\n<td>fff</td>\n</tr>\n<tr>\n<td>fff</td>\n<td>fffff</td>\n</tr>\n<tr>\n<td>gggg</td>\n<td>ffff</td>\n</tr>\n</tbody>\n</table>", "pipetables|advanced", context: "Example 24\nSection Extensions / Pipe Table\n");
|
||||
}
|
||||
|
||||
// **Normalized columns count**
|
||||
@@ -847,8 +823,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 25\nSection Extensions / Pipe Table\n");
|
||||
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");
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a “text”</p>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("This is a \"text\"", "<p>This is a “text”</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is a \"text\"", "<p>This is a “text”</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 1\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -46,8 +45,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a ‘text’</p>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("This is a 'text'", "<p>This is a ‘text’</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is a 'text'", "<p>This is a ‘text’</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 2\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -62,8 +60,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a «text»</p>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("This is a <<text>>", "<p>This is a «text»</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is a <<text>>", "<p>This is a «text»</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 3\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
// Unbalanced quotes are not changed:
|
||||
@@ -79,8 +76,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a "text</p>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("This is a \"text", "<p>This is a "text</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is a \"text", "<p>This is a "text</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 4\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -95,8 +91,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a 'text</p>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("This is a 'text", "<p>This is a 'text</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is a 'text", "<p>This is a 'text</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 5\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -111,8 +106,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a <<text</p>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("This is a <<text", "<p>This is a <<text</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is a <<text", "<p>This is a <<text</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 6\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
// Unbalanced quotes inside other quotes are not changed:
|
||||
@@ -128,8 +122,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a “text 'with” a another text'</p>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("This is a \"text 'with\" a another text'", "<p>This is a “text 'with” a another text'</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is a \"text 'with\" a another text'", "<p>This is a “text 'with” a another text'</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 7\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -144,8 +137,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is ‘a “text 'with” a another text’</p>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("This is 'a \"text 'with\" a another text'", "<p>This is ‘a “text 'with” a another text’</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is 'a \"text 'with\" a another text'", "<p>This is ‘a “text 'with” a another text’</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 8\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -160,8 +152,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a ‘text <<with’ a another text>></p>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("This is a 'text <<with' a another text>>", "<p>This is a ‘text <<with’ a another text>></p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is a 'text <<with' a another text>>", "<p>This is a ‘text <<with’ a another text>></p>", "pipetables+smartypants|advanced+smartypants", context: "Example 9\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -176,8 +167,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a «text 'with» a another text'</p>
|
||||
|
||||
Console.WriteLine("Example 10\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("This is a <<text 'with>> a another text'", "<p>This is a «text 'with» a another text'</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is a <<text 'with>> a another text'", "<p>This is a «text 'with» a another text'</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 10\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
// Quotes requires to have the same rules than emphasis `_` regarding left/right frankling rules:
|
||||
@@ -193,8 +183,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>It's not quotes'</p>
|
||||
|
||||
Console.WriteLine("Example 11\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("It's not quotes'", "<p>It's not quotes'</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("It's not quotes'", "<p>It's not quotes'</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 11\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -209,8 +198,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>They are ' not matching quotes '</p>
|
||||
|
||||
Console.WriteLine("Example 12\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("They are ' not matching quotes '", "<p>They are ' not matching quotes '</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("They are ' not matching quotes '", "<p>They are ' not matching quotes '</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 12\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -225,8 +213,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>They are' not matching 'quotes</p>
|
||||
|
||||
Console.WriteLine("Example 13\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("They are' not matching 'quotes", "<p>They are' not matching 'quotes</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("They are' not matching 'quotes", "<p>They are' not matching 'quotes</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 13\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
// An emphasis starting inside left/right quotes will span over the right quote:
|
||||
@@ -242,8 +229,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is “a <em>text” with an emphasis</em></p>
|
||||
|
||||
Console.WriteLine("Example 14\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("This is \"a *text\" with an emphasis*", "<p>This is “a <em>text” with an emphasis</em></p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is \"a *text\" with an emphasis*", "<p>This is “a <em>text” with an emphasis</em></p>", "pipetables+smartypants|advanced+smartypants", context: "Example 14\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
|
||||
// Multiple sets of quotes can be used
|
||||
@@ -259,8 +245,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>“aaa” “bbb” “ccc” “ddd”</p>
|
||||
|
||||
Console.WriteLine("Example 15\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("\"aaa\" \"bbb\" \"ccc\" \"ddd\"", "<p>“aaa” “bbb” “ccc” “ddd”</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("\"aaa\" \"bbb\" \"ccc\" \"ddd\"", "<p>“aaa” “bbb” “ccc” “ddd”</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 15\nSection Extensions / SmartyPants Quotes\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,8 +265,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a – text</p>
|
||||
|
||||
Console.WriteLine("Example 16\nSection Extensions / SmartyPants Separators\n");
|
||||
TestParser.TestSpec("This is a -- text", "<p>This is a – text</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is a -- text", "<p>This is a – text</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 16\nSection Extensions / SmartyPants Separators\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -296,8 +280,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a — text</p>
|
||||
|
||||
Console.WriteLine("Example 17\nSection Extensions / SmartyPants Separators\n");
|
||||
TestParser.TestSpec("This is a --- text", "<p>This is a — text</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is a --- text", "<p>This is a — text</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 17\nSection Extensions / SmartyPants Separators\n");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -312,8 +295,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a en ellipsis…</p>
|
||||
|
||||
Console.WriteLine("Example 18\nSection Extensions / SmartyPants Separators\n");
|
||||
TestParser.TestSpec("This is a en ellipsis...", "<p>This is a en ellipsis…</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("This is a en ellipsis...", "<p>This is a en ellipsis…</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 18\nSection Extensions / SmartyPants Separators\n");
|
||||
}
|
||||
|
||||
// Check that a smartypants are not breaking pipetable parsing:
|
||||
@@ -344,8 +326,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 19\nSection Extensions / SmartyPants Separators\n");
|
||||
TestParser.TestSpec("a | b\n-- | --\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("a | b\n-- | --\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables+smartypants|advanced+smartypants", context: "Example 19\nSection Extensions / SmartyPants Separators\n");
|
||||
}
|
||||
|
||||
// Check quotes and dash:
|
||||
@@ -361,8 +342,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>A “quote” with a —</p>
|
||||
|
||||
Console.WriteLine("Example 20\nSection Extensions / SmartyPants Separators\n");
|
||||
TestParser.TestSpec("A \"quote\" with a ---", "<p>A “quote” with a —</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
TestParser.TestSpec("A \"quote\" with a ---", "<p>A “quote” with a —</p>", "pipetables+smartypants|advanced+smartypants", context: "Example 20\nSection Extensions / SmartyPants Separators\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,7 @@ namespace Markdig.Tests.Specs.TaskLists
|
||||
// <li>Item4</li>
|
||||
// </ul>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / TaskLists\n");
|
||||
TestParser.TestSpec("- [ ] Item1\n- [x] Item2\n- [ ] Item3\n- Item4", "<ul class=\"contains-task-list\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> Item1</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> Item2</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> Item3</li>\n<li>Item4</li>\n</ul>", "tasklists|advanced");
|
||||
TestParser.TestSpec("- [ ] Item1\n- [x] Item2\n- [ ] Item3\n- Item4", "<ul class=\"contains-task-list\">\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> Item1</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> Item2</li>\n<li class=\"task-list-item\"><input disabled=\"disabled\" type=\"checkbox\" /> Item3</li>\n<li>Item4</li>\n</ul>", "tasklists|advanced", context: "Example 1\nSection Extensions / TaskLists\n");
|
||||
}
|
||||
|
||||
// A task is not recognized outside a list item:
|
||||
@@ -55,8 +54,7 @@ namespace Markdig.Tests.Specs.TaskLists
|
||||
// Should be rendered as:
|
||||
// <p>[ ] This is not a task list</p>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / TaskLists\n");
|
||||
TestParser.TestSpec("[ ] This is not a task list", "<p>[ ] This is not a task list</p>", "tasklists|advanced");
|
||||
TestParser.TestSpec("[ ] This is not a task list", "<p>[ ] This is not a task list</p>", "tasklists|advanced", context: "Example 2\nSection Extensions / TaskLists\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,7 @@ namespace Markdig.Tests.Specs.Yaml
|
||||
// Should be rendered as:
|
||||
// <p>This is a text</p>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / YAML frontmatter discard\n");
|
||||
TestParser.TestSpec("---\nthis: is a frontmatter\n---\nThis is a text", "<p>This is a text</p>", "yaml");
|
||||
TestParser.TestSpec("---\nthis: is a frontmatter\n---\nThis is a text", "<p>This is a text</p>", "yaml", context: "Example 1\nSection Extensions / YAML frontmatter discard\n");
|
||||
}
|
||||
|
||||
// But if a frontmatter doesn't happen on the first line, it will be parse as regular Markdown content
|
||||
@@ -56,8 +55,7 @@ namespace Markdig.Tests.Specs.Yaml
|
||||
// <h2>this: is a frontmatter</h2>
|
||||
// <p>This is a text2</p>
|
||||
|
||||
Console.WriteLine("Example 2\nSection Extensions / YAML frontmatter discard\n");
|
||||
TestParser.TestSpec("This is a text1\n---\nthis: is a frontmatter\n---\nThis is a text2", "<h2>This is a text1</h2>\n<h2>this: is a frontmatter</h2>\n<p>This is a text2</p>", "yaml");
|
||||
TestParser.TestSpec("This is a text1\n---\nthis: is a frontmatter\n---\nThis is a text2", "<h2>This is a text1</h2>\n<h2>this: is a frontmatter</h2>\n<p>This is a text2</p>", "yaml", context: "Example 2\nSection Extensions / YAML frontmatter discard\n");
|
||||
}
|
||||
|
||||
// It expects an exact 3 dashes `---`:
|
||||
@@ -78,8 +76,7 @@ namespace Markdig.Tests.Specs.Yaml
|
||||
// <h2>this: is a frontmatter</h2>
|
||||
// <p>This is a text</p>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / YAML frontmatter discard\n");
|
||||
TestParser.TestSpec("----\nthis: is a frontmatter\n----\nThis is a text", "<hr />\n<h2>this: is a frontmatter</h2>\n<p>This is a text</p>", "yaml");
|
||||
TestParser.TestSpec("----\nthis: is a frontmatter\n----\nThis is a text", "<hr />\n<h2>this: is a frontmatter</h2>\n<p>This is a text</p>", "yaml", context: "Example 3\nSection Extensions / YAML frontmatter discard\n");
|
||||
}
|
||||
|
||||
// It can end with three dots `...`:
|
||||
@@ -99,8 +96,7 @@ namespace Markdig.Tests.Specs.Yaml
|
||||
// Should be rendered as:
|
||||
// <p>This is a text</p>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / YAML frontmatter discard\n");
|
||||
TestParser.TestSpec("---\nthis: is a frontmatter\n\n...\nThis is a text", "<p>This is a text</p>", "yaml");
|
||||
TestParser.TestSpec("---\nthis: is a frontmatter\n\n...\nThis is a text", "<p>This is a text</p>", "yaml", context: "Example 4\nSection Extensions / YAML frontmatter discard\n");
|
||||
}
|
||||
|
||||
// If the end front matter marker (`...` or `---`) is not present, it will render the `---` has a `<hr>`:
|
||||
@@ -120,8 +116,7 @@ namespace Markdig.Tests.Specs.Yaml
|
||||
// <p>this: is a frontmatter
|
||||
// This is a text</p>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / YAML frontmatter discard\n");
|
||||
TestParser.TestSpec("---\nthis: is a frontmatter\nThis is a text", "<hr />\n<p>this: is a frontmatter\nThis is a text</p>", "yaml");
|
||||
TestParser.TestSpec("---\nthis: is a frontmatter\nThis is a text", "<hr />\n<p>this: is a frontmatter\nThis is a text</p>", "yaml", context: "Example 5\nSection Extensions / YAML frontmatter discard\n");
|
||||
}
|
||||
|
||||
// It expects exactly three dots `...`:
|
||||
@@ -143,8 +138,7 @@ namespace Markdig.Tests.Specs.Yaml
|
||||
// ....
|
||||
// This is a text</p>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / YAML frontmatter discard\n");
|
||||
TestParser.TestSpec("---\nthis: is a frontmatter\n....\nThis is a text", "<hr />\n<p>this: is a frontmatter\n....\nThis is a text</p>", "yaml");
|
||||
TestParser.TestSpec("---\nthis: is a frontmatter\n....\nThis is a text", "<hr />\n<p>this: is a frontmatter\n....\nThis is a text</p>", "yaml", context: "Example 6\nSection Extensions / YAML frontmatter discard\n");
|
||||
}
|
||||
|
||||
// Front matter ends with the first line containing three dots `...` or three dashes `---`:
|
||||
@@ -166,8 +160,7 @@ namespace Markdig.Tests.Specs.Yaml
|
||||
// Should be rendered as:
|
||||
// <p>This is a text</p>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Extensions / YAML frontmatter discard\n");
|
||||
TestParser.TestSpec("---\nthis: is a frontmatter\n....\n\nHello\n---\nThis is a text", "<p>This is a text</p>", "yaml");
|
||||
TestParser.TestSpec("---\nthis: is a frontmatter\n....\n\nHello\n---\nThis is a text", "<p>This is a text</p>", "yaml", context: "Example 7\nSection Extensions / YAML frontmatter discard\n");
|
||||
}
|
||||
|
||||
// It expects whitespace can exist after the leading characters
|
||||
@@ -186,8 +179,7 @@ namespace Markdig.Tests.Specs.Yaml
|
||||
// Should be rendered as:
|
||||
// <p>This is a text</p>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Extensions / YAML frontmatter discard\n");
|
||||
TestParser.TestSpec("--- \nthis: is a frontmatter\n...\nThis is a text", "<p>This is a text</p>", "yaml");
|
||||
TestParser.TestSpec("--- \nthis: is a frontmatter\n...\nThis is a text", "<p>This is a text</p>", "yaml", context: "Example 8\nSection Extensions / YAML frontmatter discard\n");
|
||||
}
|
||||
|
||||
// It expects whitespace can exist after the trailing characters
|
||||
@@ -206,8 +198,7 @@ namespace Markdig.Tests.Specs.Yaml
|
||||
// Should be rendered as:
|
||||
// <p>This is a text</p>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Extensions / YAML frontmatter discard\n");
|
||||
TestParser.TestSpec("---\nthis: is a frontmatter\n... \nThis is a text", "<p>This is a text</p>", "yaml");
|
||||
TestParser.TestSpec("---\nthis: is a frontmatter\n... \nThis is a text", "<p>This is a text</p>", "yaml", context: "Example 9\nSection Extensions / YAML frontmatter discard\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,5 +88,102 @@ namespace Markdig.Tests
|
||||
|
||||
Assert.Throws<ArgumentException>(() => container[0] = two); // two already has a parent
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Contains()
|
||||
{
|
||||
var container = new MockContainerBlock();
|
||||
var block = new ParagraphBlock();
|
||||
|
||||
Assert.False(container.Contains(block));
|
||||
|
||||
container.Add(block);
|
||||
Assert.True(container.Contains(block));
|
||||
|
||||
container.Add(new ParagraphBlock());
|
||||
Assert.True(container.Contains(block));
|
||||
|
||||
container.Insert(0, new ParagraphBlock());
|
||||
Assert.True(container.Contains(block));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Remove()
|
||||
{
|
||||
var container = new MockContainerBlock();
|
||||
var block = new ParagraphBlock();
|
||||
|
||||
Assert.False(container.Remove(block));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => container.RemoveAt(0));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
|
||||
container.Add(block);
|
||||
Assert.AreEqual(1, container.Count);
|
||||
Assert.True(container.Remove(block));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.False(container.Remove(block));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
|
||||
container.Add(block);
|
||||
Assert.AreEqual(1, container.Count);
|
||||
container.RemoveAt(0);
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => container.RemoveAt(0));
|
||||
Assert.AreEqual(0, container.Count);
|
||||
|
||||
container.Add(new ParagraphBlock { Column = 1 });
|
||||
container.Add(new ParagraphBlock { Column = 2 });
|
||||
container.Add(new ParagraphBlock { Column = 3 });
|
||||
container.Add(new ParagraphBlock { Column = 4 });
|
||||
Assert.AreEqual(4, container.Count);
|
||||
|
||||
container.RemoveAt(2);
|
||||
Assert.AreEqual(3, container.Count);
|
||||
Assert.AreEqual(4, container[2].Column);
|
||||
|
||||
Assert.True(container.Remove(container[1]));
|
||||
Assert.AreEqual(2, container.Count);
|
||||
Assert.AreEqual(1, container[0].Column);
|
||||
Assert.AreEqual(4, container[1].Column);
|
||||
Assert.Throws<IndexOutOfRangeException>(() => _ = container[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyTo()
|
||||
{
|
||||
var container = new MockContainerBlock();
|
||||
|
||||
var destination = new Block[4];
|
||||
container.CopyTo(destination, 0);
|
||||
container.CopyTo(destination, 1);
|
||||
container.CopyTo(destination, -1);
|
||||
container.CopyTo(destination, 5);
|
||||
Assert.Null(destination[0]);
|
||||
|
||||
container.Add(new ParagraphBlock());
|
||||
container.CopyTo(destination, 0);
|
||||
Assert.NotNull(destination[0]);
|
||||
Assert.Null(destination[1]);
|
||||
Assert.Null(destination[2]);
|
||||
Assert.Null(destination[3]);
|
||||
|
||||
container.CopyTo(destination, 2);
|
||||
Assert.NotNull(destination[0]);
|
||||
Assert.Null(destination[1]);
|
||||
Assert.NotNull(destination[2]);
|
||||
Assert.Null(destination[3]);
|
||||
|
||||
Array.Clear(destination);
|
||||
|
||||
container.Add(new ParagraphBlock());
|
||||
container.CopyTo(destination, 1);
|
||||
Assert.Null(destination[0]);
|
||||
Assert.NotNull(destination[1]);
|
||||
Assert.NotNull(destination[2]);
|
||||
Assert.Null(destination[3]);
|
||||
|
||||
Assert.Throws<IndexOutOfRangeException>(() => container.CopyTo(destination, 3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
189
src/Markdig.Tests/TestFastStringWriter.cs
Normal file
189
src/Markdig.Tests/TestFastStringWriter.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using Markdig.Helpers;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestFastStringWriter
|
||||
{
|
||||
private const string NewLineReplacement = "~~NEW_LINE~~";
|
||||
|
||||
private FastStringWriter _writer = new();
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_writer = new FastStringWriter
|
||||
{
|
||||
NewLine = NewLineReplacement
|
||||
};
|
||||
}
|
||||
|
||||
public void AssertToString(string value)
|
||||
{
|
||||
value = value.Replace("\n", NewLineReplacement);
|
||||
Assert.AreEqual(value, _writer.ToString());
|
||||
Assert.AreEqual(value, _writer.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task NewLine()
|
||||
{
|
||||
Assert.AreEqual("\n", new FastStringWriter().NewLine);
|
||||
|
||||
_writer.NewLine = "\r";
|
||||
Assert.AreEqual("\r", _writer.NewLine);
|
||||
|
||||
_writer.NewLine = "foo";
|
||||
Assert.AreEqual("foo", _writer.NewLine);
|
||||
|
||||
_writer.WriteLine();
|
||||
await _writer.WriteLineAsync();
|
||||
_writer.WriteLine("bar");
|
||||
Assert.AreEqual("foofoobarfoo", _writer.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task FlushCloseDispose()
|
||||
{
|
||||
_writer.Write('a');
|
||||
|
||||
// Nops
|
||||
_writer.Close();
|
||||
_writer.Dispose();
|
||||
await _writer.DisposeAsync();
|
||||
_writer.Flush();
|
||||
await _writer.FlushAsync();
|
||||
|
||||
_writer.Write('b');
|
||||
AssertToString("ab");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_Char()
|
||||
{
|
||||
_writer.Write('a');
|
||||
AssertToString("a");
|
||||
|
||||
_writer.Write('b');
|
||||
AssertToString("ab");
|
||||
|
||||
_writer.Write('\0');
|
||||
_writer.Write('\r');
|
||||
_writer.Write('\u1234');
|
||||
AssertToString("ab\0\r\u1234");
|
||||
|
||||
_writer.Reset();
|
||||
AssertToString("");
|
||||
|
||||
_writer.Write('a');
|
||||
_writer.WriteLine('b');
|
||||
_writer.Write('c');
|
||||
_writer.Write('d');
|
||||
_writer.WriteLine('e');
|
||||
AssertToString("ab\ncde\n");
|
||||
|
||||
await _writer.WriteAsync('f');
|
||||
await _writer.WriteLineAsync('g');
|
||||
AssertToString("ab\ncde\nfg\n");
|
||||
|
||||
_writer.Reset();
|
||||
|
||||
for (int i = 0; i < 2050; i++)
|
||||
{
|
||||
_writer.Write('a');
|
||||
AssertToString(new string('a', i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_String()
|
||||
{
|
||||
_writer.Write("foo");
|
||||
AssertToString("foo");
|
||||
|
||||
_writer.WriteLine("bar");
|
||||
AssertToString("foobar\n");
|
||||
|
||||
await _writer.WriteAsync("baz");
|
||||
await _writer.WriteLineAsync("foo");
|
||||
AssertToString("foobar\nbazfoo\n");
|
||||
|
||||
_writer.Write(new string('a', 1050));
|
||||
AssertToString("foobar\nbazfoo\n" + new string('a', 1050));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_Span()
|
||||
{
|
||||
_writer.Write("foo".AsSpan());
|
||||
AssertToString("foo");
|
||||
|
||||
_writer.WriteLine("bar".AsSpan());
|
||||
AssertToString("foobar\n");
|
||||
|
||||
await _writer.WriteAsync("baz".AsMemory());
|
||||
await _writer.WriteLineAsync("foo".AsMemory());
|
||||
AssertToString("foobar\nbazfoo\n");
|
||||
|
||||
_writer.Write(new string('a', 1050).AsSpan());
|
||||
AssertToString("foobar\nbazfoo\n" + new string('a', 1050));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_CharArray()
|
||||
{
|
||||
_writer.Write("foo".ToCharArray());
|
||||
AssertToString("foo");
|
||||
|
||||
_writer.WriteLine("bar".ToCharArray());
|
||||
AssertToString("foobar\n");
|
||||
|
||||
await _writer.WriteAsync("baz".ToCharArray());
|
||||
await _writer.WriteLineAsync("foo".ToCharArray());
|
||||
AssertToString("foobar\nbazfoo\n");
|
||||
|
||||
_writer.Write(new string('a', 1050).ToCharArray());
|
||||
AssertToString("foobar\nbazfoo\n" + new string('a', 1050));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_CharArrayWithIndexes()
|
||||
{
|
||||
_writer.Write("foo".ToCharArray(), 1, 1);
|
||||
AssertToString("o");
|
||||
|
||||
_writer.WriteLine("bar".ToCharArray(), 0, 2);
|
||||
AssertToString("oba\n");
|
||||
|
||||
await _writer.WriteAsync("baz".ToCharArray(), 0, 1);
|
||||
await _writer.WriteLineAsync("foo".ToCharArray(), 0, 3);
|
||||
AssertToString("oba\nbfoo\n");
|
||||
|
||||
_writer.Write(new string('a', 1050).ToCharArray(), 10, 1035);
|
||||
AssertToString("oba\nbfoo\n" + new string('a', 1035));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Write_StringBuilder()
|
||||
{
|
||||
_writer.Write(new StringBuilder("foo"));
|
||||
AssertToString("foo");
|
||||
|
||||
_writer.WriteLine(new StringBuilder("bar"));
|
||||
AssertToString("foobar\n");
|
||||
|
||||
await _writer.WriteAsync(new StringBuilder("baz"));
|
||||
await _writer.WriteLineAsync(new StringBuilder("foo"));
|
||||
AssertToString("foobar\nbazfoo\n");
|
||||
|
||||
var sb = new StringBuilder("foo");
|
||||
sb.Append('a', 1050);
|
||||
_writer.Write(sb);
|
||||
AssertToString("foobar\nbazfoo\nfoo" + new string('a', 1050));
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/Markdig.Tests/TestFencedCodeBlocks.cs
Normal file
46
src/Markdig.Tests/TestFencedCodeBlocks.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Linq;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class TestFencedCodeBlocks
|
||||
{
|
||||
[Test]
|
||||
[TestCase("c#", "c#", "")]
|
||||
[TestCase("C#", "C#", "")]
|
||||
[TestCase(" c#", "c#", "")]
|
||||
[TestCase(" c# ", "c#", "")]
|
||||
[TestCase(" \tc# ", "c#", "")]
|
||||
[TestCase("\t c# \t", "c#", "")]
|
||||
[TestCase(" c# ", "c#", "")]
|
||||
[TestCase(" c# foo", "c#", "foo")]
|
||||
[TestCase(" c# \t fOo \t", "c#", "fOo")]
|
||||
[TestCase("in\\%fo arg\\%ument", "in%fo", "arg%ument")]
|
||||
[TestCase("info	 arg´ument", "info\t", "arg\u00B4ument")]
|
||||
public void TestInfoAndArguments(string infoString, string expectedInfo, string expectedArguments)
|
||||
{
|
||||
Test('`');
|
||||
Test('~');
|
||||
|
||||
void Test(char fencedChar)
|
||||
{
|
||||
const string Contents = "Foo\nBar\n";
|
||||
|
||||
string fence = new string(fencedChar, 3);
|
||||
string markdownText = $"{fence}{infoString}\n{Contents}\n{fence}\n";
|
||||
|
||||
MarkdownDocument document = Markdown.Parse(markdownText);
|
||||
|
||||
FencedCodeBlock codeBlock = document.Descendants<FencedCodeBlock>().Single();
|
||||
|
||||
Assert.AreEqual(fencedChar, codeBlock.FencedChar);
|
||||
Assert.AreEqual(3, codeBlock.OpeningFencedCharCount);
|
||||
Assert.AreEqual(3, codeBlock.ClosingFencedCharCount);
|
||||
Assert.AreEqual(expectedInfo, codeBlock.Info);
|
||||
Assert.AreEqual(expectedArguments, codeBlock.Arguments);
|
||||
Assert.AreEqual(Contents, codeBlock.Lines.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
67
src/Markdig.Tests/TestLazySubstring.cs
Normal file
67
src/Markdig.Tests/TestLazySubstring.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Markdig.Helpers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class TestLazySubstring
|
||||
{
|
||||
[Theory]
|
||||
[TestCase("")]
|
||||
[TestCase("a")]
|
||||
[TestCase("foo")]
|
||||
public void LazySubstring_ReturnsCorrectSubstring(string text)
|
||||
{
|
||||
var substring = new LazySubstring(text);
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(text.Length, substring.Length);
|
||||
|
||||
Assert.AreEqual(text, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(text, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(text.Length, substring.Length);
|
||||
|
||||
Assert.AreSame(substring.ToString(), substring.ToString());
|
||||
Assert.AreEqual(text, substring.ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(text.Length, substring.Length);
|
||||
|
||||
Assert.AreEqual(text, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(text, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(text.Length, substring.Length);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[TestCase("", 0, 0)]
|
||||
[TestCase("a", 0, 0)]
|
||||
[TestCase("a", 1, 0)]
|
||||
[TestCase("a", 0, 1)]
|
||||
[TestCase("foo", 1, 0)]
|
||||
[TestCase("foo", 1, 1)]
|
||||
[TestCase("foo", 1, 2)]
|
||||
[TestCase("foo", 0, 3)]
|
||||
public void LazySubstring_ReturnsCorrectSubstring(string text, int start, int length)
|
||||
{
|
||||
var substring = new LazySubstring(text, start, length);
|
||||
Assert.AreEqual(start, substring.Offset);
|
||||
Assert.AreEqual(length, substring.Length);
|
||||
|
||||
string expectedSubstring = text.Substring(start, length);
|
||||
|
||||
Assert.AreEqual(expectedSubstring, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(expectedSubstring, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(start, substring.Offset);
|
||||
Assert.AreEqual(length, substring.Length);
|
||||
|
||||
Assert.AreSame(substring.ToString(), substring.ToString());
|
||||
Assert.AreEqual(expectedSubstring, substring.ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(length, substring.Length);
|
||||
|
||||
Assert.AreEqual(expectedSubstring, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(expectedSubstring, substring.AsSpan().ToString());
|
||||
Assert.AreEqual(0, substring.Offset);
|
||||
Assert.AreEqual(length, substring.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,35 @@ namespace Markdig.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDocumentToHtmlWithWriter()
|
||||
{
|
||||
var writer = new StringWriter();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse("This is a text with some *emphasis*");
|
||||
document.ToHtml(writer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
|
||||
writer = new StringWriter();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
MarkdownDocument document = Markdown.Parse("This is a text with a https://link.tld/", pipeline);
|
||||
document.ToHtml(writer, pipeline);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
writer.GetStringBuilder().Length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConvert()
|
||||
{
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System.Linq;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
@@ -8,10 +11,21 @@ namespace Markdig.Tests
|
||||
[TestCase("a \nb", "<p>a<br />\nb</p>\n")]
|
||||
[TestCase("a\\\nb", "<p>a<br />\nb</p>\n")]
|
||||
[TestCase("a `b\nc`", "<p>a <code>b c</code></p>\n")]
|
||||
[TestCase("# Text A\nText B\n\n## Text C", "<h1>Text A</h1>\n<p>Text B</p>\n<h2>Text C</h2>\n")]
|
||||
public void Test(string value, string expectedHtml)
|
||||
{
|
||||
Assert.AreEqual(expectedHtml, Markdown.ToHtml(value));
|
||||
Assert.AreEqual(expectedHtml, Markdown.ToHtml(value.Replace("\n", "\r\n")));
|
||||
}
|
||||
|
||||
[Test()]
|
||||
public void TestEscapeLineBreak()
|
||||
{
|
||||
var input = "test\\\r\ntest1\r\n";
|
||||
var doc = Markdown.Parse(input);
|
||||
var inlines = doc.Descendants<LineBreakInline>().ToList();
|
||||
Assert.AreEqual(1, inlines.Count, "Invalid number of LineBreakInline");
|
||||
Assert.True(inlines[0].IsBackslash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,18 +458,18 @@ This is a last line";
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null)
|
||||
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null, string context = null)
|
||||
{
|
||||
foreach (var pipeline in TestParser.GetPipeline(extensions))
|
||||
{
|
||||
AssertNormalize(inputText, expectedOutputText, trim: false, pipeline: pipeline.Value);
|
||||
AssertNormalize(inputText, expectedOutputText, trim: false, pipeline: pipeline.Value, context: context);
|
||||
}
|
||||
}
|
||||
|
||||
public static void AssertNormalizeNoTrim(string input, string expected = null, NormalizeOptions options = null)
|
||||
=> AssertNormalize(input, expected, false, options);
|
||||
|
||||
public static void AssertNormalize(string input, string expected = null, bool trim = true, NormalizeOptions options = null, MarkdownPipeline pipeline = null)
|
||||
public static void AssertNormalize(string input, string expected = null, bool trim = true, NormalizeOptions options = null, MarkdownPipeline pipeline = null, string context = null)
|
||||
{
|
||||
expected = expected ?? input;
|
||||
input = NormText(input, trim);
|
||||
@@ -484,7 +484,7 @@ This is a last line";
|
||||
var result = Markdown.Normalize(input, options, pipeline: pipeline);
|
||||
result = NormText(result, trim);
|
||||
|
||||
TestParser.PrintAssertExpected(input, result, expected);
|
||||
TestParser.PrintAssertExpected(input, result, expected, context);
|
||||
}
|
||||
|
||||
private static string NormText(string text, bool trim)
|
||||
|
||||
@@ -67,16 +67,20 @@ namespace Markdig.Tests
|
||||
TestDescendantsOrder.TestSchemas(specsSyntaxTrees);
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null, bool plainText = false)
|
||||
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null, bool plainText = false, string context = null)
|
||||
{
|
||||
context ??= string.Empty;
|
||||
if (!string.IsNullOrEmpty(context))
|
||||
{
|
||||
context += "\n";
|
||||
}
|
||||
foreach (var pipeline in GetPipeline(extensions))
|
||||
{
|
||||
Console.WriteLine($"Pipeline configured with extensions: {pipeline.Key}");
|
||||
TestSpec(inputText, expectedOutputText, pipeline.Value, plainText);
|
||||
TestSpec(inputText, expectedOutputText, pipeline.Value, plainText, context: context + $"Pipeline configured with extensions: {pipeline.Key}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, MarkdownPipeline pipeline, bool plainText = false)
|
||||
public static void TestSpec(string inputText, string expectedOutputText, MarkdownPipeline pipeline, bool plainText = false, string context = null)
|
||||
{
|
||||
// Uncomment this line to get more debug information for process inlines.
|
||||
//pipeline.DebugLog = Console.Out;
|
||||
@@ -85,20 +89,27 @@ namespace Markdig.Tests
|
||||
result = Compact(result);
|
||||
expectedOutputText = Compact(expectedOutputText);
|
||||
|
||||
PrintAssertExpected(inputText, result, expectedOutputText);
|
||||
PrintAssertExpected(inputText, result, expectedOutputText, context);
|
||||
}
|
||||
|
||||
public static void PrintAssertExpected(string source, string result, string expected)
|
||||
public static void PrintAssertExpected(string source, string result, string expected, string context = null)
|
||||
{
|
||||
Console.WriteLine("```````````````````Source");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(source));
|
||||
Console.WriteLine("```````````````````Result");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(result));
|
||||
Console.WriteLine("```````````````````Expected");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(expected));
|
||||
Console.WriteLine("```````````````````");
|
||||
Console.WriteLine();
|
||||
TextAssert.AreEqual(expected, result);
|
||||
if (expected != result)
|
||||
{
|
||||
if (context != null)
|
||||
{
|
||||
Console.WriteLine(context);
|
||||
}
|
||||
Console.WriteLine("```````````````````Source");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(source));
|
||||
Console.WriteLine("```````````````````Result");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(result));
|
||||
Console.WriteLine("```````````````````Expected");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(expected));
|
||||
Console.WriteLine("```````````````````");
|
||||
Console.WriteLine();
|
||||
TextAssert.AreEqual(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<KeyValuePair<string, MarkdownPipeline>> GetPipeline(string extensionsGroupText)
|
||||
@@ -108,6 +119,8 @@ namespace Markdig.Tests
|
||||
{
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>("default", new MarkdownPipelineBuilder().Build());
|
||||
|
||||
//yield return new KeyValuePair<string, MarkdownPipeline>("default-trivia", new MarkdownPipelineBuilder().EnableTrackTrivia().Build());
|
||||
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>("advanced", new MarkdownPipelineBuilder() // Use similar to advanced extension without auto-identifier
|
||||
.UseAbbreviations()
|
||||
//.UseAutoIdentifiers()
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Markdig.Tests
|
||||
[TestCase(/* markdownText: */ "# foo\nbar", /* expected: */ "foo\nbar\n")]
|
||||
[TestCase(/* markdownText: */ "> foo", /* expected: */ "foo\n")]
|
||||
[TestCase(/* markdownText: */ "> foo\nbar\n> baz", /* expected: */ "foo\nbar\nbaz\n")]
|
||||
[TestCase(/* markdownText: */ "`foo`", /* expected: */ "foo\n")]
|
||||
[TestCase(/* markdownText: */ "`foo`", /* expected: */ "foo\n")]
|
||||
[TestCase(/* markdownText: */ "`foo\nbar`", /* expected: */ "foo bar\n")] // new line within codespan is treated as whitespace (Example317)
|
||||
[TestCase(/* markdownText: */ "```\nfoo bar\n```", /* expected: */ "foo bar\n")]
|
||||
[TestCase(/* markdownText: */ "- foo\n- bar\n- baz", /* expected: */ "foo\nbar\nbaz\n")]
|
||||
@@ -23,7 +23,7 @@ namespace Markdig.Tests
|
||||
[TestCase(/* markdownText: */ "- foo<baz", /* expected: */ "foo<baz\n")]
|
||||
[TestCase(/* markdownText: */ "## foo `bar::baz >`", /* expected: */ "foo bar::baz >\n")]
|
||||
public void TestPlainEnsureNewLine(string markdownText, string expected)
|
||||
{
|
||||
{
|
||||
var actual = Markdown.ToPlainText(markdownText);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
@@ -31,14 +31,15 @@ namespace Markdig.Tests
|
||||
[Test]
|
||||
[TestCase(/* markdownText: */ ":::\nfoo\n:::", /* expected: */ "foo\n", /*extensions*/ "customcontainers|advanced")]
|
||||
[TestCase(/* markdownText: */ ":::bar\nfoo\n:::", /* expected: */ "foo\n", /*extensions*/ "customcontainers+attributes|advanced")]
|
||||
[TestCase(/* markdownText: */ "| Header1 | Header2 | Header3 |\n|--|--|--|\nt**es**t|value2|value3", /* expected: */ "Header1 Header2 Header3 test value2 value3","pipetables")]
|
||||
public void TestPlainWithExtensions(string markdownText, string expected, string extensions)
|
||||
{
|
||||
TestParser.TestSpec(markdownText, expected, extensions, plainText: true);
|
||||
}
|
||||
|
||||
public static void TestSpec(string markdownText, string expected, string extensions)
|
||||
public static void TestSpec(string markdownText, string expected, string extensions, string context = null)
|
||||
{
|
||||
TestParser.TestSpec(markdownText, expected, extensions, plainText: true);
|
||||
TestParser.TestSpec(markdownText, expected, extensions, plainText: true, context: context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,12 @@ namespace Markdig.Tests
|
||||
[TestFixture]
|
||||
public class TestPlayParser
|
||||
{
|
||||
[Test]
|
||||
public void TestBugWithEmphasisAndTable()
|
||||
{
|
||||
TestParser.TestSpec("**basics | 8:00**", "<p><strong>basics | 8:00</strong></p>", "advanced");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinksWithCarriageReturn()
|
||||
{
|
||||
@@ -55,7 +61,6 @@ namespace Markdig.Tests
|
||||
|
||||
Later in a text we are using HTML and it becomes an abbr tag HTML
|
||||
";
|
||||
|
||||
// var reader = new StringReader(@"> > toto tata
|
||||
//> titi toto
|
||||
//");
|
||||
@@ -63,7 +68,7 @@ Later in a text we are using HTML and it becomes an abbr tag HTML
|
||||
//var result = Markdown.ToHtml(text, new MarkdownPipeline().UseFootnotes().UseEmphasisExtras());
|
||||
var result = Markdown.ToHtml(text, new MarkdownPipelineBuilder().UseAbbreviations().Build());
|
||||
//File.WriteAllText("test.html", result, Encoding.UTF8);
|
||||
Console.WriteLine(result);
|
||||
//Console.WriteLine(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -7,24 +7,26 @@ namespace Markdig.Tests
|
||||
{
|
||||
internal static class TestRoundtrip
|
||||
{
|
||||
internal static void TestSpec(string markdownText, string expected, string extensions)
|
||||
internal static void TestSpec(string markdownText, string expected, string extensions, string context = null)
|
||||
{
|
||||
RoundTrip(markdownText);
|
||||
RoundTrip(markdownText, context);
|
||||
}
|
||||
|
||||
internal static void RoundTrip(string markdown)
|
||||
internal static void RoundTrip(string markdown, string context = null)
|
||||
{
|
||||
var pipelineBuilder = new MarkdownPipelineBuilder();
|
||||
pipelineBuilder.EnableTrackTrivia();
|
||||
pipelineBuilder.UseYamlFrontMatter();
|
||||
MarkdownPipeline pipeline = pipelineBuilder.Build();
|
||||
MarkdownDocument markdownDocument = Markdown.Parse(markdown, pipeline);
|
||||
var sw = new StringWriter();
|
||||
var nr = new RoundtripRenderer(sw);
|
||||
pipeline.Setup(nr);
|
||||
|
||||
nr.Write(markdownDocument);
|
||||
|
||||
var result = sw.ToString();
|
||||
Assert.AreEqual(markdown, result);
|
||||
TestParser.PrintAssertExpected("", result, markdown, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
102
src/Markdig.Tests/TestTransformedStringCache.cs
Normal file
102
src/Markdig.Tests/TestTransformedStringCache.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Markdig.Helpers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class TestTransformedStringCache
|
||||
{
|
||||
[Test]
|
||||
public void GetRunsTransformationCallback()
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => "callback-" + s);
|
||||
|
||||
Assert.AreEqual("callback-foo", cache.Get("foo"));
|
||||
Assert.AreEqual("callback-bar", cache.Get("bar"));
|
||||
Assert.AreEqual("callback-baz", cache.Get("baz"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CachesTransformedInstance()
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => "callback-" + s);
|
||||
|
||||
string transformedBar = cache.Get("bar");
|
||||
Assert.AreSame(transformedBar, cache.Get("bar"));
|
||||
|
||||
string transformedFoo = cache.Get("foo".AsSpan());
|
||||
Assert.AreSame(transformedFoo, cache.Get("foo"));
|
||||
|
||||
Assert.AreSame(cache.Get("baz"), cache.Get("baz".AsSpan()));
|
||||
|
||||
Assert.AreSame(transformedBar, cache.Get("bar"));
|
||||
Assert.AreSame(transformedFoo, cache.Get("foo"));
|
||||
Assert.AreSame(transformedBar, cache.Get("bar".AsSpan()));
|
||||
Assert.AreSame(transformedFoo, cache.Get("foo".AsSpan()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoesNotCacheEmptyInputs()
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => new string('a', 4));
|
||||
|
||||
string cached = cache.Get("");
|
||||
string cached2 = cache.Get("");
|
||||
string cached3 = cache.Get(ReadOnlySpan<char>.Empty);
|
||||
|
||||
Assert.AreEqual("aaaa", cached);
|
||||
Assert.AreEqual(cached, cached2);
|
||||
Assert.AreEqual(cached, cached3);
|
||||
|
||||
Assert.AreNotSame(cached, cached2);
|
||||
Assert.AreNotSame(cached, cached3);
|
||||
Assert.AreNotSame(cached2, cached3);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(TransformedStringCache.InputLengthLimit, true)]
|
||||
[TestCase(TransformedStringCache.InputLengthLimit + 1, false)]
|
||||
public void DoesNotCacheLongInputs(int length, bool shouldBeCached)
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => "callback-" + s);
|
||||
|
||||
string input = new string('a', length);
|
||||
|
||||
string cached = cache.Get(input);
|
||||
string cached2 = cache.Get(input);
|
||||
|
||||
Assert.AreEqual("callback-" + input, cached);
|
||||
Assert.AreEqual(cached, cached2);
|
||||
|
||||
if (shouldBeCached)
|
||||
{
|
||||
Assert.AreSame(cached, cached2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.AreNotSame(cached, cached2);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CachesAtMostNEntriesPerCharacter()
|
||||
{
|
||||
var cache = new TransformedStringCache(static s => "callback-" + s);
|
||||
|
||||
int limit = TransformedStringCache.MaxEntriesPerCharacter;
|
||||
|
||||
string[] a = Enumerable.Range(1, limit + 1).Select(i => $"a{i}").ToArray();
|
||||
string[] cachedAs = a.Select(a => cache.Get(a)).ToArray();
|
||||
|
||||
for (int i = 0; i < limit; i++)
|
||||
{
|
||||
Assert.AreSame(cachedAs[i], cache.Get(a[i]));
|
||||
}
|
||||
|
||||
Assert.AreNotSame(cachedAs[limit], cache.Get(a[limit]));
|
||||
|
||||
Assert.AreSame(cache.Get("b1"), cache.Get("b1"));
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/Markdig.Tests/TestYamlFrontMatterExtension.cs
Normal file
78
src/Markdig.Tests/TestYamlFrontMatterExtension.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Markdig.Extensions.Yaml;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class TestYamlFrontMatterExtension
|
||||
{
|
||||
[TestCaseSource(nameof(TestCases))]
|
||||
public void ProperYamlFrontMatterRenderersAdded(IMarkdownObjectRenderer[] objectRenderers, bool hasYamlFrontMatterHtmlRenderer, bool hasYamlFrontMatterRoundtripRenderer)
|
||||
{
|
||||
var builder = new MarkdownPipelineBuilder();
|
||||
builder.Extensions.Add(new YamlFrontMatterExtension());
|
||||
var markdownRenderer = new DummyRenderer();
|
||||
markdownRenderer.ObjectRenderers.AddRange(objectRenderers);
|
||||
builder.Build().Setup(markdownRenderer);
|
||||
Assert.That(markdownRenderer.ObjectRenderers.Contains<YamlFrontMatterHtmlRenderer>(), Is.EqualTo(hasYamlFrontMatterHtmlRenderer));
|
||||
Assert.That(markdownRenderer.ObjectRenderers.Contains<YamlFrontMatterRoundtripRenderer>(), Is.EqualTo(hasYamlFrontMatterRoundtripRenderer));
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> TestCases()
|
||||
{
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
}, false, false) {TestName = "No ObjectRenderers"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Html.CodeBlockRenderer()
|
||||
}, true, false) {TestName = "Html CodeBlock"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Roundtrip.CodeBlockRenderer()
|
||||
}, false, true) {TestName = "Roundtrip CodeBlock"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Html.CodeBlockRenderer(),
|
||||
new Markdig.Renderers.Roundtrip.CodeBlockRenderer()
|
||||
}, true, true) {TestName = "Html/Roundtrip CodeBlock"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Html.CodeBlockRenderer(),
|
||||
new Markdig.Renderers.Roundtrip.CodeBlockRenderer(),
|
||||
new YamlFrontMatterHtmlRenderer()
|
||||
}, true, true) {TestName = "Html/Roundtrip CodeBlock, Yaml Html"};
|
||||
|
||||
yield return new TestCaseData(new IMarkdownObjectRenderer[]
|
||||
{
|
||||
new Markdig.Renderers.Html.CodeBlockRenderer(),
|
||||
new Markdig.Renderers.Roundtrip.CodeBlockRenderer(),
|
||||
new YamlFrontMatterRoundtripRenderer()
|
||||
}, true, true) { TestName = "Html/Roundtrip CodeBlock, Yaml Roundtrip" };
|
||||
}
|
||||
|
||||
private class DummyRenderer : IMarkdownRenderer
|
||||
{
|
||||
public DummyRenderer()
|
||||
{
|
||||
ObjectRenderers = new ObjectRendererCollection();
|
||||
}
|
||||
|
||||
public event Action<IMarkdownRenderer, MarkdownObject> ObjectWriteBefore;
|
||||
public event Action<IMarkdownRenderer, MarkdownObject> ObjectWriteAfter;
|
||||
public ObjectRendererCollection ObjectRenderers { get; }
|
||||
public object Render(MarkdownObject markdownObject)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,17 +172,22 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
var baseHeadingId = string.IsNullOrEmpty(headingText) ? "section" : headingText;
|
||||
|
||||
// Add a trailing -1, -2, -3...etc. in case of collision
|
||||
int index = 0;
|
||||
var headingId = baseHeadingId;
|
||||
var headingBuffer = StringBuilderCache.Local();
|
||||
while (!identifiers.Add(headingId))
|
||||
if (!identifiers.Add(headingId))
|
||||
{
|
||||
index++;
|
||||
var headingBuffer = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
headingBuffer.Append(baseHeadingId);
|
||||
headingBuffer.Append('-');
|
||||
headingBuffer.Append(index);
|
||||
headingId = headingBuffer.ToString();
|
||||
headingBuffer.Length = 0;
|
||||
uint index = 0;
|
||||
do
|
||||
{
|
||||
index++;
|
||||
headingBuffer.Append(index);
|
||||
headingId = headingBuffer.AsSpan().ToString();
|
||||
headingBuffer.Length = baseHeadingId.Length + 1;
|
||||
}
|
||||
while (!identifiers.Add(headingId));
|
||||
headingBuffer.Dispose();
|
||||
}
|
||||
|
||||
attributes.Id = headingId;
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Normalize;
|
||||
using Markdig.Renderers.Normalize.Inlines;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.AutoLinks
|
||||
@@ -33,10 +31,6 @@ namespace Markdig.Extensions.AutoLinks
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
if (renderer is NormalizeRenderer normalizeRenderer && !normalizeRenderer.ObjectRenderers.Contains<NormalizeAutoLinkRenderer>())
|
||||
{
|
||||
normalizeRenderer.ObjectRenderers.InsertBefore<LinkInlineRenderer>(new NormalizeAutoLinkRenderer());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,53 @@ namespace Markdig.Extensions.AutoLinks
|
||||
return false;
|
||||
}
|
||||
|
||||
var startPosition = slice.Start;
|
||||
int domainOffset = 0;
|
||||
|
||||
var c = slice.CurrentChar;
|
||||
// Precheck URL
|
||||
switch (c)
|
||||
{
|
||||
case 'h':
|
||||
if (slice.MatchLowercase("ttp://", 1))
|
||||
{
|
||||
domainOffset = 7; // http://
|
||||
}
|
||||
else if (slice.MatchLowercase("ttps://", 1))
|
||||
{
|
||||
domainOffset = 8; // https://
|
||||
}
|
||||
else return false;
|
||||
break;
|
||||
case 'f':
|
||||
if (!slice.MatchLowercase("tp://", 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
domainOffset = 6; // ftp://
|
||||
break;
|
||||
case 'm':
|
||||
if (!slice.MatchLowercase("ailto:", 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (!slice.MatchLowercase("el:", 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
domainOffset = 4;
|
||||
break;
|
||||
case 'w':
|
||||
if (!slice.MatchLowercase("ww.", 1)) // We won't match http:/www. or /www.xxx
|
||||
{
|
||||
return false;
|
||||
}
|
||||
domainOffset = 4; // www.
|
||||
break;
|
||||
}
|
||||
|
||||
List<char> pendingEmphasis = _listOfCharCache.Get();
|
||||
try
|
||||
{
|
||||
@@ -58,53 +105,6 @@ namespace Markdig.Extensions.AutoLinks
|
||||
return false;
|
||||
}
|
||||
|
||||
var startPosition = slice.Start;
|
||||
int domainOffset = 0;
|
||||
|
||||
var c = slice.CurrentChar;
|
||||
// Precheck URL
|
||||
switch (c)
|
||||
{
|
||||
case 'h':
|
||||
if (slice.MatchLowercase("ttp://", 1))
|
||||
{
|
||||
domainOffset = 7; // http://
|
||||
}
|
||||
else if (slice.MatchLowercase("ttps://", 1))
|
||||
{
|
||||
domainOffset = 8; // https://
|
||||
}
|
||||
else return false;
|
||||
break;
|
||||
case 'f':
|
||||
if (!slice.MatchLowercase("tp://", 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
domainOffset = 6; // ftp://
|
||||
break;
|
||||
case 'm':
|
||||
if (!slice.MatchLowercase("ailto:", 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (!slice.MatchLowercase("el:", 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
domainOffset = 4;
|
||||
break;
|
||||
case 'w':
|
||||
if (!slice.MatchLowercase("ww.", 1)) // We won't match http:/www. or /www.xxx
|
||||
{
|
||||
return false;
|
||||
}
|
||||
domainOffset = 4; // www.
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse URL
|
||||
if (!LinkHelper.TryParseUrl(ref slice, out string? link, out _, true))
|
||||
{
|
||||
|
||||
@@ -1,33 +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 Markdig.Renderers;
|
||||
using Markdig.Renderers.Normalize;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.AutoLinks
|
||||
{
|
||||
public class NormalizeAutoLinkRenderer : NormalizeObjectRenderer<LinkInline>
|
||||
{
|
||||
public override bool Accept(RendererBase renderer, MarkdownObject obj)
|
||||
{
|
||||
if (base.Accept(renderer, obj))
|
||||
{
|
||||
return renderer is NormalizeRenderer normalizeRenderer
|
||||
&& obj is LinkInline link
|
||||
&& !normalizeRenderer.Options.ExpandAutoLinks
|
||||
&& link.IsAutoLink;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
protected override void Write(NormalizeRenderer renderer, LinkInline obj)
|
||||
{
|
||||
renderer.Write(obj.Url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,16 @@ namespace Markdig.Extensions.Bootstrap
|
||||
|
||||
private static void PipelineOnDocumentProcessed(MarkdownDocument document)
|
||||
{
|
||||
foreach(var node in document.Descendants())
|
||||
foreach (var node in document.Descendants())
|
||||
{
|
||||
if (node is Block)
|
||||
if (node.IsInline)
|
||||
{
|
||||
if (node.IsContainerInline && node is LinkInline link && link.IsImage)
|
||||
{
|
||||
link.GetAttributes().AddClass("img-fluid");
|
||||
}
|
||||
}
|
||||
else if (node.IsContainerBlock)
|
||||
{
|
||||
if (node is Tables.Table)
|
||||
{
|
||||
@@ -44,18 +51,14 @@ namespace Markdig.Extensions.Bootstrap
|
||||
{
|
||||
node.GetAttributes().AddClass("figure");
|
||||
}
|
||||
else if (node is Figures.FigureCaption)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (node is Figures.FigureCaption)
|
||||
{
|
||||
node.GetAttributes().AddClass("figure-caption");
|
||||
}
|
||||
}
|
||||
else if (node is Inline)
|
||||
{
|
||||
if (node is LinkInline link && link.IsImage)
|
||||
{
|
||||
link.GetAttributes().AddClass("img-fluid");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,20 +93,23 @@ namespace Markdig.Extensions.Globalization
|
||||
{
|
||||
for (int i = slice.Start; i <= slice.End; i++)
|
||||
{
|
||||
if (slice[i] < 128)
|
||||
char c = slice[i];
|
||||
if (c < 128)
|
||||
{
|
||||
if (CharHelper.IsAlpha(c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int rune;
|
||||
if (CharHelper.IsHighSurrogate(slice[i]) && i < slice.End && CharHelper.IsLowSurrogate(slice[i + 1]))
|
||||
int rune = c;
|
||||
if (CharHelper.IsHighSurrogate(c) && i < slice.End && CharHelper.IsLowSurrogate(slice[i + 1]))
|
||||
{
|
||||
Debug.Assert(char.IsSurrogatePair(slice[i], slice[i + 1]));
|
||||
rune = char.ConvertToUtf32(slice[i], slice[i + 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
rune = slice[i];
|
||||
Debug.Assert(char.IsSurrogatePair(c, slice[i + 1]));
|
||||
rune = char.ConvertToUtf32(c, slice[i + 1]);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (CharHelper.IsRightToLeft(rune))
|
||||
|
||||
@@ -95,13 +95,19 @@ namespace Markdig.Extensions.JiraLinks
|
||||
jiraLink.Span.End = jiraLink.Span.Start + (endIssue - startKey);
|
||||
|
||||
// Builds the Url
|
||||
var builder = StringBuilderCache.Local();
|
||||
builder.Append(_baseUrl).Append('/').Append(jiraLink.ProjectKey).Append('-').Append(jiraLink.Issue);
|
||||
jiraLink.Url = builder.ToString();
|
||||
var builder = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
builder.Append(_baseUrl);
|
||||
builder.Append('/');
|
||||
builder.Append(jiraLink.ProjectKey.AsSpan());
|
||||
builder.Append('-');
|
||||
builder.Append(jiraLink.Issue.AsSpan());
|
||||
jiraLink.Url = builder.AsSpan().ToString();
|
||||
|
||||
// Builds the Label
|
||||
builder.Length = 0;
|
||||
builder.Append(jiraLink.ProjectKey).Append('-').Append(jiraLink.Issue);
|
||||
builder.Append(jiraLink.ProjectKey.AsSpan());
|
||||
builder.Append('-');
|
||||
builder.Append(jiraLink.Issue.AsSpan());
|
||||
jiraLink.AppendChild(new LiteralInline(builder.ToString()));
|
||||
|
||||
if (_options.OpenInNewWindow)
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
// 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 Markdig.Helpers;
|
||||
using System;
|
||||
|
||||
namespace Markdig.Extensions.JiraLinks
|
||||
{
|
||||
@@ -38,20 +39,10 @@ namespace Markdig.Extensions.JiraLinks
|
||||
/// </summary>
|
||||
public virtual string GetUrl()
|
||||
{
|
||||
var url = new StringBuilder();
|
||||
var baseUrl = BaseUrl;
|
||||
if (baseUrl != null)
|
||||
{
|
||||
url.Append(baseUrl.TrimEnd('/'));
|
||||
}
|
||||
|
||||
url.Append("/");
|
||||
|
||||
if (BasePath != null)
|
||||
{
|
||||
url.Append(BasePath.Trim('/'));
|
||||
}
|
||||
|
||||
var url = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
url.Append(BaseUrl.AsSpan().TrimEnd('/'));
|
||||
url.Append('/');
|
||||
url.Append(BasePath.AsSpan().Trim('/'));
|
||||
return url.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,122 +17,154 @@ namespace Markdig.Extensions.Tables
|
||||
{
|
||||
protected override void Write(HtmlRenderer renderer, Table table)
|
||||
{
|
||||
renderer.EnsureLine();
|
||||
renderer.Write("<table").WriteAttributes(table).WriteLine('>');
|
||||
|
||||
bool hasBody = false;
|
||||
bool hasAlreadyHeader = false;
|
||||
bool isHeaderOpen = false;
|
||||
|
||||
|
||||
bool hasColumnWidth = false;
|
||||
foreach (var tableColumnDefinition in table.ColumnDefinitions)
|
||||
if (renderer.EnableHtmlForBlock)
|
||||
{
|
||||
if (tableColumnDefinition.Width != 0.0f && tableColumnDefinition.Width != 1.0f)
|
||||
{
|
||||
hasColumnWidth = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
renderer.EnsureLine();
|
||||
renderer.Write("<table").WriteAttributes(table).WriteLine('>');
|
||||
|
||||
if (hasColumnWidth)
|
||||
{
|
||||
bool hasBody = false;
|
||||
bool hasAlreadyHeader = false;
|
||||
bool isHeaderOpen = false;
|
||||
|
||||
|
||||
bool hasColumnWidth = false;
|
||||
foreach (var tableColumnDefinition in table.ColumnDefinitions)
|
||||
{
|
||||
var width = Math.Round(tableColumnDefinition.Width*100)/100;
|
||||
var widthValue = string.Format(CultureInfo.InvariantCulture, "{0:0.##}", width);
|
||||
renderer.WriteLine($"<col style=\"width:{widthValue}%\" />");
|
||||
if (tableColumnDefinition.Width != 0.0f && tableColumnDefinition.Width != 1.0f)
|
||||
{
|
||||
hasColumnWidth = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var rowObj in table)
|
||||
{
|
||||
var row = (TableRow)rowObj;
|
||||
if (row.IsHeader)
|
||||
if (hasColumnWidth)
|
||||
{
|
||||
// Allow a single thead
|
||||
if (!hasAlreadyHeader)
|
||||
foreach (var tableColumnDefinition in table.ColumnDefinitions)
|
||||
{
|
||||
renderer.WriteLine("<thead>");
|
||||
isHeaderOpen = true;
|
||||
var width = Math.Round(tableColumnDefinition.Width * 100) / 100;
|
||||
var widthValue = string.Format(CultureInfo.InvariantCulture, "{0:0.##}", width);
|
||||
renderer.WriteLine($"<col style=\"width:{widthValue}%\" />");
|
||||
}
|
||||
hasAlreadyHeader = true;
|
||||
}
|
||||
else if (!hasBody)
|
||||
{
|
||||
if (isHeaderOpen)
|
||||
{
|
||||
renderer.WriteLine("</thead>");
|
||||
isHeaderOpen = false;
|
||||
}
|
||||
|
||||
renderer.WriteLine("<tbody>");
|
||||
hasBody = true;
|
||||
}
|
||||
renderer.Write("<tr").WriteAttributes(row).WriteLine('>');
|
||||
for (int i = 0; i < row.Count; i++)
|
||||
foreach (var rowObj in table)
|
||||
{
|
||||
var cellObj = row[i];
|
||||
var cell = (TableCell)cellObj;
|
||||
|
||||
renderer.EnsureLine();
|
||||
renderer.Write(row.IsHeader ? "<th" : "<td");
|
||||
if (cell.ColumnSpan != 1)
|
||||
var row = (TableRow)rowObj;
|
||||
if (row.IsHeader)
|
||||
{
|
||||
renderer.Write($" colspan=\"{cell.ColumnSpan}\"");
|
||||
}
|
||||
if (cell.RowSpan != 1)
|
||||
{
|
||||
renderer.Write($" rowspan=\"{cell.RowSpan}\"");
|
||||
}
|
||||
if (table.ColumnDefinitions.Count > 0)
|
||||
{
|
||||
var columnIndex = cell.ColumnIndex < 0 || cell.ColumnIndex >= table.ColumnDefinitions.Count
|
||||
? i
|
||||
: cell.ColumnIndex;
|
||||
columnIndex = columnIndex >= table.ColumnDefinitions.Count ? table.ColumnDefinitions.Count - 1 : columnIndex;
|
||||
var alignment = table.ColumnDefinitions[columnIndex].Alignment;
|
||||
if (alignment.HasValue)
|
||||
// Allow a single thead
|
||||
if (!hasAlreadyHeader)
|
||||
{
|
||||
switch (alignment)
|
||||
renderer.WriteLine("<thead>");
|
||||
isHeaderOpen = true;
|
||||
}
|
||||
|
||||
hasAlreadyHeader = true;
|
||||
}
|
||||
else if (!hasBody)
|
||||
{
|
||||
if (isHeaderOpen)
|
||||
{
|
||||
renderer.WriteLine("</thead>");
|
||||
isHeaderOpen = false;
|
||||
}
|
||||
|
||||
renderer.WriteLine("<tbody>");
|
||||
hasBody = true;
|
||||
}
|
||||
|
||||
renderer.Write("<tr").WriteAttributes(row).WriteLine('>');
|
||||
for (int i = 0; i < row.Count; i++)
|
||||
{
|
||||
var cellObj = row[i];
|
||||
var cell = (TableCell)cellObj;
|
||||
|
||||
renderer.EnsureLine();
|
||||
renderer.Write(row.IsHeader ? "<th" : "<td");
|
||||
if (cell.ColumnSpan != 1)
|
||||
{
|
||||
renderer.Write($" colspan=\"{cell.ColumnSpan}\"");
|
||||
}
|
||||
|
||||
if (cell.RowSpan != 1)
|
||||
{
|
||||
renderer.Write($" rowspan=\"{cell.RowSpan}\"");
|
||||
}
|
||||
|
||||
if (table.ColumnDefinitions.Count > 0)
|
||||
{
|
||||
var columnIndex = cell.ColumnIndex < 0 || cell.ColumnIndex >= table.ColumnDefinitions.Count
|
||||
? i
|
||||
: cell.ColumnIndex;
|
||||
columnIndex = columnIndex >= table.ColumnDefinitions.Count ? table.ColumnDefinitions.Count - 1 : columnIndex;
|
||||
var alignment = table.ColumnDefinitions[columnIndex].Alignment;
|
||||
if (alignment.HasValue)
|
||||
{
|
||||
case TableColumnAlign.Center:
|
||||
renderer.Write(" style=\"text-align: center;\"");
|
||||
break;
|
||||
case TableColumnAlign.Right:
|
||||
renderer.Write(" style=\"text-align: right;\"");
|
||||
break;
|
||||
case TableColumnAlign.Left:
|
||||
renderer.Write(" style=\"text-align: left;\"");
|
||||
break;
|
||||
switch (alignment)
|
||||
{
|
||||
case TableColumnAlign.Center:
|
||||
renderer.Write(" style=\"text-align: center;\"");
|
||||
break;
|
||||
case TableColumnAlign.Right:
|
||||
renderer.Write(" style=\"text-align: right;\"");
|
||||
break;
|
||||
case TableColumnAlign.Left:
|
||||
renderer.Write(" style=\"text-align: left;\"");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
renderer.WriteAttributes(cell);
|
||||
renderer.Write('>');
|
||||
|
||||
var previousImplicitParagraph = renderer.ImplicitParagraph;
|
||||
if (cell.Count == 1)
|
||||
{
|
||||
renderer.ImplicitParagraph = true;
|
||||
}
|
||||
renderer.Write(cell);
|
||||
renderer.ImplicitParagraph = previousImplicitParagraph;
|
||||
renderer.WriteAttributes(cell);
|
||||
renderer.Write('>');
|
||||
|
||||
renderer.WriteLine(row.IsHeader ? "</th>" : "</td>");
|
||||
var previousImplicitParagraph = renderer.ImplicitParagraph;
|
||||
if (cell.Count == 1)
|
||||
{
|
||||
renderer.ImplicitParagraph = true;
|
||||
}
|
||||
|
||||
renderer.Write(cell);
|
||||
renderer.ImplicitParagraph = previousImplicitParagraph;
|
||||
|
||||
renderer.WriteLine(row.IsHeader ? "</th>" : "</td>");
|
||||
}
|
||||
|
||||
renderer.WriteLine("</tr>");
|
||||
}
|
||||
renderer.WriteLine("</tr>");
|
||||
}
|
||||
|
||||
if (hasBody)
|
||||
{
|
||||
renderer.WriteLine("</tbody>");
|
||||
if (hasBody)
|
||||
{
|
||||
renderer.WriteLine("</tbody>");
|
||||
}
|
||||
else if (isHeaderOpen)
|
||||
{
|
||||
renderer.WriteLine("</thead>");
|
||||
}
|
||||
|
||||
renderer.WriteLine("</table>");
|
||||
}
|
||||
else if (isHeaderOpen)
|
||||
else
|
||||
{
|
||||
renderer.WriteLine("</thead>");
|
||||
//no html, just write the table contents
|
||||
var impliciParagraph = renderer.ImplicitParagraph;
|
||||
|
||||
//enable implicit paragraphs to avoid newlines after each cell
|
||||
renderer.ImplicitParagraph = true;
|
||||
foreach (var rowObj in table)
|
||||
{
|
||||
var row = (TableRow)rowObj;
|
||||
for (int i = 0; i < row.Count; i++)
|
||||
{
|
||||
var cellObj = row[i];
|
||||
var cell = (TableCell)cellObj;
|
||||
renderer.Write(cell);
|
||||
//write a space after each cell to avoid text being merged with the next cell
|
||||
renderer.Write(' ');
|
||||
}
|
||||
}
|
||||
renderer.ImplicitParagraph = impliciParagraph;
|
||||
}
|
||||
renderer.WriteLine("</table>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Markdig.Extensions.Tables
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
// Only working on Paragraph block
|
||||
if (!(processor.Block is ParagraphBlock))
|
||||
if (!processor.Block!.IsParagraphBlock)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,10 +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 Markdig.Parsers;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
|
||||
namespace Markdig.Extensions.Yaml
|
||||
{
|
||||
@@ -24,9 +23,14 @@ namespace Markdig.Extensions.Yaml
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
if (!renderer.ObjectRenderers.Contains<YamlFrontMatterRenderer>())
|
||||
if (!renderer.ObjectRenderers.Contains<YamlFrontMatterHtmlRenderer>())
|
||||
{
|
||||
renderer.ObjectRenderers.InsertBefore<CodeBlockRenderer>(new YamlFrontMatterRenderer());
|
||||
renderer.ObjectRenderers.InsertBefore<Renderers.Html.CodeBlockRenderer>(new YamlFrontMatterHtmlRenderer());
|
||||
}
|
||||
|
||||
if (!renderer.ObjectRenderers.Contains<YamlFrontMatterRoundtripRenderer>())
|
||||
{
|
||||
renderer.ObjectRenderers.InsertBefore<Renderers.Roundtrip.CodeBlockRenderer>(new YamlFrontMatterRoundtripRenderer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Markdig.Extensions.Yaml
|
||||
/// Empty renderer for a <see cref="YamlFrontMatterBlock"/>
|
||||
/// </summary>
|
||||
/// <seealso cref="HtmlObjectRenderer{YamlFrontMatterBlock}" />
|
||||
public class YamlFrontMatterRenderer : HtmlObjectRenderer<YamlFrontMatterBlock>
|
||||
public class YamlFrontMatterHtmlRenderer : HtmlObjectRenderer<YamlFrontMatterBlock>
|
||||
{
|
||||
protected override void Write(HtmlRenderer renderer, YamlFrontMatterBlock obj)
|
||||
{
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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.Renderers;
|
||||
using Markdig.Renderers.Roundtrip;
|
||||
|
||||
namespace Markdig.Extensions.Yaml
|
||||
{
|
||||
public class YamlFrontMatterRoundtripRenderer : MarkdownObjectRenderer<RoundtripRenderer, YamlFrontMatterBlock>
|
||||
{
|
||||
private readonly CodeBlockRenderer _codeBlockRenderer;
|
||||
|
||||
public YamlFrontMatterRoundtripRenderer()
|
||||
{
|
||||
_codeBlockRenderer = new CodeBlockRenderer();
|
||||
}
|
||||
|
||||
protected override void Write(RoundtripRenderer renderer, YamlFrontMatterBlock obj)
|
||||
{
|
||||
renderer.Writer.WriteLine("---");
|
||||
_codeBlockRenderer.Write(renderer, obj);
|
||||
renderer.Writer.WriteLine("---");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
30
src/Markdig/Helpers/BlockWrapper.cs
Normal file
30
src/Markdig/Helpers/BlockWrapper.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Syntax;
|
||||
using System;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
// Used to avoid the overhead of type covariance checks
|
||||
internal readonly struct BlockWrapper : IEquatable<BlockWrapper>
|
||||
{
|
||||
public readonly Block Block;
|
||||
|
||||
public BlockWrapper(Block block)
|
||||
{
|
||||
Block = block;
|
||||
}
|
||||
|
||||
public static implicit operator Block(BlockWrapper wrapper) => wrapper.Block;
|
||||
|
||||
public static implicit operator BlockWrapper(Block block) => new BlockWrapper(block);
|
||||
|
||||
public bool Equals(BlockWrapper other) => ReferenceEquals(Block, other.Block);
|
||||
|
||||
public override bool Equals(object obj) => Block.Equals(obj);
|
||||
|
||||
public override int GetHashCode() => Block.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -744,7 +744,7 @@ namespace Markdig.Helpers
|
||||
return cache[number];
|
||||
}
|
||||
|
||||
return number.ToString();
|
||||
return number.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,11 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
using System.Numerics;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
#endif
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
@@ -16,6 +21,10 @@ namespace Markdig.Helpers
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public sealed class CharacterMap<T> where T : class
|
||||
{
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
private readonly Vector128<byte> _asciiBitmap;
|
||||
#endif
|
||||
|
||||
private readonly T[] asciiMap;
|
||||
private readonly Dictionary<uint, T>? nonAsciiMap;
|
||||
private readonly BoolVector128 isOpeningCharacter;
|
||||
@@ -39,6 +48,11 @@ namespace Markdig.Helpers
|
||||
if (openingChar < 128)
|
||||
{
|
||||
maxChar = Math.Max(maxChar, openingChar);
|
||||
|
||||
if (openingChar == 0)
|
||||
{
|
||||
ThrowHelper.ArgumentOutOfRangeException("Null is not a valid opening character.", nameof(maps));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -64,6 +78,23 @@ namespace Markdig.Helpers
|
||||
nonAsciiMap[openingChar] = state.Value;
|
||||
}
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
if (nonAsciiMap is null)
|
||||
{
|
||||
long bitmap_0_3 = 0;
|
||||
long bitmap_4_7 = 0;
|
||||
|
||||
foreach (char openingChar in OpeningCharacters)
|
||||
{
|
||||
int position = (openingChar >> 4) | ((openingChar & 0x0F) << 3);
|
||||
if (position < 64) bitmap_0_3 |= 1L << position;
|
||||
else bitmap_4_7 |= 1L << (position - 64);
|
||||
}
|
||||
|
||||
_asciiBitmap = Vector128.Create(bitmap_0_3, bitmap_4_7).AsByte();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -105,9 +136,63 @@ 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)
|
||||
{
|
||||
Debug.Assert(text is not null);
|
||||
Debug.Assert(start >= 0 && end >= 0);
|
||||
Debug.Assert(end - start + 1 >= 0);
|
||||
Debug.Assert(end - start + 1 <= text.Length);
|
||||
|
||||
if (nonAsciiMap is null)
|
||||
{
|
||||
#if NETCOREAPP3_1
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
if (Ssse3.IsSupported && BitConverter.IsLittleEndian)
|
||||
{
|
||||
// Based on http://0x80.pl/articles/simd-byte-lookup.html#universal-algorithm
|
||||
// Optimized for sets in the [1, 127] range
|
||||
|
||||
int lengthMinusOne = end - start;
|
||||
int charsToProcessVectorized = lengthMinusOne & ~(2 * Vector128<short>.Count - 1);
|
||||
int finalStart = start + charsToProcessVectorized;
|
||||
|
||||
if (start < finalStart)
|
||||
{
|
||||
ref char textStartRef = ref Unsafe.Add(ref Unsafe.AsRef(in text.GetPinnableReference()), start);
|
||||
Vector128<byte> bitmap = _asciiBitmap;
|
||||
do
|
||||
{
|
||||
// Load 32 bytes (16 chars) into two Vector128<short>s (chars)
|
||||
// Drop the high byte of each char
|
||||
// Pack the remaining bytes into a single Vector128<byte>
|
||||
Vector128<byte> input = Sse2.PackUnsignedSaturate(
|
||||
Unsafe.ReadUnaligned<Vector128<short>>(ref Unsafe.As<char, byte>(ref textStartRef)),
|
||||
Unsafe.ReadUnaligned<Vector128<short>>(ref Unsafe.As<char, byte>(ref Unsafe.Add(ref textStartRef, Vector128<short>.Count))));
|
||||
|
||||
// Extract the higher nibble of each character ((input >> 4) & 0xF)
|
||||
Vector128<byte> higherNibbles = Sse2.And(Sse2.ShiftRightLogical(input.AsUInt16(), 4).AsByte(), Vector128.Create((byte)0xF));
|
||||
|
||||
// Lookup the matching higher nibble for each character based on the lower nibble
|
||||
// PSHUFB will set the result to 0 for any non-ASCII (> 127) character
|
||||
Vector128<byte> bitsets = Ssse3.Shuffle(bitmap, input);
|
||||
|
||||
// Calculate a bitmask (1 << (higherNibble % 8)) for each character
|
||||
Vector128<byte> bitmask = Ssse3.Shuffle(Vector128.Create(0x8040201008040201).AsByte(), higherNibbles);
|
||||
|
||||
// Check which characters are present in the set
|
||||
// We are relying on bitsets being zero for non-ASCII characters
|
||||
Vector128<byte> result = Sse2.And(bitsets, bitmask);
|
||||
|
||||
if (!result.Equals(Vector128<byte>.Zero))
|
||||
{
|
||||
int resultMask = ~Sse2.MoveMask(Sse2.CompareEqual(result, Vector128<byte>.Zero));
|
||||
return start + BitOperations.TrailingZeroCount((uint)resultMask);
|
||||
}
|
||||
|
||||
start += 2 * Vector128<short>.Count;
|
||||
textStartRef = ref Unsafe.Add(ref textStartRef, 2 * Vector128<short>.Count);
|
||||
}
|
||||
while (start != finalStart);
|
||||
}
|
||||
}
|
||||
|
||||
ref char textRef = ref Unsafe.AsRef(in text.GetPinnableReference());
|
||||
for (; start <= end; start++)
|
||||
{
|
||||
@@ -154,7 +239,7 @@ namespace Markdig.Helpers
|
||||
|
||||
private int IndexOfOpeningCharacterNonAscii(string text, int start, int end)
|
||||
{
|
||||
#if NETCOREAPP3_1
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
ref char textRef = ref Unsafe.AsRef(in text.GetPinnableReference());
|
||||
for (int i = start; i <= end; i++)
|
||||
{
|
||||
|
||||
@@ -19,8 +19,6 @@ using System.Runtime.CompilerServices;
|
||||
* Please note the lack of a Remove method on this data structure
|
||||
*/
|
||||
|
||||
// Relies on NETCORE being set for selective use of Spans
|
||||
|
||||
//namespace SharpCollections.Generic
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
@@ -69,7 +68,7 @@ namespace Markdig.Helpers
|
||||
|
||||
utf32 -= 65536;
|
||||
return new string(
|
||||
#if NETCORE
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
stackalloc
|
||||
#else
|
||||
new
|
||||
@@ -81,7 +80,7 @@ namespace Markdig.Helpers
|
||||
});
|
||||
}
|
||||
|
||||
public static void DecodeEntity(int utf32, StringBuilder sb)
|
||||
internal static void DecodeEntity(int utf32, ref ValueStringBuilder sb)
|
||||
{
|
||||
if (!CharHelper.IsInInclusiveRange(utf32, 1, 1114111) || CharHelper.IsInInclusiveRange(utf32, 55296, 57343))
|
||||
{
|
||||
@@ -99,7 +98,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
#region [ EntityMap ]
|
||||
#region [ EntityMap ]
|
||||
/// <summary>
|
||||
/// Source: http://www.w3.org/html/wg/drafts/html/master/syntax.html#named-character-references
|
||||
/// </summary>
|
||||
|
||||
293
src/Markdig/Helpers/FastStringWriter.cs
Normal file
293
src/Markdig/Helpers/FastStringWriter.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
// 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.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
internal sealed class FastStringWriter : TextWriter
|
||||
{
|
||||
#if NET452
|
||||
private static Task CompletedTask => Task.FromResult(0);
|
||||
#else
|
||||
private static Task CompletedTask => Task.CompletedTask;
|
||||
#endif
|
||||
|
||||
public override Encoding Encoding => Encoding.Unicode;
|
||||
|
||||
private char[] _chars;
|
||||
private int _pos;
|
||||
private string _newLine;
|
||||
|
||||
public FastStringWriter()
|
||||
{
|
||||
_chars = new char[1024];
|
||||
_newLine = "\n";
|
||||
}
|
||||
|
||||
[AllowNull]
|
||||
public override string NewLine
|
||||
{
|
||||
get => _newLine;
|
||||
set => _newLine = value ?? Environment.NewLine;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Write(char value)
|
||||
{
|
||||
char[] chars = _chars;
|
||||
int pos = _pos;
|
||||
if ((uint)pos < (uint)chars.Length)
|
||||
{
|
||||
chars[pos] = value;
|
||||
_pos = pos + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
GrowAndAppend(value);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void WriteLine(char value)
|
||||
{
|
||||
Write(value);
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
public override Task WriteAsync(char value)
|
||||
{
|
||||
Write(value);
|
||||
return CompletedTask;
|
||||
}
|
||||
|
||||
public override Task WriteLineAsync(char value)
|
||||
{
|
||||
WriteLine(value);
|
||||
return CompletedTask;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Write(string? value)
|
||||
{
|
||||
if (value is not null)
|
||||
{
|
||||
if (_pos > _chars.Length - value.Length)
|
||||
{
|
||||
Grow(value.Length);
|
||||
}
|
||||
|
||||
value.AsSpan().CopyTo(_chars.AsSpan(_pos));
|
||||
_pos += value.Length;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void WriteLine(string? value)
|
||||
{
|
||||
Write(value);
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
public override Task WriteAsync(string? value)
|
||||
{
|
||||
Write(value);
|
||||
return CompletedTask;
|
||||
}
|
||||
|
||||
public override Task WriteLineAsync(string? value)
|
||||
{
|
||||
WriteLine(value);
|
||||
return CompletedTask;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Write(char[]? buffer)
|
||||
{
|
||||
if (buffer is not null)
|
||||
{
|
||||
if (_pos > _chars.Length - buffer.Length)
|
||||
{
|
||||
Grow(buffer.Length);
|
||||
}
|
||||
|
||||
buffer.CopyTo(_chars.AsSpan(_pos));
|
||||
_pos += buffer.Length;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void WriteLine(char[]? buffer)
|
||||
{
|
||||
Write(buffer);
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Write(char[] buffer, int index, int count)
|
||||
{
|
||||
if (buffer is not null)
|
||||
{
|
||||
if (_pos > _chars.Length - count)
|
||||
{
|
||||
Grow(buffer.Length);
|
||||
}
|
||||
|
||||
buffer.AsSpan(index, count).CopyTo(_chars.AsSpan(_pos));
|
||||
_pos += count;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void WriteLine(char[] buffer, int index, int count)
|
||||
{
|
||||
Write(buffer, index, count);
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
public override Task WriteAsync(char[] buffer, int index, int count)
|
||||
{
|
||||
Write(buffer, index, count);
|
||||
return CompletedTask;
|
||||
}
|
||||
|
||||
public override Task WriteLineAsync(char[] buffer, int index, int count)
|
||||
{
|
||||
WriteLine(buffer, index, count);
|
||||
return CompletedTask;
|
||||
}
|
||||
|
||||
#if !(NET452 || NETSTANDARD2_0)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Write(ReadOnlySpan<char> value)
|
||||
{
|
||||
if (_pos > _chars.Length - value.Length)
|
||||
{
|
||||
Grow(value.Length);
|
||||
}
|
||||
|
||||
value.CopyTo(_chars.AsSpan(_pos));
|
||||
_pos += value.Length;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void WriteLine(ReadOnlySpan<char> buffer)
|
||||
{
|
||||
Write(buffer);
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
public override Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Write(buffer.Span);
|
||||
return CompletedTask;
|
||||
}
|
||||
|
||||
public override Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default)
|
||||
{
|
||||
WriteLine(buffer.Span);
|
||||
return CompletedTask;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !(NET452 || NETSTANDARD2_0 || NETSTANDARD2_1)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void Write(StringBuilder? value)
|
||||
{
|
||||
if (value is not null)
|
||||
{
|
||||
int length = value.Length;
|
||||
if (_pos > _chars.Length - length)
|
||||
{
|
||||
Grow(length);
|
||||
}
|
||||
|
||||
value.CopyTo(0, _chars.AsSpan(_pos), length);
|
||||
_pos += length;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void WriteLine(StringBuilder? value)
|
||||
{
|
||||
Write(value);
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
public override Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Write(value);
|
||||
return CompletedTask;
|
||||
}
|
||||
|
||||
public override Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default)
|
||||
{
|
||||
WriteLine(value);
|
||||
return CompletedTask;
|
||||
}
|
||||
#endif
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override void WriteLine()
|
||||
{
|
||||
foreach (char c in _newLine)
|
||||
{
|
||||
Write(c);
|
||||
}
|
||||
}
|
||||
|
||||
public override Task WriteLineAsync()
|
||||
{
|
||||
WriteLine();
|
||||
return CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void GrowAndAppend(char value)
|
||||
{
|
||||
Grow(1);
|
||||
Write(value);
|
||||
}
|
||||
|
||||
private void Grow(int additionalCapacityBeyondPos)
|
||||
{
|
||||
Debug.Assert(additionalCapacityBeyondPos > 0);
|
||||
Debug.Assert(_pos > _chars.Length - additionalCapacityBeyondPos, "No resize is needed.");
|
||||
|
||||
char[] newArray = new char[(int)Math.Max((uint)(_pos + additionalCapacityBeyondPos), (uint)_chars.Length * 2)];
|
||||
_chars.AsSpan(0, _pos).CopyTo(newArray);
|
||||
_chars = newArray;
|
||||
}
|
||||
|
||||
|
||||
public override void Flush() { }
|
||||
|
||||
public override void Close() { }
|
||||
|
||||
public override Task FlushAsync() => CompletedTask;
|
||||
|
||||
#if !(NET452 || NETSTANDARD2_0)
|
||||
public override ValueTask DisposeAsync() => default;
|
||||
#endif
|
||||
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_pos = 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _chars.AsSpan(0, _pos).ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
@@ -39,22 +38,22 @@ namespace Markdig.Helpers
|
||||
|
||||
public static bool TryParseHtmlTag(ref StringSlice text, [NotNullWhen(true)] out string? htmlTag)
|
||||
{
|
||||
var builder = StringBuilderCache.Local();
|
||||
if (TryParseHtmlTag(ref text, builder))
|
||||
var builder = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
if (TryParseHtmlTag(ref text, ref builder))
|
||||
{
|
||||
htmlTag = builder.GetStringAndReset();
|
||||
htmlTag = builder.ToString();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Dispose();
|
||||
htmlTag = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryParseHtmlTag(ref StringSlice text, StringBuilder builder)
|
||||
private static bool TryParseHtmlTag(ref StringSlice text, ref ValueStringBuilder builder)
|
||||
{
|
||||
if (builder is null) ThrowHelper.ArgumentNullException(nameof(builder));
|
||||
var c = text.CurrentChar;
|
||||
if (c != '<')
|
||||
{
|
||||
@@ -67,29 +66,29 @@ namespace Markdig.Helpers
|
||||
switch (c)
|
||||
{
|
||||
case '/':
|
||||
return TryParseHtmlCloseTag(ref text, builder);
|
||||
return TryParseHtmlCloseTag(ref text, ref builder);
|
||||
case '?':
|
||||
return TryParseHtmlTagProcessingInstruction(ref text, builder);
|
||||
return TryParseHtmlTagProcessingInstruction(ref text, ref builder);
|
||||
case '!':
|
||||
builder.Append(c);
|
||||
c = text.NextChar();
|
||||
if (c == '-')
|
||||
{
|
||||
return TryParseHtmlTagHtmlComment(ref text, builder);
|
||||
return TryParseHtmlTagHtmlComment(ref text, ref builder);
|
||||
}
|
||||
|
||||
if (c == '[')
|
||||
{
|
||||
return TryParseHtmlTagCData(ref text, builder);
|
||||
return TryParseHtmlTagCData(ref text, ref builder);
|
||||
}
|
||||
|
||||
return TryParseHtmlTagDeclaration(ref text, builder);
|
||||
return TryParseHtmlTagDeclaration(ref text, ref builder);
|
||||
}
|
||||
|
||||
return TryParseHtmlTagOpenTag(ref text, builder);
|
||||
return TryParseHtmlTagOpenTag(ref text, ref builder);
|
||||
}
|
||||
|
||||
internal static bool TryParseHtmlTagOpenTag(ref StringSlice text, StringBuilder builder)
|
||||
internal static bool TryParseHtmlTagOpenTag(ref StringSlice text, ref ValueStringBuilder builder)
|
||||
{
|
||||
var c = text.CurrentChar;
|
||||
|
||||
@@ -244,7 +243,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseHtmlTagDeclaration(ref StringSlice text, StringBuilder builder)
|
||||
private static bool TryParseHtmlTagDeclaration(ref StringSlice text, ref ValueStringBuilder builder)
|
||||
{
|
||||
var c = text.CurrentChar;
|
||||
bool hasAlpha = false;
|
||||
@@ -279,7 +278,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseHtmlTagCData(ref StringSlice text, StringBuilder builder)
|
||||
private static bool TryParseHtmlTagCData(ref StringSlice text, ref ValueStringBuilder builder)
|
||||
{
|
||||
if (text.Match("[CDATA["))
|
||||
{
|
||||
@@ -310,7 +309,7 @@ namespace Markdig.Helpers
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool TryParseHtmlCloseTag(ref StringSlice text, StringBuilder builder)
|
||||
internal static bool TryParseHtmlCloseTag(ref StringSlice text, ref ValueStringBuilder builder)
|
||||
{
|
||||
// </[A-Za-z][A-Za-z0-9]+\s*>
|
||||
builder.Append('/');
|
||||
@@ -355,7 +354,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
|
||||
|
||||
private static bool TryParseHtmlTagHtmlComment(ref StringSlice text, StringBuilder builder)
|
||||
private static bool TryParseHtmlTagHtmlComment(ref StringSlice text, ref ValueStringBuilder builder)
|
||||
{
|
||||
var c = text.NextChar();
|
||||
if (c != '-')
|
||||
@@ -393,7 +392,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseHtmlTagProcessingInstruction(ref StringSlice text, StringBuilder builder)
|
||||
private static bool TryParseHtmlTagProcessingInstruction(ref StringSlice text, ref ValueStringBuilder builder)
|
||||
{
|
||||
builder.Append('?');
|
||||
var prevChar = '\0';
|
||||
@@ -435,13 +434,12 @@ namespace Markdig.Helpers
|
||||
// remove backslashes before punctuation chars:
|
||||
int searchPos = 0;
|
||||
int lastPos = 0;
|
||||
char c;
|
||||
char c = '\0';
|
||||
char[] search = removeBackSlash ? SearchBackAndAmp : SearchAmp;
|
||||
StringBuilder? sb = null;
|
||||
var sb = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
|
||||
while ((searchPos = text!.IndexOfAny(search, searchPos)) != -1)
|
||||
{
|
||||
sb ??= StringBuilderCache.Local();
|
||||
c = text[searchPos];
|
||||
if (removeBackSlash && c == '\\')
|
||||
{
|
||||
@@ -453,7 +451,7 @@ namespace Markdig.Helpers
|
||||
c = text[searchPos];
|
||||
if (c.IsEscapableSymbol())
|
||||
{
|
||||
sb.Append(text, lastPos, searchPos - lastPos - 1);
|
||||
sb.Append(text.AsSpan(lastPos, searchPos - lastPos - 1));
|
||||
lastPos = searchPos;
|
||||
}
|
||||
}
|
||||
@@ -473,26 +471,29 @@ namespace Markdig.Helpers
|
||||
var decoded = EntityHelper.DecodeEntity(text.AsSpan(entityNameStart, entityNameLength));
|
||||
if (decoded != null)
|
||||
{
|
||||
sb.Append(text, lastPos, searchPos - match - lastPos);
|
||||
sb.Append(text.AsSpan(lastPos, searchPos - match - lastPos));
|
||||
sb.Append(decoded);
|
||||
lastPos = searchPos;
|
||||
}
|
||||
}
|
||||
else if (numericEntity >= 0)
|
||||
{
|
||||
sb.Append(text, lastPos, searchPos - match - lastPos);
|
||||
EntityHelper.DecodeEntity(numericEntity, sb);
|
||||
sb.Append(text.AsSpan(lastPos, searchPos - match - lastPos));
|
||||
EntityHelper.DecodeEntity(numericEntity, ref sb);
|
||||
lastPos = searchPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sb is null || lastPos == 0)
|
||||
if (c == 0)
|
||||
{
|
||||
sb.Dispose();
|
||||
return text;
|
||||
}
|
||||
|
||||
sb.Append(text, lastPos, text.Length - lastPos);
|
||||
return sb.GetStringAndReset();
|
||||
sb.Append(text.AsSpan(lastPos, text.Length - lastPos));
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
44
src/Markdig/Helpers/LazySubstring.cs
Normal file
44
src/Markdig/Helpers/LazySubstring.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
internal struct LazySubstring
|
||||
{
|
||||
private string _text;
|
||||
public int Offset;
|
||||
public int Length;
|
||||
|
||||
public LazySubstring(string text)
|
||||
{
|
||||
_text = text;
|
||||
Offset = 0;
|
||||
Length = text.Length;
|
||||
}
|
||||
|
||||
public LazySubstring(string text, int offset, int length)
|
||||
{
|
||||
Debug.Assert((ulong)offset + (ulong)length <= (ulong)text.Length, $"{offset}-{length} in {text}");
|
||||
_text = text;
|
||||
Offset = offset;
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<char> AsSpan() => _text.AsSpan(Offset, Length);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Offset != 0 || Length != _text.Length)
|
||||
{
|
||||
_text = _text.Substring(Offset, Length);
|
||||
Offset = 0;
|
||||
}
|
||||
|
||||
return _text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
@@ -39,41 +41,55 @@ namespace Markdig.Helpers
|
||||
/// <returns>A new line or null if the end of <see cref="TextReader"/> has been reached</returns>
|
||||
public StringSlice ReadLine()
|
||||
{
|
||||
string text = _text;
|
||||
string? text = _text;
|
||||
int end = text.Length;
|
||||
int sourcePosition = SourcePosition;
|
||||
int newSourcePosition = int.MaxValue;
|
||||
NewLine newLine = NewLine.None;
|
||||
|
||||
for (int i = sourcePosition; i < text.Length; i++)
|
||||
if ((uint)sourcePosition >= (uint)end)
|
||||
{
|
||||
char c = text[i];
|
||||
if (c == '\r')
|
||||
text = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
ReadOnlySpan<char> span = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref Unsafe.AsRef(text.GetPinnableReference()), sourcePosition), end - sourcePosition);
|
||||
#else
|
||||
ReadOnlySpan<char> span = text.AsSpan(sourcePosition);
|
||||
#endif
|
||||
|
||||
int crlf = span.IndexOfAny('\r', '\n');
|
||||
if (crlf >= 0)
|
||||
{
|
||||
int length = 1;
|
||||
var newLine = NewLine.CarriageReturn;
|
||||
if (c == '\r' && (uint)(i + 1) < (uint)text.Length && text[i + 1] == '\n')
|
||||
end = sourcePosition + crlf;
|
||||
newSourcePosition = end + 1;
|
||||
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
if (Unsafe.Add(ref Unsafe.AsRef(text.GetPinnableReference()), end) == '\r')
|
||||
#else
|
||||
if ((uint)end < (uint)text.Length && text[end] == '\r')
|
||||
#endif
|
||||
{
|
||||
i++;
|
||||
length = 2;
|
||||
newLine = NewLine.CarriageReturnLineFeed;
|
||||
if ((uint)(newSourcePosition) < (uint)text.Length && text[newSourcePosition] == '\n')
|
||||
{
|
||||
newLine = NewLine.CarriageReturnLineFeed;
|
||||
newSourcePosition++;
|
||||
}
|
||||
else
|
||||
{
|
||||
newLine = NewLine.CarriageReturn;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newLine = NewLine.LineFeed;
|
||||
}
|
||||
|
||||
var slice = new StringSlice(text, sourcePosition, i - length, newLine);
|
||||
SourcePosition = i + 1;
|
||||
return slice;
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
{
|
||||
var slice = new StringSlice(text, sourcePosition, i - 1, NewLine.LineFeed);
|
||||
SourcePosition = i + 1;
|
||||
return slice;
|
||||
}
|
||||
}
|
||||
|
||||
if (sourcePosition >= text.Length)
|
||||
return default;
|
||||
|
||||
SourcePosition = int.MaxValue;
|
||||
return new StringSlice(text, sourcePosition, text.Length - 1);
|
||||
SourcePosition = newSourcePosition;
|
||||
return new StringSlice(text, sourcePosition, end - 1, newLine, dummy: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
@@ -21,7 +20,7 @@ namespace Markdig.Helpers
|
||||
|
||||
public static string Urilize(string headingText, bool allowOnlyAscii, bool keepOpeningDigits = false)
|
||||
{
|
||||
var headingBuffer = StringBuilderCache.Local();
|
||||
var headingBuffer = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
bool hasLetter = keepOpeningDigits && headingText.Length > 0 && char.IsLetterOrDigit(headingText[0]);
|
||||
bool previousIsSpace = false;
|
||||
for (int i = 0; i < headingText.Length; i++)
|
||||
@@ -92,15 +91,13 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
var text = headingBuffer.ToString();
|
||||
headingBuffer.Length = 0;
|
||||
return text;
|
||||
return headingBuffer.ToString();
|
||||
}
|
||||
|
||||
public static string UrilizeAsGfm(string headingText)
|
||||
{
|
||||
// Following https://github.com/jch/html-pipeline/blob/master/lib/html/pipeline/toc_filter.rb
|
||||
var headingBuffer = StringBuilderCache.Local();
|
||||
var headingBuffer = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
for (int i = 0; i < headingText.Length; i++)
|
||||
{
|
||||
var c = headingText[i];
|
||||
@@ -109,7 +106,7 @@ namespace Markdig.Helpers
|
||||
headingBuffer.Append(c == ' ' ? '-' : char.ToLowerInvariant(c));
|
||||
}
|
||||
}
|
||||
return headingBuffer.GetStringAndReset();
|
||||
return headingBuffer.ToString();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -165,7 +162,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
var builder = StringBuilderCache.Local();
|
||||
var builder = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
|
||||
// ****************************
|
||||
// 1. Scan scheme or user email
|
||||
@@ -193,8 +190,7 @@ namespace Markdig.Helpers
|
||||
// a scheme is any sequence of 2–32 characters
|
||||
if (state > 0 && builder.Length >= 32)
|
||||
{
|
||||
builder.Length = 0;
|
||||
return false;
|
||||
goto ReturnFalse;
|
||||
}
|
||||
builder.Append(c);
|
||||
}
|
||||
@@ -202,8 +198,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
if (state < 0 || builder.Length <= 2)
|
||||
{
|
||||
builder.Length = 0;
|
||||
return false;
|
||||
goto ReturnFalse;
|
||||
}
|
||||
state = 1;
|
||||
break;
|
||||
@@ -211,16 +206,14 @@ namespace Markdig.Helpers
|
||||
{
|
||||
if (state > 0)
|
||||
{
|
||||
builder.Length = 0;
|
||||
return false;
|
||||
goto ReturnFalse;
|
||||
}
|
||||
state = -1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Length = 0;
|
||||
return false;
|
||||
goto ReturnFalse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +242,6 @@ namespace Markdig.Helpers
|
||||
|
||||
text.SkipChar();
|
||||
link = builder.ToString();
|
||||
builder.Length = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -297,7 +289,6 @@ namespace Markdig.Helpers
|
||||
{
|
||||
text.SkipChar();
|
||||
link = builder.ToString();
|
||||
builder.Length = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -318,7 +309,8 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
builder.Length = 0;
|
||||
ReturnFalse:
|
||||
builder.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -528,8 +520,7 @@ namespace Markdig.Helpers
|
||||
|
||||
public static bool TryParseTitle<T>(ref T text, out string? title, out char enclosingCharacter) where T : ICharIterator
|
||||
{
|
||||
bool isValid = false;
|
||||
var buffer = StringBuilderCache.Local();
|
||||
var buffer = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
enclosingCharacter = '\0';
|
||||
|
||||
// a sequence of zero or more characters between straight double-quote characters ("), including a " character only if it is backslash-escaped, or
|
||||
@@ -582,8 +573,7 @@ namespace Markdig.Helpers
|
||||
|
||||
// Skip last quote
|
||||
text.SkipChar();
|
||||
isValid = true;
|
||||
break;
|
||||
goto ReturnValid;
|
||||
}
|
||||
|
||||
if (hasEscape && !c.IsAsciiPunctuation())
|
||||
@@ -615,15 +605,18 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
title = isValid ? buffer.ToString() : null;
|
||||
buffer.Length = 0;
|
||||
return isValid;
|
||||
buffer.Dispose();
|
||||
title = null;
|
||||
return false;
|
||||
|
||||
ReturnValid:
|
||||
title = buffer.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryParseTitleTrivia<T>(ref T text, out string? title, out char enclosingCharacter) where T : ICharIterator
|
||||
{
|
||||
bool isValid = false;
|
||||
var buffer = StringBuilderCache.Local();
|
||||
var buffer = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
enclosingCharacter = '\0';
|
||||
|
||||
// a sequence of zero or more characters between straight double-quote characters ("), including a " character only if it is backslash-escaped, or
|
||||
@@ -676,8 +669,7 @@ namespace Markdig.Helpers
|
||||
|
||||
// Skip last quote
|
||||
text.SkipChar();
|
||||
isValid = true;
|
||||
break;
|
||||
goto ReturnValid;
|
||||
}
|
||||
|
||||
if (hasEscape && !c.IsAsciiPunctuation())
|
||||
@@ -709,9 +701,13 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
title = isValid ? buffer.ToString() : null;
|
||||
buffer.Length = 0;
|
||||
return isValid;
|
||||
buffer.Dispose();
|
||||
title = null;
|
||||
return false;
|
||||
|
||||
ReturnValid:
|
||||
title = buffer.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryParseUrl<T>(T text, [NotNullWhen(true)] out string? link) where T : ICharIterator
|
||||
@@ -723,7 +719,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
bool isValid = false;
|
||||
hasPointyBrackets = false;
|
||||
var buffer = StringBuilderCache.Local();
|
||||
var buffer = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
|
||||
var c = text.CurrentChar;
|
||||
|
||||
@@ -854,8 +850,15 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
link = isValid ? buffer.ToString() : null;
|
||||
buffer.Length = 0;
|
||||
if (isValid)
|
||||
{
|
||||
link = buffer.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.Dispose();
|
||||
link = null;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
@@ -863,8 +866,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
bool isValid = false;
|
||||
hasPointyBrackets = false;
|
||||
var buffer = StringBuilderCache.Local();
|
||||
var unescaped = new StringBuilder();
|
||||
var buffer = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
|
||||
var c = text.CurrentChar;
|
||||
|
||||
@@ -892,13 +894,11 @@ namespace Markdig.Helpers
|
||||
if (hasEscape && !c.IsAsciiPunctuation())
|
||||
{
|
||||
buffer.Append('\\');
|
||||
unescaped.Append('\\');
|
||||
}
|
||||
|
||||
if (c == '\\')
|
||||
{
|
||||
hasEscape = true;
|
||||
unescaped.Append('\\');
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -910,7 +910,6 @@ namespace Markdig.Helpers
|
||||
hasEscape = false;
|
||||
|
||||
buffer.Append(c);
|
||||
unescaped.Append(c);
|
||||
|
||||
} while (c != '\0');
|
||||
}
|
||||
@@ -958,7 +957,6 @@ namespace Markdig.Helpers
|
||||
{
|
||||
hasEscape = true;
|
||||
c = text.NextChar();
|
||||
unescaped.Append('\\');
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -989,7 +987,6 @@ namespace Markdig.Helpers
|
||||
}
|
||||
|
||||
buffer.Append(c);
|
||||
unescaped.Append(c);
|
||||
|
||||
c = text.NextChar();
|
||||
}
|
||||
@@ -1000,8 +997,15 @@ namespace Markdig.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
link = isValid ? buffer.ToString() : null;
|
||||
buffer.Length = 0;
|
||||
if (isValid)
|
||||
{
|
||||
link = buffer.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.Dispose();
|
||||
link = null;
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
|
||||
@@ -1357,7 +1361,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var buffer = StringBuilderCache.Local();
|
||||
var buffer = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
|
||||
var startLabel = -1;
|
||||
var endLabel = -1;
|
||||
@@ -1365,7 +1369,6 @@ namespace Markdig.Helpers
|
||||
bool hasEscape = false;
|
||||
bool previousWhitespace = true;
|
||||
bool hasNonWhiteSpace = false;
|
||||
bool isValid = false;
|
||||
while (true)
|
||||
{
|
||||
c = lines.NextChar();
|
||||
@@ -1413,9 +1416,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
labelSpan = SourceSpan.Empty;
|
||||
}
|
||||
|
||||
label = buffer.ToString();
|
||||
isValid = true;
|
||||
goto ReturnValid;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1458,9 +1459,12 @@ namespace Markdig.Helpers
|
||||
previousWhitespace = isWhitespace;
|
||||
}
|
||||
|
||||
buffer.Length = 0;
|
||||
buffer.Dispose();
|
||||
return false;
|
||||
|
||||
return isValid;
|
||||
ReturnValid:
|
||||
label = buffer.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryParseLabelTrivia<T>(ref T lines, bool allowEmpty, out string? label, out SourceSpan labelSpan) where T : ICharIterator
|
||||
@@ -1472,7 +1476,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var buffer = StringBuilderCache.Local();
|
||||
var buffer = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
|
||||
var startLabel = -1;
|
||||
var endLabel = -1;
|
||||
@@ -1480,7 +1484,6 @@ namespace Markdig.Helpers
|
||||
bool hasEscape = false;
|
||||
bool previousWhitespace = true;
|
||||
bool hasNonWhiteSpace = false;
|
||||
bool isValid = false;
|
||||
while (true)
|
||||
{
|
||||
c = lines.NextChar();
|
||||
@@ -1528,9 +1531,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
labelSpan = SourceSpan.Empty;
|
||||
}
|
||||
|
||||
label = buffer.ToString();
|
||||
isValid = true;
|
||||
goto ReturnValid;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1577,10 +1578,12 @@ namespace Markdig.Helpers
|
||||
previousWhitespace = isWhitespace;
|
||||
}
|
||||
|
||||
buffer.Length = 0;
|
||||
buffer.Dispose();
|
||||
return false;
|
||||
|
||||
return isValid;
|
||||
ReturnValid:
|
||||
label = buffer.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
@@ -13,14 +13,14 @@ namespace Markdig.Helpers
|
||||
/// <typeparam name="T">Type of the object to cache</typeparam>
|
||||
public abstract class ObjectCache<T> where T : class
|
||||
{
|
||||
private readonly Stack<T> builders;
|
||||
private readonly ConcurrentQueue<T> _builders;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ObjectCache{T}"/> class.
|
||||
/// </summary>
|
||||
protected ObjectCache()
|
||||
{
|
||||
builders = new Stack<T>(4);
|
||||
_builders = new ConcurrentQueue<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,10 +28,7 @@ namespace Markdig.Helpers
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock (builders)
|
||||
{
|
||||
builders.Clear();
|
||||
}
|
||||
_builders.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -40,12 +37,9 @@ namespace Markdig.Helpers
|
||||
/// <returns></returns>
|
||||
public T Get()
|
||||
{
|
||||
lock (builders)
|
||||
if (_builders.TryDequeue(out T instance))
|
||||
{
|
||||
if (builders.Count > 0)
|
||||
{
|
||||
return builders.Pop();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
return NewInstance();
|
||||
@@ -60,10 +54,7 @@ namespace Markdig.Helpers
|
||||
{
|
||||
if (instance is null) ThrowHelper.ArgumentNullException(nameof(instance));
|
||||
Reset(instance);
|
||||
lock (builders)
|
||||
{
|
||||
builders.Push(instance);
|
||||
}
|
||||
_builders.Enqueue(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -20,12 +20,5 @@ namespace Markdig.Helpers
|
||||
{
|
||||
return builder.Append(slice.Text, slice.Start, slice.Length);
|
||||
}
|
||||
|
||||
internal static string GetStringAndReset(this StringBuilder builder)
|
||||
{
|
||||
string text = builder.ToString();
|
||||
builder.Length = 0;
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,14 @@ namespace Markdig.Helpers
|
||||
Count--;
|
||||
}
|
||||
|
||||
internal void RemoveStartRange(int toRemove)
|
||||
{
|
||||
int remaining = Count - toRemove;
|
||||
Count = remaining;
|
||||
Array.Copy(Lines, toRemove, Lines, 0, remaining);
|
||||
Array.Clear(Lines, remaining, toRemove);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified line to this instance.
|
||||
/// </summary>
|
||||
@@ -139,7 +147,7 @@ namespace Markdig.Helpers
|
||||
}
|
||||
|
||||
// Else use a builder
|
||||
var builder = StringBuilderCache.Local();
|
||||
var builder = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]);
|
||||
int previousStartOfLine = 0;
|
||||
var newLine = NewLine.None;
|
||||
for (int i = 0; i < Count; i++)
|
||||
@@ -152,13 +160,13 @@ namespace Markdig.Helpers
|
||||
ref StringLine line = ref Lines[i];
|
||||
if (!line.Slice.IsEmpty)
|
||||
{
|
||||
builder.Append(line.Slice.Text, line.Slice.Start, line.Slice.Length);
|
||||
builder.Append(line.Slice.AsSpan());
|
||||
}
|
||||
newLine = line.NewLine;
|
||||
|
||||
lineOffsets?.Add(new LineOffset(line.Position, line.Column, line.Slice.Start - line.Position, previousStartOfLine, builder.Length));
|
||||
}
|
||||
return new StringSlice(builder.GetStringAndReset());
|
||||
return new StringSlice(builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -213,21 +221,24 @@ namespace Markdig.Helpers
|
||||
public struct Iterator : ICharIterator
|
||||
{
|
||||
private readonly StringLineGroup _lines;
|
||||
private StringSlice _currentSlice;
|
||||
private int _offset;
|
||||
|
||||
public Iterator(StringLineGroup lines)
|
||||
public Iterator(StringLineGroup stringLineGroup)
|
||||
{
|
||||
this._lines = lines;
|
||||
_lines = stringLineGroup;
|
||||
Start = -1;
|
||||
_offset = -1;
|
||||
SliceIndex = 0;
|
||||
CurrentChar = '\0';
|
||||
End = -1;
|
||||
for (int i = 0; i < lines.Count; i++)
|
||||
StringLine[] lines = stringLineGroup.Lines;
|
||||
for (int i = 0; i < stringLineGroup.Count && i < lines.Length; i++)
|
||||
{
|
||||
ref StringLine line = ref lines.Lines[i];
|
||||
End += line.Slice.Length + line.NewLine.Length(); // Add chars
|
||||
ref StringSlice slice = ref lines[i].Slice;
|
||||
End += slice.Length + slice.NewLine.Length(); // Add chars
|
||||
}
|
||||
_currentSlice = _lines.Lines[0].Slice;
|
||||
SkipChar();
|
||||
}
|
||||
|
||||
@@ -243,17 +254,14 @@ namespace Markdig.Helpers
|
||||
|
||||
public StringLineGroup Remaining()
|
||||
{
|
||||
var lines = _lines;
|
||||
StringLineGroup lines = _lines;
|
||||
if (IsEmpty)
|
||||
{
|
||||
lines.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = SliceIndex - 1; i >= 0; i--)
|
||||
{
|
||||
lines.RemoveAt(i);
|
||||
}
|
||||
lines.RemoveStartRange(SliceIndex);
|
||||
|
||||
if (lines.Count > 0 && _offset > 0)
|
||||
{
|
||||
@@ -266,59 +274,85 @@ namespace Markdig.Helpers
|
||||
return lines;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public char NextChar()
|
||||
{
|
||||
Start++;
|
||||
if (Start <= End)
|
||||
{
|
||||
var slice = _lines.Lines[SliceIndex].Slice;
|
||||
ref StringSlice slice = ref _currentSlice;
|
||||
_offset++;
|
||||
if (_offset < slice.Length)
|
||||
|
||||
int index = slice.Start + _offset;
|
||||
string text = slice.Text;
|
||||
if (index <= slice.End && (uint)index < (uint)text.Length)
|
||||
{
|
||||
CurrentChar = slice[slice.Start + _offset];
|
||||
char c = text[index];
|
||||
CurrentChar = c;
|
||||
return c;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newLine = slice.NewLine;
|
||||
if (_offset == slice.Length)
|
||||
{
|
||||
if (newLine == NewLine.LineFeed)
|
||||
{
|
||||
CurrentChar = '\n';
|
||||
SliceIndex++;
|
||||
_offset = -1;
|
||||
}
|
||||
else if (newLine == NewLine.CarriageReturn)
|
||||
{
|
||||
CurrentChar = '\r';
|
||||
SliceIndex++;
|
||||
_offset = -1;
|
||||
}
|
||||
else if (newLine == NewLine.CarriageReturnLineFeed)
|
||||
{
|
||||
CurrentChar = '\r';
|
||||
}
|
||||
}
|
||||
else if (_offset - 1 == slice.Length)
|
||||
{
|
||||
if (newLine == NewLine.CarriageReturnLineFeed)
|
||||
{
|
||||
CurrentChar = '\n';
|
||||
SliceIndex++;
|
||||
_offset = -1;
|
||||
}
|
||||
}
|
||||
return NextCharNewLine();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentChar = '\0';
|
||||
Start = End + 1;
|
||||
SliceIndex = _lines.Count;
|
||||
return NextCharEndOfEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
private char NextCharNewLine()
|
||||
{
|
||||
int sliceLength = _currentSlice.Length;
|
||||
NewLine newLine = _currentSlice.NewLine;
|
||||
|
||||
if (_offset == sliceLength)
|
||||
{
|
||||
if (newLine == NewLine.LineFeed)
|
||||
{
|
||||
CurrentChar = '\n';
|
||||
goto MoveToNewLine;
|
||||
}
|
||||
else if (newLine == NewLine.CarriageReturn)
|
||||
{
|
||||
CurrentChar = '\r';
|
||||
goto MoveToNewLine;
|
||||
}
|
||||
else if (newLine == NewLine.CarriageReturnLineFeed)
|
||||
{
|
||||
CurrentChar = '\r';
|
||||
}
|
||||
}
|
||||
else if (_offset - 1 == sliceLength)
|
||||
{
|
||||
if (newLine == NewLine.CarriageReturnLineFeed)
|
||||
{
|
||||
CurrentChar = '\n';
|
||||
goto MoveToNewLine;
|
||||
}
|
||||
}
|
||||
|
||||
goto Return;
|
||||
|
||||
MoveToNewLine:
|
||||
SliceIndex++;
|
||||
_offset = -1;
|
||||
_currentSlice = _lines.Lines[SliceIndex];
|
||||
|
||||
Return:
|
||||
return CurrentChar;
|
||||
}
|
||||
|
||||
private char NextCharEndOfEnumerator()
|
||||
{
|
||||
CurrentChar = '\0';
|
||||
Start = End + 1;
|
||||
SliceIndex = _lines.Count;
|
||||
return '\0';
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SkipChar() => NextChar();
|
||||
|
||||
public readonly char PeekChar() => PeekChar(1);
|
||||
@@ -335,16 +369,17 @@ namespace Markdig.Helpers
|
||||
offset += _offset;
|
||||
|
||||
int sliceIndex = SliceIndex;
|
||||
ref StringLine line = ref _lines.Lines[sliceIndex];
|
||||
ref StringSlice slice = ref line.Slice;
|
||||
if (!(line.NewLine == NewLine.CarriageReturnLineFeed && offset == slice.Length + 1))
|
||||
ref StringSlice slice = ref _lines.Lines[sliceIndex].Slice;
|
||||
NewLine newLine = slice.NewLine;
|
||||
|
||||
if (!(newLine == NewLine.CarriageReturnLineFeed && offset == slice.Length + 1))
|
||||
{
|
||||
while (offset > slice.Length)
|
||||
{
|
||||
// We are not peeking at the same line
|
||||
offset -= slice.Length + 1; // + 1 for new line
|
||||
|
||||
Debug.Assert(sliceIndex + 1 < _lines.Lines.Length, "'Start + offset > End' check above should prevent us from indexing out of range");
|
||||
Debug.Assert(sliceIndex + 1 < _lines.Count, "'Start + offset > End' check above should prevent us from indexing out of range");
|
||||
slice = ref _lines.Lines[++sliceIndex].Slice;
|
||||
}
|
||||
}
|
||||
@@ -358,15 +393,15 @@ namespace Markdig.Helpers
|
||||
|
||||
if (offset == slice.Length)
|
||||
{
|
||||
if (line.NewLine == NewLine.LineFeed)
|
||||
if (newLine == NewLine.LineFeed)
|
||||
{
|
||||
return '\n';
|
||||
}
|
||||
if (line.NewLine == NewLine.CarriageReturn)
|
||||
if (newLine == NewLine.CarriageReturn)
|
||||
{
|
||||
return '\r';
|
||||
}
|
||||
if (line.NewLine == NewLine.CarriageReturnLineFeed)
|
||||
if (newLine == NewLine.CarriageReturnLineFeed)
|
||||
{
|
||||
return '\r'; // /r of /r/n (first character)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Markdig.Helpers
|
||||
{
|
||||
@@ -82,6 +83,15 @@ namespace Markdig.Helpers
|
||||
NewLine = newLine;
|
||||
}
|
||||
|
||||
// Internal ctor to skip the null check
|
||||
internal StringSlice(string text, int start, int end, NewLine newLine, bool dummy)
|
||||
{
|
||||
Text = text;
|
||||
Start = start;
|
||||
End = end;
|
||||
NewLine = newLine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The text of this slice.
|
||||
/// </summary>
|
||||
@@ -453,6 +463,25 @@ namespace Markdig.Helpers
|
||||
return text.Substring(start, length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly ReadOnlySpan<char> AsSpan()
|
||||
{
|
||||
string text = Text;
|
||||
int start = Start;
|
||||
int length = End - start + 1;
|
||||
|
||||
if (text is null || (ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)text.Length)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
#if NETCOREAPP3_1_OR_GREATER
|
||||
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref Unsafe.AsRef(text.GetPinnableReference()), start), length);
|
||||
#else
|
||||
return text.AsSpan(start, length);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this slice is empty or made only of whitespaces.
|
||||
/// </summary>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user