Compare commits
229 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e523dfd7f4 | ||
|
|
412500158c | ||
|
|
fc942b4d01 | ||
|
|
d1d64842ca | ||
|
|
930eee383f | ||
|
|
87c45db46b | ||
|
|
751c79fce4 | ||
|
|
4dc8cc3977 | ||
|
|
a1891e2984 | ||
|
|
ff0637993c | ||
|
|
8b64ce456f | ||
|
|
a781ae1e5b | ||
|
|
2e5007241d | ||
|
|
cf6d98b7f8 | ||
|
|
ccb7e8edfa | ||
|
|
db25c1db43 | ||
|
|
5db90ede4b | ||
|
|
fef1ad3563 | ||
|
|
e9302d93bd | ||
|
|
4704c49fbf | ||
|
|
aca70e5c9a | ||
|
|
da5eff075d | ||
|
|
971207e942 | ||
|
|
fb942f9810 | ||
|
|
25a227fffd | ||
|
|
2be8cd4aa7 | ||
|
|
569b80befe | ||
|
|
0b8b14490e | ||
|
|
c59fd5c651 | ||
|
|
4893e2b177 | ||
|
|
b30b219237 | ||
|
|
d6c627aa88 | ||
|
|
a26f4298a4 | ||
|
|
d5b80f6a7b | ||
|
|
98d747c839 | ||
|
|
7dd9321a97 | ||
|
|
c22d681c20 | ||
|
|
ffe7f56bc2 | ||
|
|
16c0b6a3fa | ||
|
|
db867dee48 | ||
|
|
d35b1b82ea | ||
|
|
8b829a2dea | ||
|
|
4f7ef61303 | ||
|
|
831a529338 | ||
|
|
4f5e9cd334 | ||
|
|
cb0b3a6292 | ||
|
|
4390ff289e | ||
|
|
be53e778be | ||
|
|
0eab0a3107 | ||
|
|
8fa47427ac | ||
|
|
bca544575b | ||
|
|
0017dbffc4 | ||
|
|
569efa357a | ||
|
|
7e9df72173 | ||
|
|
b2fcb1cc37 | ||
|
|
8758ba460f | ||
|
|
477a290538 | ||
|
|
0fc112a6b8 | ||
|
|
b7aea44502 | ||
|
|
0545faa9af | ||
|
|
ea67a295b2 | ||
|
|
d53128d56d | ||
|
|
ba192ce065 | ||
|
|
42462d71ac | ||
|
|
6e0363aa91 | ||
|
|
e9be3725d6 | ||
|
|
38f4332de1 | ||
|
|
bcc123d240 | ||
|
|
144e8ad156 | ||
|
|
55a7eae4d4 | ||
|
|
d96862378d | ||
|
|
8acf5a548e | ||
|
|
222a99c7e4 | ||
|
|
ac4a75bdf9 | ||
|
|
fc2e1ab896 | ||
|
|
ef7f20482e | ||
|
|
fc50e2e2e7 | ||
|
|
4d513acf6b | ||
|
|
32b1ee7eaa | ||
|
|
3e2a498ae7 | ||
|
|
e0ac9b7ddf | ||
|
|
bbf10a7ad0 | ||
|
|
62a0992e5e | ||
|
|
b2e49d174e | ||
|
|
5e218721e7 | ||
|
|
c64cd53ac3 | ||
|
|
920ab6cb60 | ||
|
|
3ef1d735d5 | ||
|
|
47c6c49f5c | ||
|
|
a19f78342f | ||
|
|
edb4c6c3cb | ||
|
|
4b6d7c78f5 | ||
|
|
0386cfd617 | ||
|
|
5c52a7249d | ||
|
|
14fb550704 | ||
|
|
741d09a4e8 | ||
|
|
34d9fa2bcc | ||
|
|
8ee50a265d | ||
|
|
eb002db3c7 | ||
|
|
a15203a061 | ||
|
|
b0a2f19ed7 | ||
|
|
a9d78e04a1 | ||
|
|
a8b6e2c2e4 | ||
|
|
665f5f5e51 | ||
|
|
86a72c3582 | ||
|
|
784b999d6c | ||
|
|
be3a893d3d | ||
|
|
2679c84788 | ||
|
|
3d005d6444 | ||
|
|
09593ff3da | ||
|
|
f0269fc61f | ||
|
|
2d97628cfd | ||
|
|
ee97d32ef0 | ||
|
|
9ca5cdcb31 | ||
|
|
5f453fbe92 | ||
|
|
8300a9cca2 | ||
|
|
f7d763230d | ||
|
|
cce1b99edc | ||
|
|
2dca2119fa | ||
|
|
faf0a20816 | ||
|
|
68196c1ce6 | ||
|
|
fdf125d05d | ||
|
|
46133715ab | ||
|
|
ae726b3796 | ||
|
|
4151ac2287 | ||
|
|
305f722899 | ||
|
|
abc8aa25b7 | ||
|
|
e6bb83ecdf | ||
|
|
e6ea8ad274 | ||
|
|
1f3cf5962f | ||
|
|
83b3427805 | ||
|
|
3ff3f1ccb9 | ||
|
|
5d91f0e26f | ||
|
|
9af462e412 | ||
|
|
e1ba52da20 | ||
|
|
70ee97d5c7 | ||
|
|
da916a1700 | ||
|
|
5548246703 | ||
|
|
097fc5c4e0 | ||
|
|
168ad39ff6 | ||
|
|
54e065aabe | ||
|
|
98d58797d9 | ||
|
|
2f0dd2a6a7 | ||
|
|
247209870e | ||
|
|
ef5b958267 | ||
|
|
7d61df2c0c | ||
|
|
51f9da1974 | ||
|
|
9321628b9c | ||
|
|
525e2c7fb8 | ||
|
|
1f9b70636f | ||
|
|
d4f43f826f | ||
|
|
62788b1101 | ||
|
|
c3c4d37b82 | ||
|
|
333bf04274 | ||
|
|
dab4b7176d | ||
|
|
25ec56e1be | ||
|
|
3455fd12da | ||
|
|
ae5d47a0ed | ||
|
|
1ff721d512 | ||
|
|
2c130504e2 | ||
|
|
6405b16692 | ||
|
|
2e1912a23d | ||
|
|
71c680388c | ||
|
|
3f50b3fd0b | ||
|
|
99b250b4c9 | ||
|
|
655bf13df0 | ||
|
|
d2c89a3a06 | ||
|
|
754d11fd44 | ||
|
|
1252e49ba4 | ||
|
|
d18487ae53 | ||
|
|
f401d5082e | ||
|
|
dd6418d108 | ||
|
|
7875e4bce9 | ||
|
|
2e1b1a1fdc | ||
|
|
fa2b157c1a | ||
|
|
e4e6406546 | ||
|
|
1cff10270a | ||
|
|
87023184cb | ||
|
|
aecdf2192e | ||
|
|
0a36382126 | ||
|
|
0057b368ec | ||
|
|
3033284096 | ||
|
|
9b1b791b18 | ||
|
|
2b7d205701 | ||
|
|
89b28659b1 | ||
|
|
446b1bcc0d | ||
|
|
ec7a4a6902 | ||
|
|
07a77142f4 | ||
|
|
3606f234b8 | ||
|
|
616eed62bd | ||
|
|
99f55e9ddc | ||
|
|
f8ab1cccc5 | ||
|
|
f24067cd16 | ||
|
|
9af96ba2b4 | ||
|
|
c99f7dd96a | ||
|
|
bf28cbd33f | ||
|
|
2040e23545 | ||
|
|
2761e36b6b | ||
|
|
891334134c | ||
|
|
0987fab6f2 | ||
|
|
ed5eea5e27 | ||
|
|
f73cbe4e76 | ||
|
|
aefad219cf | ||
|
|
76c3e88c58 | ||
|
|
afe4308e91 | ||
|
|
606556b692 | ||
|
|
253be5c362 | ||
|
|
33037d1034 | ||
|
|
f16ee828db | ||
|
|
a76305f39b | ||
|
|
f52a41e167 | ||
|
|
4d172bf905 | ||
|
|
890b2cda2a | ||
|
|
c818670919 | ||
|
|
a78a0b7016 | ||
|
|
6a0c9aeb47 | ||
|
|
b1cfcf2431 | ||
|
|
b411522a23 | ||
|
|
1d2977d47b | ||
|
|
ee8c87c357 | ||
|
|
25959174d5 | ||
|
|
033ddaf6a8 | ||
|
|
8886b48634 | ||
|
|
64cd8ec262 | ||
|
|
a1e19912a9 | ||
|
|
ca51967fb1 | ||
|
|
bb3a4f372c | ||
|
|
1d6a464c5d | ||
|
|
0fe5c17a93 |
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
* text=auto
|
||||
*.cs text=auto diff=csharp
|
||||
*.sln text=auto eol=crlf
|
||||
12
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [xoofx]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
64
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
name: ci
|
||||
|
||||
env:
|
||||
PROJECT_NAME: Markdig
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'doc/**'
|
||||
- 'img/**'
|
||||
- 'changelog.md'
|
||||
- 'readme.md'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install .NET Core
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '3.1.x'
|
||||
|
||||
- name: Build (Release)
|
||||
run: dotnet build src -c Release
|
||||
|
||||
- name: SpecFileGen
|
||||
run: dotnet src/SpecFileGen/bin/Release/netcoreapp3.1/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 netcoreapp3.1 /p:Include=\"[${{env.PROJECT_NAME}}]*\" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
|
||||
|
||||
- name: Coveralls Upload
|
||||
uses: coverallsapp/github-action@v1.0.1
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path-to-lcov: src/${{env.PROJECT_NAME}}.Tests/coverage.netcoreapp3.1.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"
|
||||
}
|
||||
25
.gitignore
vendored
@@ -138,7 +138,7 @@ publish/
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
@@ -216,3 +216,26 @@ FakesAssemblies/
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Common IntelliJ Platform excludes
|
||||
|
||||
# User specific
|
||||
**/.idea/**/workspace.xml
|
||||
**/.idea/**/tasks.xml
|
||||
**/.idea/shelf/*
|
||||
**/.idea/dictionaries
|
||||
**/.idea/httpRequests/
|
||||
|
||||
# Sensitive or high-churn files
|
||||
**/.idea/**/dataSources/
|
||||
**/.idea/**/dataSources.ids
|
||||
**/.idea/**/dataSources.xml
|
||||
**/.idea/**/dataSources.local.xml
|
||||
**/.idea/**/sqlDataSources.xml
|
||||
**/.idea/**/dynamic.xml
|
||||
|
||||
# Rider
|
||||
# Rider auto-generates .iml files, and contentModel.xml
|
||||
**/.idea/**/*.iml
|
||||
**/.idea/**/contentModel.xml
|
||||
**/.idea/**/modules.xml
|
||||
|
||||
74
appveyor.yml
@@ -1,74 +0,0 @@
|
||||
version: 10.0.{build}
|
||||
image: Visual Studio 2017
|
||||
configuration: Release
|
||||
environment:
|
||||
COVERALLS_REPO_TOKEN:
|
||||
secure: /SEtLgIE6ZrJaWBC1xFZIeESiwfwiXEk9N4pSJ53rFhqBZC2sXJg7ZxZ1DBhnZGu
|
||||
install:
|
||||
- ps: >-
|
||||
cd src
|
||||
|
||||
dotnet tool install -g coveralls.net --version 1.0.0
|
||||
|
||||
nuget restore Markdig.sln
|
||||
|
||||
$env:MARKDIG_BUILD_NUMBER = ([int]$env:APPVEYOR_BUILD_NUMBER).ToString("000")
|
||||
|
||||
$env:MARKDIG_VERSION_SUFFIX = ""
|
||||
|
||||
$env:appveyor_nuget_push = 'false'
|
||||
|
||||
if(-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) {
|
||||
if($env:appveyor_repo_tag -eq 'True') {
|
||||
if($env:appveyor_repo_tag_name -match '^[0-9]') {
|
||||
$env:appveyor_nuget_push = 'true'
|
||||
$env:MARKDIG_VERSION_SUFFIX = ""
|
||||
}
|
||||
if($env:appveyor_repo_tag_name -eq 'latest') {
|
||||
$env:appveyor_nuget_push = 'true'
|
||||
$env:MARKDIG_VERSION_SUFFIX = "pre$env:MARKDIG_BUILD_NUMBER"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
build:
|
||||
project: src/Markdig.sln
|
||||
verbosity: minimal
|
||||
|
||||
after_build: >
|
||||
dotnet SpecFileGen/bin/Release/netcoreapp2.2/SpecFileGen.dll
|
||||
|
||||
test_script:
|
||||
- cmd: >-
|
||||
dotnet test Markdig.Tests -c Release --no-build
|
||||
|
||||
dotnet test Markdig.Tests -c Debug
|
||||
|
||||
dotnet test Markdig.Tests -c Release -f netcoreapp2.1 /p:CollectCoverage=true /p:Include=\"[Markdig]*\" /p:CoverletOutputFormat=opencover /p:CoverletOutput=../../coverage.xml
|
||||
|
||||
after_test:
|
||||
- ps: >-
|
||||
if($env:APPVEYOR_REPO_BRANCH -eq "master") {
|
||||
cd ..
|
||||
if (Test-Path "./coverage.xml") {
|
||||
csmacnz.Coveralls --opencover -i "./coverage.xml" --repoToken $env:COVERALLS_REPO_TOKEN --basePath "$env:APPVEYOR_BUILD_FOLDER" --useRelativePath --commitId $env:APPVEYOR_REPO_COMMIT --commitBranch $env:APPVEYOR_REPO_BRANCH --commitAuthor $env:APPVEYOR_REPO_COMMIT_AUTHOR --commitEmail $env:APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL --commitMessage $env:APPVEYOR_REPO_COMMIT_MESSAGE --jobId $env:APPVEYOR_BUILD_NUMBER --serviceName appveyor
|
||||
}
|
||||
cd src
|
||||
}
|
||||
|
||||
before_package:
|
||||
- cmd: >-
|
||||
msbuild /t:pack /p:VersionSuffix="%MARKDIG_VERSION_SUFFIX%" /p:Configuration=Release Markdig/Markdig.csproj
|
||||
|
||||
msbuild /t:pack /p:VersionSuffix="%MARKDIG_VERSION_SUFFIX%" /p:Configuration=Release Markdig.Signed/Markdig.Signed.csproj
|
||||
|
||||
artifacts:
|
||||
- path: src\**\*.nupkg
|
||||
name: Markdig Nugets
|
||||
|
||||
deploy:
|
||||
- provider: NuGet
|
||||
api_key:
|
||||
secure: 7cthHh+wYWZjhqxaxR6QObRaRnstvFkQOY7MkxIsC5kpQEBlKZXuinf0IybbYxJt
|
||||
on:
|
||||
appveyor_nuget_push: true
|
||||
418
changelog.md
@@ -1,180 +1,238 @@
|
||||
# Changelog
|
||||
|
||||
## 0.17.1 (04 July 2019)
|
||||
- Fix regression when escaping HTML characters ([(PR #340)](https://github.com/lunet-io/markdig/pull/340))
|
||||
- Update Emoji Dictionary ([(PR #346)](https://github.com/lunet-io/markdig/pull/346))
|
||||
|
||||
## 0.17.0 (10 May 2019)
|
||||
- Update to latest CommonMark specs 0.29 ([(PR #327)](https://github.com/lunet-io/markdig/pull/327))
|
||||
- Add `AutoLinkOptions` with `OpenInNewWindow`, `UseHttpsForWWWLinks` ([(PR #327)](https://github.com/lunet-io/markdig/pull/327))
|
||||
- Add `DisableHeadings` extension method to `MarkdownPipelineBuilder` ([(PR #327)](https://github.com/lunet-io/markdig/pull/327))
|
||||
- Drop support for netstandard1.1 and Portable Class Libraries ([(PR #319)](https://github.com/lunet-io/markdig/pull/319))
|
||||
- Allow non-ASCII characters in url domain names ([(PR #319)](https://github.com/lunet-io/markdig/pull/319))
|
||||
- Add better support for youtu.be link ([(PR #336)](https://github.com/lunet-io/markdig/pull/336))
|
||||
- Fix backsticks in Markdown.Normalize ([(PR #334)](https://github.com/lunet-io/markdig/pull/334))
|
||||
|
||||
## 0.16.0 (25 Feb 2019)
|
||||
- Improve performance of emoji-abbreviation parser ([(PR #305)](https://github.com/lunet-io/markdig/pull/305))
|
||||
- Change output for math extension to use a rendering more compatible with existing Math JS libraries ([(PR #311)](https://github.com/lunet-io/markdig/pull/311))
|
||||
- Improve emphasis parser to allow to match more than 2 characters ([(PR #301)](https://github.com/lunet-io/markdig/pull/301))
|
||||
- Output attached attributes to a `<tr>` from a table row ([(PR #300)](https://github.com/lunet-io/markdig/pull/300))
|
||||
- Improve MarkdownObject.Descendants() search ([(PR #288)](https://github.com/lunet-io/markdig/pull/288))
|
||||
- Allow to pass a `MarkdownParserContext` ([(PR #285)](https://github.com/lunet-io/markdig/pull/285))
|
||||
|
||||
## 0.15.7 (11 Jan 2019)
|
||||
- Add configurable leading count for ATX headers ([(PR #282)](https://github.com/lunet-io/markdig/pull/282))
|
||||
- Render XML well-formed boolean attribute ([(PR #281)](https://github.com/lunet-io/markdig/pull/281))
|
||||
|
||||
## 0.15.6 (28 Dec 2018)
|
||||
- Fix potential hang when parsing LinkReferenceDefinition #278
|
||||
- Fix parsing of an invalid html entity (#277)
|
||||
- Fix IndexOutOfRangeException while parsing fenced code block with a single trailing space (#276)
|
||||
- Add tests for checking that ArgumentOutOfRangeException doesn't occur on invalid input md string (#275)
|
||||
|
||||
## 0.15.5 (11 Dec 2018)
|
||||
- Empty image alt text for link reference definitions ([(PR #254)](https://github.com/lunet-io/markdig/pull/254))
|
||||
- Fix AutoLink Match links without slash after domain ([(PR #260)](https://github.com/lunet-io/markdig/pull/260))
|
||||
- Make AutoLink ValidPreviousCharacters configurable ([(PR #264)](https://github.com/lunet-io/markdig/pull/264))
|
||||
- Ensuring line breaks when renderer does not have html enabled ([(PR #270)](https://github.com/lunet-io/markdig/pull/270))
|
||||
|
||||
## 0.15.4 (07 Oct 2018)
|
||||
- Add autolink domain GFM validation ([(PR #253)](https://github.com/lunet-io/markdig/pull/253))
|
||||
|
||||
## 0.15.3 (15 Sep 2018)
|
||||
- Add support for RTL ([(PR #239)](https://github.com/lunet-io/markdig/pull/239))
|
||||
- Add MarkdownDocument.LineCount ([(PR #241)](https://github.com/lunet-io/markdig/pull/241))
|
||||
- Fix source positions for link definitions ([(PR #243)](https://github.com/lunet-io/markdig/pull/243))
|
||||
- Add ListItemBlock.Order ([(PR #244)](https://github.com/lunet-io/markdig/pull/244))
|
||||
- Add MarkdownDocument.LineStartIndexes ([(PR #247)](https://github.com/lunet-io/markdig/pull/247))
|
||||
|
||||
## 0.15.2 (21 Aug 2018)
|
||||
- Fix footnotes parsing when they are defined after a container that has been closed in the meantime (#223)
|
||||
|
||||
## 0.15.1 (10 July 2018)
|
||||
- Add support for `netstandard2.0`
|
||||
- Make AutoIdentifierExtension thread safe
|
||||
|
||||
## 0.15.0 (4 Apr 2018)
|
||||
- Add `ConfigureNewLine` extension method to `MarkdownPipelineBuilder` ([(PR #214)](https://github.com/lunet-io/markdig/pull/214))
|
||||
- Add alternative `Use` extension method to `MarkdownPipelineBuilder` that receives an object instance ([(PR #213)](https://github.com/lunet-io/markdig/pull/213))
|
||||
- Added class attribute to media link extension ([(PR #203)](https://github.com/lunet-io/markdig/pull/203))
|
||||
- Optional link rewriter func for HtmlRenderer #143 ([(PR #201)](https://github.com/lunet-io/markdig/pull/201))
|
||||
- Upgrade NUnit3TestAdapter from 3.2 to 3.9 to address Resharper test runner problems ([(PR #199)](https://github.com/lunet-io/markdig/pull/199))
|
||||
- HTML renderer supports converting relative URLs on links and images to absolute #143 ([(PR #197)](https://github.com/lunet-io/markdig/pull/197))
|
||||
|
||||
## 0.14.9 (15 Jan 2018)
|
||||
- AutoLinkParser should to remove mailto: in outputted text ([(PR #195)](https://github.com/lunet-io/markdig/pull/195))
|
||||
- Add support for `music.yandex.ru` and `ok.ru` for MediaLinks extension ([(PR #193)](https://github.com/lunet-io/markdig/pull/193))
|
||||
## 0.14.8 (05 Dec 2017)
|
||||
- Fix potential StackOverflow exception when processing deep nested `|` delimiters (#179)
|
||||
## 0.14.7 (25 Nov 2017)
|
||||
- Fix autolink attached attributes not being displayed properly (#175)
|
||||
## 0.14.6 (21 Nov 2017)
|
||||
- Fix yaml frontmatter issue when ending with a empty line (#170)
|
||||
## 0.14.5 (18 Nov 2017)
|
||||
- Fix changelog link from nuget package
|
||||
## 0.14.4 (18 Nov 2017)
|
||||
- Add changelog.md
|
||||
- Fix bug when a thematic break is inside a fenced code block inside a pending list (#164)
|
||||
- Add support for GFM autolinks (#165, #169)
|
||||
- Better handle YAML frontmatter in case the opening `---` is never actually closed (#160)
|
||||
- Fix link conflict between a link to an image definition and heading auto-identifiers (#159)
|
||||
## 0.14.3
|
||||
- Make EmojiExtension.EnableSmiley public
|
||||
## 0.14.2
|
||||
- Fix issue with emphasis preceded/followed by an HTML entity (#157)
|
||||
- Add support for link reference definitions for Normalize renderer (#155)
|
||||
- Add option to disable smiley parsing in EmojiAndSmiley extension
|
||||
## 0.14.1
|
||||
- Fix crash in Markdown.Normalize to handle HtmlBlock correctly
|
||||
- Add better handling of bullet character for lists in Markdown.Normalize
|
||||
## 0.14.0
|
||||
- Add Markdown.ToPlainText, Add option HtmlRenderer.EnableHtmlForBlock.
|
||||
- Add Markdown.Normalize, to allow to normalize a markdown document. Add NormalizeRenderer, to render a MarkdownDocument back to markdown.
|
||||
## 0.13.4
|
||||
- Add support for single table header row without a table body rows (#141)
|
||||
- ADd support for `nomnoml` diagrams
|
||||
## 0.13.3
|
||||
- Add support for Pandoc YAML frontmatter (#138)
|
||||
## 0.13.2
|
||||
- Add support for UAP10.0 (#137)
|
||||
## 0.13.1
|
||||
- Fix indenting issue after a double digit list block using a tab (#134)
|
||||
## 0.13.0
|
||||
- Update to latest CommonMark specs 0.28
|
||||
## 0.12.3
|
||||
- Fix issue with HTML blocks for heading h2,h3,h4,h5,h6 that were not correctly identified as HTML blocks as per CommonMark spec
|
||||
## 0.12.2
|
||||
- Fix issue with generic attributes used just before a pipe table (issue #121)
|
||||
## 0.12.1
|
||||
- Fix issue with media links extension when a URL to video is used, an unexpected closing `</iframe>` was inserted (issue #119)
|
||||
## 0.12.0
|
||||
- Add new extension JiraLink support (thanks to @clarkd)
|
||||
- Fix issue in html attributes not parsing correctly properties (thanks to @meziantou)
|
||||
- Fix issues detected by an automatic static code analysis tool
|
||||
## 0.11.0
|
||||
- Fix issue with math extension and $$ block parsing not handling correctly beginning of a $$ as a inline math instead (issue #107)
|
||||
- Fix issue with custom attributes for emphasis
|
||||
- Add support for new special custom arrows emoji (`->` `<-` `<->` `<=` `=>` `<==>`)
|
||||
## 0.10.7
|
||||
- Fix issue when an url ends by a dot `.`
|
||||
## 0.10.6
|
||||
- Fix emphasis with HTML entities
|
||||
## 0.10.5
|
||||
- Several minor fixes
|
||||
## 0.10.4
|
||||
- Fix issue with autolinks
|
||||
- Normalize number of columns for tables
|
||||
## 0.10.3
|
||||
- Fix issue with pipetables shifting a cell to a new column (issue #73)
|
||||
## 0.10.2
|
||||
- Fix exception when trying to urlize an url with an unicode character outside the supported range by NormD (issue #75)
|
||||
## 0.10.1
|
||||
- Update to latest CommonMark specs
|
||||
- Fix source span for LinkReferenceDefinition
|
||||
## 0.10.0
|
||||
- Breaking change of the IMarkdownExtension to allow to receive the MarkdownPipeline for the renderers setup
|
||||
## 0.9.1
|
||||
- Fix regression bug with conflicts between autolink extension and html inline/regular links
|
||||
## 0.9.0
|
||||
- Add new Autolink extension
|
||||
## 0.8.5
|
||||
- Allow to force table column alignment to left
|
||||
## 0.8.4
|
||||
- Fix issue when calculating the span of an indented code block within a list. Make sure to include first whitespace on the line
|
||||
## 0.8.3
|
||||
- fix NullReferenceException with Gridtables extension when a single `+` is entered on a line
|
||||
## 0.8.2
|
||||
- fix potential cast exception with Abbreviation extension and empty literals
|
||||
## 0.8.1
|
||||
- new extension to disable URI escaping for non-US-ASCII characters to workaround a bug in Edge/IE
|
||||
- Fix an issue with abbreviations with left/right multiple non-punctuation/space characters
|
||||
## 0.8.0
|
||||
- Update to latest CommonMark specs
|
||||
- Fix empty literal
|
||||
- Add YAML frontmatter extension
|
||||
## 0.7.5
|
||||
- several bug fixes (pipe tables, disable HTML, special attributes, inline maths, abbreviations...)
|
||||
- add support for rowspan in grid tables
|
||||
## 0.7.4
|
||||
- Fix bug with strong emphasis starting at the beginning of a line
|
||||
## 0.7.3
|
||||
- Fix threading issue with pipeline
|
||||
## 0.7.2
|
||||
- Fix rendering of table colspan with non english locale
|
||||
- Fix grid table colspan parsing
|
||||
- Add nofollow extension for links
|
||||
## 0.7.1
|
||||
- Fix issue in smarty pants which could lead to an InvalidCastException
|
||||
- Update parsers to latest CommonMark specs
|
||||
## 0.7.0
|
||||
- Update to latest NETStandard.Library 1.6.0
|
||||
- Fix issue with digits in auto-identifier extension
|
||||
- Fix incorrect start of span calculated for code indented blocks
|
||||
## 0.6.2
|
||||
- Handle latest CommonMark specs for corner cases for emphasis (See https://talk.commonmark.org/t/emphasis-strong-emphasis-corner-cases/2123/1 )
|
||||
## 0.6.1:
|
||||
- Fix issue with autoidentifier extension overriding manual HTML attributes id on headings
|
||||
## 0.6.0
|
||||
- Fix conflicts between PipeTables and SmartyPants extensions
|
||||
- Add SelfPipeline extension
|
||||
# Changelog
|
||||
|
||||
## 0.22.1 (2 Dec 2020)
|
||||
- Update logo for NuGet package
|
||||
|
||||
## 0.22.0 (05 Oct 2020)
|
||||
- Fix Setext headings in block quotes.
|
||||
- Fix tel: treated as autolink ([PR #478](https://github.com/lunet-io/markdig/pull/478)
|
||||
- Make Inline.FirstParentOfType public ([PR #474](https://github.com/lunet-io/markdig/pull/474)
|
||||
- Fix `&` to be parsed as a punctuation while it was detected as a html entity in certain cases ([PR #471](https://github.com/lunet-io/markdig/pull/471)
|
||||
- Add ParentBlock property to ContainerInline ([PR #468](https://github.com/lunet-io/markdig/pull/468)
|
||||
|
||||
## 0.21.1 (17 Aug 2020)
|
||||
- Fix Markdig.Signed on GitHub Actions
|
||||
|
||||
## 0.21.0 (17 Aug 2020)
|
||||
- Restore support for .NET 4.5 (#)
|
||||
- Add IReadonlyList interface to ContainerBlock to unify and simplify enumeration (#425)
|
||||
- Fix relative uri detection to be cross-platform compatible (#430)
|
||||
- Escape URLs scheme (#431)
|
||||
- Fix media links (#435)
|
||||
- Fix parsing math blocks with no leading or trailing whitespace (#452)
|
||||
- Add support for autolink `tel:` uri (#453)
|
||||
- Fallback to non-punycode encoding for invalid IDN urls (#449)
|
||||
- Pipe Tables: Normalize using header column count (#455)
|
||||
- Expose IndentCount of FencedCodeBlock (#464)
|
||||
|
||||
## 0.20.0 (18 Apr 2020)
|
||||
- Markdig is now compatible only with `NETStandard 2.0`, `NETStandard 2.1`, `NETCoreApp 2.1` and `NETCoreApp 3.1`.
|
||||
- Many performance improvements from [PR #416](https://github.com/lunet-io/markdig/pull/416)
|
||||
[PR #417](https://github.com/lunet-io/markdig/pull/417)
|
||||
[PR #418](https://github.com/lunet-io/markdig/pull/418)
|
||||
[PR #421](https://github.com/lunet-io/markdig/pull/421)
|
||||
[PR #422](https://github.com/lunet-io/markdig/pull/422)
|
||||
[PR #410](https://github.com/lunet-io/markdig/pull/410)
|
||||
|
||||
## 0.18.3 (8 Mar 2020)
|
||||
- Publish NuGet Symbol packages
|
||||
|
||||
## 0.18.2 (8 Mar 2020)
|
||||
- Optimize LineReader.ReadLine in [PR #393](https://github.com/lunet-io/markdig/pull/393)
|
||||
- Use HashSet<T> instead of Dictionary<TKey, TValue> in CharacterMap<T> in [PR #394](https://github.com/lunet-io/markdig/pull/394)
|
||||
- Use BitVector128 in CharacterMap<T> in [PR #396](https://github.com/lunet-io/markdig/pull/396)
|
||||
- Optimizations in StringLineGroup in [PR #399](https://github.com/lunet-io/markdig/pull/399)
|
||||
- Fixed a bug in HeadingRenderer in [PR #402](https://github.com/lunet-io/markdig/pull/402)
|
||||
- Fixes issue #303 in [PR #404](https://github.com/lunet-io/markdig/pull/404)
|
||||
- Make output of HtmlTableRenderer XML wellformed in [PR #406](https://github.com/lunet-io/markdig/pull/406)
|
||||
|
||||
## 0.18.1 (21 Jan 2020)
|
||||
- Re-allow emojis and smileys customization, that was broken in [PR #308](https://github.com/lunet-io/markdig/pull/308) ([PR #386](https://github.com/lunet-io/markdig/pull/386))
|
||||
- Add `IHostProvider` for medialink customization (#337), support protocol-less url (#135) ([(PR #341)](https://github.com/lunet-io/markdig/pull/341))
|
||||
- Add missing Descendants<T> overload ([(PR #387)](https://github.com/lunet-io/markdig/pull/387))
|
||||
|
||||
## 0.18.0 (24 Oct 2019)
|
||||
- Ignore backslashes in GFM AutoLinks ([(PR #357)](https://github.com/lunet-io/markdig/pull/357))
|
||||
- Fix SmartyPants quote matching ([(PR #360)](https://github.com/lunet-io/markdig/pull/360))
|
||||
- Fix generic attributes with values of length 1 ([(PR #361)](https://github.com/lunet-io/markdig/pull/361))
|
||||
- Fix link text balanced bracket matching ([(PR #375)](https://github.com/lunet-io/markdig/pull/375))
|
||||
- Improve overall performance and substantially reduce allocations ([(PR #377)](https://github.com/lunet-io/markdig/pull/377))
|
||||
|
||||
## 0.17.1 (04 July 2019)
|
||||
- Fix regression when escaping HTML characters ([(PR #340)](https://github.com/lunet-io/markdig/pull/340))
|
||||
- Update Emoji Dictionary ([(PR #346)](https://github.com/lunet-io/markdig/pull/346))
|
||||
|
||||
## 0.17.0 (10 May 2019)
|
||||
- Update to latest CommonMark specs 0.29 ([(PR #327)](https://github.com/lunet-io/markdig/pull/327))
|
||||
- Add `AutoLinkOptions` with `OpenInNewWindow`, `UseHttpsForWWWLinks` ([(PR #327)](https://github.com/lunet-io/markdig/pull/327))
|
||||
- Add `DisableHeadings` extension method to `MarkdownPipelineBuilder` ([(PR #327)](https://github.com/lunet-io/markdig/pull/327))
|
||||
- Drop support for netstandard1.1 and Portable Class Libraries ([(PR #319)](https://github.com/lunet-io/markdig/pull/319))
|
||||
- Allow non-ASCII characters in url domain names ([(PR #319)](https://github.com/lunet-io/markdig/pull/319))
|
||||
- Add better support for youtu.be link ([(PR #336)](https://github.com/lunet-io/markdig/pull/336))
|
||||
- Fix backsticks in Markdown.Normalize ([(PR #334)](https://github.com/lunet-io/markdig/pull/334))
|
||||
|
||||
## 0.16.0 (25 Feb 2019)
|
||||
- Improve performance of emoji-abbreviation parser ([(PR #305)](https://github.com/lunet-io/markdig/pull/305))
|
||||
- Change output for math extension to use a rendering more compatible with existing Math JS libraries ([(PR #311)](https://github.com/lunet-io/markdig/pull/311))
|
||||
- Improve emphasis parser to allow to match more than 2 characters ([(PR #301)](https://github.com/lunet-io/markdig/pull/301))
|
||||
- Output attached attributes to a `<tr>` from a table row ([(PR #300)](https://github.com/lunet-io/markdig/pull/300))
|
||||
- Improve MarkdownObject.Descendants() search ([(PR #288)](https://github.com/lunet-io/markdig/pull/288))
|
||||
- Allow to pass a `MarkdownParserContext` ([(PR #285)](https://github.com/lunet-io/markdig/pull/285))
|
||||
|
||||
## 0.15.7 (11 Jan 2019)
|
||||
- Add configurable leading count for ATX headers ([(PR #282)](https://github.com/lunet-io/markdig/pull/282))
|
||||
- Render XML well-formed boolean attribute ([(PR #281)](https://github.com/lunet-io/markdig/pull/281))
|
||||
|
||||
## 0.15.6 (28 Dec 2018)
|
||||
- Fix potential hang when parsing LinkReferenceDefinition #278
|
||||
- Fix parsing of an invalid html entity (#277)
|
||||
- Fix IndexOutOfRangeException while parsing fenced code block with a single trailing space (#276)
|
||||
- Add tests for checking that ArgumentOutOfRangeException doesn't occur on invalid input md string (#275)
|
||||
|
||||
## 0.15.5 (11 Dec 2018)
|
||||
- Empty image alt text for link reference definitions ([(PR #254)](https://github.com/lunet-io/markdig/pull/254))
|
||||
- Fix AutoLink Match links without slash after domain ([(PR #260)](https://github.com/lunet-io/markdig/pull/260))
|
||||
- Make AutoLink ValidPreviousCharacters configurable ([(PR #264)](https://github.com/lunet-io/markdig/pull/264))
|
||||
- Ensuring line breaks when renderer does not have html enabled ([(PR #270)](https://github.com/lunet-io/markdig/pull/270))
|
||||
|
||||
## 0.15.4 (07 Oct 2018)
|
||||
- Add autolink domain GFM validation ([(PR #253)](https://github.com/lunet-io/markdig/pull/253))
|
||||
|
||||
## 0.15.3 (15 Sep 2018)
|
||||
- Add support for RTL ([(PR #239)](https://github.com/lunet-io/markdig/pull/239))
|
||||
- Add MarkdownDocument.LineCount ([(PR #241)](https://github.com/lunet-io/markdig/pull/241))
|
||||
- Fix source positions for link definitions ([(PR #243)](https://github.com/lunet-io/markdig/pull/243))
|
||||
- Add ListItemBlock.Order ([(PR #244)](https://github.com/lunet-io/markdig/pull/244))
|
||||
- Add MarkdownDocument.LineStartIndexes ([(PR #247)](https://github.com/lunet-io/markdig/pull/247))
|
||||
|
||||
## 0.15.2 (21 Aug 2018)
|
||||
- Fix footnotes parsing when they are defined after a container that has been closed in the meantime (#223)
|
||||
|
||||
## 0.15.1 (10 July 2018)
|
||||
- Add support for `netstandard2.0`
|
||||
- Make AutoIdentifierExtension thread safe
|
||||
|
||||
## 0.15.0 (4 Apr 2018)
|
||||
- Add `ConfigureNewLine` extension method to `MarkdownPipelineBuilder` ([(PR #214)](https://github.com/lunet-io/markdig/pull/214))
|
||||
- Add alternative `Use` extension method to `MarkdownPipelineBuilder` that receives an object instance ([(PR #213)](https://github.com/lunet-io/markdig/pull/213))
|
||||
- Added class attribute to media link extension ([(PR #203)](https://github.com/lunet-io/markdig/pull/203))
|
||||
- Optional link rewriter func for HtmlRenderer #143 ([(PR #201)](https://github.com/lunet-io/markdig/pull/201))
|
||||
- Upgrade NUnit3TestAdapter from 3.2 to 3.9 to address Resharper test runner problems ([(PR #199)](https://github.com/lunet-io/markdig/pull/199))
|
||||
- HTML renderer supports converting relative URLs on links and images to absolute #143 ([(PR #197)](https://github.com/lunet-io/markdig/pull/197))
|
||||
|
||||
## 0.14.9 (15 Jan 2018)
|
||||
- AutoLinkParser should to remove mailto: in outputted text ([(PR #195)](https://github.com/lunet-io/markdig/pull/195))
|
||||
- Add support for `music.yandex.ru` and `ok.ru` for MediaLinks extension ([(PR #193)](https://github.com/lunet-io/markdig/pull/193))
|
||||
## 0.14.8 (05 Dec 2017)
|
||||
- Fix potential StackOverflow exception when processing deep nested `|` delimiters (#179)
|
||||
## 0.14.7 (25 Nov 2017)
|
||||
- Fix autolink attached attributes not being displayed properly (#175)
|
||||
## 0.14.6 (21 Nov 2017)
|
||||
- Fix yaml frontmatter issue when ending with a empty line (#170)
|
||||
## 0.14.5 (18 Nov 2017)
|
||||
- Fix changelog link from nuget package
|
||||
## 0.14.4 (18 Nov 2017)
|
||||
- Add changelog.md
|
||||
- Fix bug when a thematic break is inside a fenced code block inside a pending list (#164)
|
||||
- Add support for GFM autolinks (#165, #169)
|
||||
- Better handle YAML frontmatter in case the opening `---` is never actually closed (#160)
|
||||
- Fix link conflict between a link to an image definition and heading auto-identifiers (#159)
|
||||
## 0.14.3
|
||||
- Make EmojiExtension.EnableSmiley public
|
||||
## 0.14.2
|
||||
- Fix issue with emphasis preceded/followed by an HTML entity (#157)
|
||||
- Add support for link reference definitions for Normalize renderer (#155)
|
||||
- Add option to disable smiley parsing in EmojiAndSmiley extension
|
||||
## 0.14.1
|
||||
- Fix crash in Markdown.Normalize to handle HtmlBlock correctly
|
||||
- Add better handling of bullet character for lists in Markdown.Normalize
|
||||
## 0.14.0
|
||||
- Add Markdown.ToPlainText, Add option HtmlRenderer.EnableHtmlForBlock.
|
||||
- Add Markdown.Normalize, to allow to normalize a markdown document. Add NormalizeRenderer, to render a MarkdownDocument back to markdown.
|
||||
## 0.13.4
|
||||
- Add support for single table header row without a table body rows (#141)
|
||||
- ADd support for `nomnoml` diagrams
|
||||
## 0.13.3
|
||||
- Add support for Pandoc YAML frontmatter (#138)
|
||||
## 0.13.2
|
||||
- Add support for UAP10.0 (#137)
|
||||
## 0.13.1
|
||||
- Fix indenting issue after a double digit list block using a tab (#134)
|
||||
## 0.13.0
|
||||
- Update to latest CommonMark specs 0.28
|
||||
## 0.12.3
|
||||
- Fix issue with HTML blocks for heading h2,h3,h4,h5,h6 that were not correctly identified as HTML blocks as per CommonMark spec
|
||||
## 0.12.2
|
||||
- Fix issue with generic attributes used just before a pipe table (issue #121)
|
||||
## 0.12.1
|
||||
- Fix issue with media links extension when a URL to video is used, an unexpected closing `</iframe>` was inserted (issue #119)
|
||||
## 0.12.0
|
||||
- Add new extension JiraLink support (thanks to @clarkd)
|
||||
- Fix issue in html attributes not parsing correctly properties (thanks to @meziantou)
|
||||
- Fix issues detected by an automatic static code analysis tool
|
||||
## 0.11.0
|
||||
- Fix issue with math extension and $$ block parsing not handling correctly beginning of a $$ as a inline math instead (issue #107)
|
||||
- Fix issue with custom attributes for emphasis
|
||||
- Add support for new special custom arrows emoji (`->` `<-` `<->` `<=` `=>` `<==>`)
|
||||
## 0.10.7
|
||||
- Fix issue when an url ends by a dot `.`
|
||||
## 0.10.6
|
||||
- Fix emphasis with HTML entities
|
||||
## 0.10.5
|
||||
- Several minor fixes
|
||||
## 0.10.4
|
||||
- Fix issue with autolinks
|
||||
- Normalize number of columns for tables
|
||||
## 0.10.3
|
||||
- Fix issue with pipetables shifting a cell to a new column (issue #73)
|
||||
## 0.10.2
|
||||
- Fix exception when trying to urlize an url with an unicode character outside the supported range by NormD (issue #75)
|
||||
## 0.10.1
|
||||
- Update to latest CommonMark specs
|
||||
- Fix source span for LinkReferenceDefinition
|
||||
## 0.10.0
|
||||
- Breaking change of the IMarkdownExtension to allow to receive the MarkdownPipeline for the renderers setup
|
||||
## 0.9.1
|
||||
- Fix regression bug with conflicts between autolink extension and html inline/regular links
|
||||
## 0.9.0
|
||||
- Add new Autolink extension
|
||||
## 0.8.5
|
||||
- Allow to force table column alignment to left
|
||||
## 0.8.4
|
||||
- Fix issue when calculating the span of an indented code block within a list. Make sure to include first whitespace on the line
|
||||
## 0.8.3
|
||||
- fix NullReferenceException with Gridtables extension when a single `+` is entered on a line
|
||||
## 0.8.2
|
||||
- fix potential cast exception with Abbreviation extension and empty literals
|
||||
## 0.8.1
|
||||
- new extension to disable URI escaping for non-US-ASCII characters to workaround a bug in Edge/IE
|
||||
- Fix an issue with abbreviations with left/right multiple non-punctuation/space characters
|
||||
## 0.8.0
|
||||
- Update to latest CommonMark specs
|
||||
- Fix empty literal
|
||||
- Add YAML frontmatter extension
|
||||
## 0.7.5
|
||||
- several bug fixes (pipe tables, disable HTML, special attributes, inline maths, abbreviations...)
|
||||
- add support for rowspan in grid tables
|
||||
## 0.7.4
|
||||
- Fix bug with strong emphasis starting at the beginning of a line
|
||||
## 0.7.3
|
||||
- Fix threading issue with pipeline
|
||||
## 0.7.2
|
||||
- Fix rendering of table colspan with non english locale
|
||||
- Fix grid table colspan parsing
|
||||
- Add nofollow extension for links
|
||||
## 0.7.1
|
||||
- Fix issue in smarty pants which could lead to an InvalidCastException
|
||||
- Update parsers to latest CommonMark specs
|
||||
## 0.7.0
|
||||
- Update to latest NETStandard.Library 1.6.0
|
||||
- Fix issue with digits in auto-identifier extension
|
||||
- Fix incorrect start of span calculated for code indented blocks
|
||||
## 0.6.2
|
||||
- Handle latest CommonMark specs for corner cases for emphasis (See https://talk.commonmark.org/t/emphasis-strong-emphasis-corner-cases/2123/1 )
|
||||
## 0.6.1:
|
||||
- Fix issue with autoidentifier extension overriding manual HTML attributes id on headings
|
||||
## 0.6.0
|
||||
- Fix conflicts between PipeTables and SmartyPants extensions
|
||||
- Add SelfPipeline extension
|
||||
|
||||
BIN
img/markdig.png
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 16 KiB |
@@ -14,11 +14,11 @@
|
||||
viewBox="0 0 192 192"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="markdig.svg"
|
||||
inkscape:export-filename="C:\Code\lunet-io\markdig\markdig.png"
|
||||
inkscape:export-xdpi="93.400002"
|
||||
inkscape:export-ydpi="93.400002">
|
||||
inkscape:export-filename="C:\code\lunet\markdig\img\markdig.png"
|
||||
inkscape:export-xdpi="256"
|
||||
inkscape:export-ydpi="256">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
@@ -28,16 +28,16 @@
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4.4374889"
|
||||
inkscape:cx="174.14769"
|
||||
inkscape:cy="93.189838"
|
||||
inkscape:zoom="6.275557"
|
||||
inkscape:cx="81.620292"
|
||||
inkscape:cy="119.68434"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1377"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="2066"
|
||||
inkscape:window-x="-11"
|
||||
inkscape:window-y="-11"
|
||||
inkscape:window-maximized="1"
|
||||
units="px" />
|
||||
<metadata
|
||||
@@ -48,7 +48,7 @@
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
@@ -59,13 +59,8 @@
|
||||
style="display:inline"
|
||||
transform="translate(0,-860.36216)">
|
||||
<path
|
||||
style="fill:#000000"
|
||||
d="M 75.009766 60.486328 L 34.648438 122.74414 C 33.793918 123.59769 37.647081 134.96384 37.052734 136.09766 L 4.0234375 177.69727 C 2.4142291 180.39677 3.2900484 182.21846 4.8730469 183.84766 C 5.9214414 184.93636 6.6591287 186.06887 8.3828125 185.67188 C 9.0750612 185.50987 10.104893 185.27338 10.875 184.76758 L 52.806641 151.81445 C 53.912466 151.23195 64.44071 154.77813 65.289062 153.92383 L 126.45312 111.46875 L 75.009766 60.486328 z M 89.632812 84.769531 L 103.77539 98.912109 L 79.027344 123.66016 L 86.238281 130.87109 L 48.621094 139.92383 L 57.435547 102.30859 L 64.884766 109.51758 L 89.632812 84.769531 z "
|
||||
transform="translate(0,860.36216)"
|
||||
id="path4140" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
d="m 111.18463,862.06984 c -1.98231,0 -3.96282,0.78454 -5.54759,2.38445 L 88.200894,881.94537 75.123368,868.82653 c -3.169466,-3.18017 -7.92567,-3.18017 -11.095213,0 -3.169466,3.18017 -3.169466,7.95108 0,11.13109 l 13.077526,13.11885 -11.095212,10.73223 82.031621,81.49227 11.09525,-11.13109 13.87084,13.51554 c 1.57915,1.59105 3.56724,2.38445 5.54759,2.38445 1.98231,0 3.96285,-0.78453 5.54762,-2.38445 3.16947,-3.18017 3.16947,-7.95111 0,-11.13109 l -13.87083,-13.11884 17.43611,-17.09442 c 1.17983,-1.59105 1.98231,-3.5788 1.98231,-5.96329 0,-1.98351 -0.79863,-3.97554 -2.37816,-5.56446 l -70.54053,-70.35856 c -1.57914,-1.59105 -3.56724,-2.38446 -5.54758,-2.38446 z m 15.86949,20.75826 9.50139,9.5291 -4.04453,23.11372 23.04691,-4.05619 9.50138,9.5291 -36.8437,36.95052 -9.50135,-9.5291 21.13082,-21.19197 -23.04672,4.05619 4.04453,-23.11396 -21.131009,21.19198 -9.501383,-9.5291 36.843662,-36.95048 z"
|
||||
style="fill:#006680;fill-opacity:1;stroke-width:1.09204066"
|
||||
d="m 34.5307,873.24547 c -1.506145,1.5557 -2.414834,3.72567 -2.403331,6.22497 l 0.04173,27.41047 -19.903814,-0.0324 c -4.824408,-0.009 -8.4381391,3.7242 -8.4300642,8.70738 0.00817,4.98312 3.6330369,8.72727 8.4573232,8.73554 l 19.903821,0.0324 -0.275791,17.12991 124.244306,-0.42328 -0.0273,-17.44297 20.80798,-0.27883 c 2.4087,0.009 4.52205,-0.92824 6.02671,-2.48239 1.50614,-1.55569 2.41486,-3.72569 2.40336,-6.225 -0.009,-4.98311 -3.63306,-8.7273 -8.45733,-8.73554 l -20.50656,0.59015 0.25961,-27.09914 c -0.31244,-2.17456 -1.213,-4.36429 -3.02472,-6.23561 -1.50705,-1.55665 -3.62738,-2.49321 -6.03475,-2.50058 l -107.054134,0.14282 c -2.408688,-0.009 -4.522059,0.92823 -6.026707,2.48237 z m 27.829505,3.83665 14.459233,0.0217 14.488635,21.31347 14.429017,-21.27019 14.45922,0.0217 0.0811,57.91281 -14.4592,-0.0217 -0.0464,-33.21443 -14.428869,21.27005 -14.488819,-21.31366 0.04632,33.21458 -14.459226,-0.0218 -0.08116,-57.91275 z"
|
||||
id="path4142"
|
||||
inkscape:connector-curvature="0" />
|
||||
<rect
|
||||
@@ -95,7 +90,7 @@
|
||||
<flowRoot
|
||||
xml:space="preserve"
|
||||
id="flowRoot4797"
|
||||
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
transform="matrix(0.1746417,0,0,0.1459084,499.69318,366.39614)"><flowRegion
|
||||
id="flowRegion4799"><rect
|
||||
id="rect4801"
|
||||
@@ -103,7 +98,8 @@
|
||||
height="618.71844"
|
||||
x="959.01355"
|
||||
y="-976.15039" /></flowRegion><flowPara
|
||||
id="flowPara4803" /></flowRoot> <g
|
||||
id="flowPara4803"
|
||||
style="font-size:40px;line-height:1.25"> </flowPara></flowRoot> <g
|
||||
id="g4833"
|
||||
transform="matrix(0.09510056,0,0,0.09061765,496.09965,368.83934)">
|
||||
<rect
|
||||
@@ -114,18 +110,20 @@
|
||||
y="0"
|
||||
style="fill:#ffffff" />
|
||||
</g>
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4886"
|
||||
d="m 111.18463,862.06984 c -1.98231,0 -3.96282,0.78454 -5.54759,2.38445 L 88.200894,881.94537 75.123368,868.82653 c -3.169466,-3.18017 -7.92567,-3.18017 -11.095213,0 -3.169466,3.18017 -3.169466,7.95108 0,11.13109 l 13.077526,13.11885 -11.095212,10.73223 82.031621,81.49227 11.09525,-11.13109 13.87084,13.51554 c 1.57915,1.59105 3.56724,2.38445 5.54759,2.38445 1.98231,0 3.96285,-0.78453 5.54762,-2.38445 3.16947,-3.18017 3.16947,-7.95111 0,-11.13109 l -13.87083,-13.11884 17.43611,-17.09442 c 1.17983,-1.59105 1.98231,-3.5788 1.98231,-5.96329 0,-1.98351 -0.79863,-3.97554 -2.37816,-5.56446 l -70.54053,-70.35856 c -1.57914,-1.59105 -3.56724,-2.38446 -5.54758,-2.38446 z m 15.86949,20.75826 9.50139,9.5291 -4.04453,23.11372 23.04691,-4.05619 9.50138,9.5291 -36.8437,36.95052 -9.50135,-9.5291 21.13082,-21.19197 -23.04672,4.05619 4.04453,-23.11396 -21.131009,21.19198 -9.501383,-9.5291 36.843662,-36.95048 z"
|
||||
style="fill:#000000;fill-opacity:1" />
|
||||
<g
|
||||
transform="translate(234.63786,787.55486)"
|
||||
id="g4170" />
|
||||
<path
|
||||
id="path4225"
|
||||
transform="translate(0,860.36216)"
|
||||
d="M 75.009766 60.486328 L 34.648438 122.74414 C 33.793918 123.59769 37.647081 134.96384 37.052734 136.09766 L 4.0234375 177.69727 C 2.4142291 180.39677 3.2900484 182.21846 4.8730469 183.84766 C 5.9214414 184.93636 6.6591287 186.06887 8.3828125 185.67188 C 9.0750612 185.50987 10.104893 185.27338 10.875 184.76758 L 52.806641 151.81445 C 53.912466 151.23195 64.44071 154.77813 65.289062 153.92383 L 126.45312 111.46875 L 75.009766 60.486328 z M 89.632812 84.769531 L 103.77539 98.912109 L 79.027344 123.66016 L 86.238281 130.87109 L 48.621094 139.92383 L 57.435547 102.30859 L 64.884766 109.51758 L 89.632812 84.769531 z "
|
||||
style="fill:#000000" />
|
||||
sodipodi:nodetypes="cccccccccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path826"
|
||||
d="m 45.058494,943.54749 20.013843,88.87081 c -8.87e-4,1.4793 13.909884,7.9857 28.77803,7.9868 14.868153,0 27.732473,-6.0454 27.727033,-7.5199 l 17.1004,-89.73688 z m 35.561125,8.36596 h 25.853221 l 10e-6,42.86491 h 13.18189 l -26.108514,40.41734 -26.324515,-40.20925 13.397908,-0.20809 z"
|
||||
style="fill:#ff6600;stroke-width:1.2582258" />
|
||||
<path
|
||||
style="fill:#c83737;stroke-width:1.2582258"
|
||||
d="m 45.058494,943.54749 20.013843,88.87081 c -8.87e-4,1.4793 13.909884,7.9857 28.77803,7.9868 14.868153,0 27.732473,-6.0454 27.727033,-7.5199 l 17.1004,-89.73688 z m 35.561125,8.36596 h 25.853221 l 10e-6,42.86491 h 13.18189 l -26.108514,40.41734 -26.324515,-40.20925 13.397908,-0.20809 z"
|
||||
id="path828"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccccccc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.0 KiB |
15
readme.md
@@ -1,4 +1,4 @@
|
||||
# Markdig [](https://ci.appveyor.com/project/xoofx/markdig) [](https://coveralls.io/github/lunet-io/markdig?branch=master) [](https://www.nuget.org/packages/Markdig/) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRGHXBTP442JL)
|
||||
# Markdig [](https://github.com/lunet-io/markdig/actions) [](https://coveralls.io/github/lunet-io/markdig?branch=master) [](https://www.nuget.org/packages/Markdig/) [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FRGHXBTP442JL)
|
||||
|
||||
<img align="right" width="160px" height="160px" src="img/markdig.png">
|
||||
|
||||
@@ -21,7 +21,7 @@ You can **try Markdig online** and compare it to other implementations on [babel
|
||||
- Even the core Markdown/CommonMark parsing is pluggable, so it allows to disable builtin Markdown/Commonmark parsing (e.g [Disable HTML parsing](https://github.com/lunet-io/markdig/blob/7964bd0160d4c18e4155127a4c863d61ebd8944a/src/Markdig/MarkdownExtensions.cs#L306)) or change behaviour (e.g change matching `#` of a headers with `@`)
|
||||
- Built-in with **20+ extensions**, including:
|
||||
- 2 kind of tables:
|
||||
- [**Pipe tables**](src/Markdig.Tests/Specs/PipeTableSpecs.md) (inspired from Github tables and [PanDoc - Pipe Tables](http://pandoc.org/README.html#extension-pipe_tables))
|
||||
- [**Pipe tables**](src/Markdig.Tests/Specs/PipeTableSpecs.md) (inspired from GitHub tables and [PanDoc - Pipe Tables](http://pandoc.org/README.html#extension-pipe_tables))
|
||||
- [**Grid tables**](src/Markdig.Tests/Specs/GridTableSpecs.md) (inspired from [Pandoc - Grid Tables](http://pandoc.org/README.html#extension-grid_tables))
|
||||
- [**Extra emphasis**](src/Markdig.Tests/Specs/EmphasisExtraSpecs.md) (inspired from [Pandoc - Emphasis](http://pandoc.org/README.html#strikeout) and [Markdown-it](https://markdown-it.github.io/))
|
||||
- strike through `~~`,
|
||||
@@ -48,15 +48,18 @@ You can **try Markdig online** and compare it to other implementations on [babel
|
||||
- [**SmartyPants**](src/Markdig.Tests/Specs/SmartyPantsSpecs.md) (inspired from [Daring Fireball - SmartyPants](https://daringfireball.net/projects/smartypants/))
|
||||
- [**Bootstrap**](src/Markdig.Tests/Specs/BootstrapSpecs.md) class (to output bootstrap class)
|
||||
- [**Diagrams**](src/Markdig.Tests/Specs/DiagramsSpecs.md) extension whenever a fenced code block contains a special keyword, it will be converted to a div block with the content as-is (currently, supports [`mermaid`](https://knsv.github.io/mermaid/) and [`nomnoml`](https://github.com/skanaar/nomnoml) diagrams)
|
||||
- [**YAML frontmatter**](src/Markdig.Tests/Specs/YamlSpecs.md) to parse without evaluating the frontmatter and to discard it from the HTML output (typically used for previewing without the frontmatter in MarkdownEditor)
|
||||
- [**YAML Front Matter**](src/Markdig.Tests/Specs/YamlSpecs.md) to parse without evaluating the front matter and to discard it from the HTML output (typically used for previewing without the front matter in MarkdownEditor)
|
||||
- [**JIRA links**](src/Markdig.Tests/Specs/JiraLinks.md) to automatically generate links for JIRA project references (Thanks to @clarkd: https://github.com/clarkd/MarkdigJiraLinker)
|
||||
- Compatible with .NET 3.5, 4.0+ and .NET Core (`netstandard1.1+`)
|
||||
- Starting with Markdig version `0.20.0+`, Markdig is compatible only with `NETStandard 2.0`, `NETStandard 2.1`, `NETCoreApp 2.1` and `NETCoreApp 3.1`.
|
||||
|
||||
If you are looking for support for an old .NET Framework 3.5 or 4.0, you can download Markdig `0.18.3`.
|
||||
|
||||
### Third Party Extensions
|
||||
|
||||
- [**WPF/XAML Markdown Renderer**: `markdig.wpf`](https://github.com/Kryptos-FR/markdig.wpf)
|
||||
- [**WPF/XAML Markdown Renderer**: `Neo.Markdig.Xaml`](https://github.com/neolithos/NeoMarkdigXaml)
|
||||
- [**Syntax highlighting**: `Markdig.SyntaxHighlighting`](https://github.com/RichardSlater/Markdig.SyntaxHighlighting)
|
||||
- [**Syntax highlighting using Prism.js**: `WebStoating.Markdig.Prism`](https://github.com/ilich/Markdig.Prism)
|
||||
- [**Embedded C# scripting**: `Markdig.Extensions.ScriptCs`](https://github.com/macaba/Markdig.Extensions.ScriptCs)
|
||||
|
||||
## Documentation
|
||||
@@ -84,7 +87,7 @@ var result = Markdown.ToHtml("This is a text with some *emphasis*");
|
||||
Console.WriteLine(result); // prints: <p>This is a text with some <em>emphasis</em></p>
|
||||
```
|
||||
|
||||
In order to activate most of all advanced extensions (except Emoji, SoftLine as HardLine, JiraLinks and SmartyPants)
|
||||
In order to activate most of all advanced extensions (except Emoji, SoftLine as HardLine, Bootstrap, YAML Front Matter, JiraLinks and SmartyPants)
|
||||
|
||||
```csharp
|
||||
// Configure the pipeline with all advanced extensions active
|
||||
@@ -92,6 +95,8 @@ var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
var result = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
|
||||
```
|
||||
|
||||
[Try it online!](https://dotnetfiddle.net/GoZXyI)
|
||||
|
||||
You can have a look at the [MarkdownExtensions](https://github.com/lunet-io/markdig/blob/master/src/Markdig/MarkdownExtensions.cs) that describes all actionable extensions (by modifying the MarkdownPipeline)
|
||||
|
||||
## Build
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net471</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="spec.md" />
|
||||
|
||||
@@ -5,14 +5,9 @@ extern alias newcmark;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Diagnostics;
|
||||
using BenchmarkDotNet.Diagnostics.Windows;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using BenchmarkDotNet.Running;
|
||||
using newcmark::CommonMark.Extension;
|
||||
using Markdig;
|
||||
|
||||
|
||||
@@ -55,7 +50,7 @@ namespace Testamina.Markdig.Benchmarks
|
||||
//CommonMark.CommonMarkConverter.Parse(reader);
|
||||
//reader.Dispose();
|
||||
//var writer = new StringWriter();
|
||||
global::CommonMark.CommonMarkConverter.Convert(text);
|
||||
CommonMark.CommonMarkConverter.Convert(text);
|
||||
//writer.Flush();
|
||||
//writer.ToString();
|
||||
}
|
||||
|
||||
@@ -2,13 +2,9 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Markdig.Helpers;
|
||||
|
||||
namespace Testamina.Markdig.Benchmarks
|
||||
{
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net451;netcoreapp2.1</TargetFrameworks>
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.msbuild" Version="2.6.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Markdig\Markdig.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp3.1</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<IsPackable>false</IsPackable>
|
||||
<StartupObject>Markdig.Tests.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.msbuild" Version="2.9.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Markdig\Markdig.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,191 +1,265 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using Markdig.Extensions.AutoLinks;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class MiscTests
|
||||
{
|
||||
[Test]
|
||||
public void TestAltTextIsCorrectlyEscaped()
|
||||
{
|
||||
TestParser.TestSpec(
|
||||
@"",
|
||||
@"<p><img src=""girl.png"" alt=""This is image alt text with quotation ' and double quotation "hello" world"" /></p>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangelogPRLinksMatchDescription()
|
||||
{
|
||||
string solutionFolder = Path.GetFullPath(Path.Combine(TestParser.TestsDirectory, "../.."));
|
||||
string changelogPath = Path.Combine(solutionFolder, "changelog.md");
|
||||
string changelog = File.ReadAllText(changelogPath);
|
||||
var matches = Regex.Matches(changelog, @"\(\[\(PR #(\d+)\)\]\(.*?pull\/(\d+)\)\)");
|
||||
Assert.Greater(matches.Count, 0);
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
Assert.True(int.TryParse(match.Groups[1].Value, out int textNr));
|
||||
Assert.True(int.TryParse(match.Groups[2].Value, out int linkNr));
|
||||
Assert.AreEqual(textNr, linkNr);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFixHang()
|
||||
{
|
||||
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "hang.md"));
|
||||
_ = Markdown.ToHtml(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidHtmlEntity()
|
||||
{
|
||||
var input = "9&ddr;&*&ddr;&de<64><65>__";
|
||||
TestParser.TestSpec(input, "<p>9&ddr;&*&ddr;&de<64><65>__</p>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidCharacterHandling()
|
||||
{
|
||||
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "ArgumentOutOfRangeException.md"));
|
||||
_ = Markdown.ToHtml(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidCodeEscape()
|
||||
{
|
||||
var input = "```**Header** ";
|
||||
_ = Markdown.ToHtml(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmphasisAndHtmlEntity()
|
||||
{
|
||||
var markdownText = "*Unlimited-Fun®*®";
|
||||
TestParser.TestSpec(markdownText, "<p><em>Unlimited-Fun®</em>®</p>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestThematicInsideCodeBlockInsideList()
|
||||
{
|
||||
var input = @"1. In the :
|
||||
|
||||
```
|
||||
Id DisplayName Description
|
||||
-- ----------- -----------
|
||||
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
|
||||
```";
|
||||
TestParser.TestSpec(input, @"<ol>
|
||||
<li><p>In the :</p>
|
||||
<pre><code>Id DisplayName Description
|
||||
-- ----------- -----------
|
||||
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
|
||||
</code></pre></li>
|
||||
</ol>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VisualizeMathExpressions()
|
||||
{
|
||||
string math = @"Math expressions
|
||||
|
||||
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
|
||||
|
||||
$$\frac{n!}{k!(n-k)!} = \binom{n}{k}$$
|
||||
|
||||
$$
|
||||
\frac{n!}{k!(n-k)!} = \binom{n}{k}
|
||||
$$
|
||||
|
||||
<div class=""math"">
|
||||
\begin{align}
|
||||
\sqrt{37} & = \sqrt{\frac{73^2-1}{12^2}} \\
|
||||
& = \sqrt{\frac{73^2}{12^2}\cdot\frac{73^2-1}{73^2}} \\
|
||||
& = \sqrt{\frac{73^2}{12^2}}\sqrt{\frac{73^2-1}{73^2}} \\
|
||||
& = \frac{73}{12}\sqrt{1 - \frac{1}{73^2}} \\
|
||||
& \approx \frac{73}{12}\left(1 - \frac{1}{2\cdot73^2}\right)
|
||||
\end{align}
|
||||
</div>
|
||||
";
|
||||
Console.WriteLine("Math Expressions:\n");
|
||||
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
|
||||
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
Console.WriteLine(html);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InlineMathExpression()
|
||||
{
|
||||
string math = @"Math expressions
|
||||
|
||||
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
|
||||
";
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
|
||||
var 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");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BlockMathExpression()
|
||||
{
|
||||
string math = @"Math expressions
|
||||
|
||||
$$
|
||||
\frac{n!}{k!(n-k)!} = \binom{n}{k}
|
||||
$$
|
||||
";
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
Console.WriteLine(html);
|
||||
|
||||
Assert.IsTrue(html.Contains("<div class=\"math\">\n\\["), "Leading bracket missing");
|
||||
Assert.IsTrue(html.Contains("\\]</div>"), "Trailing bracket missing");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanDisableParsingHeadings()
|
||||
{
|
||||
var noHeadingsPipeline = new MarkdownPipelineBuilder().DisableHeadings().Build();
|
||||
|
||||
TestParser.TestSpec("Foo\n===", "<h1>Foo</h1>");
|
||||
TestParser.TestSpec("Foo\n===", "<p>Foo\n===</p>", noHeadingsPipeline);
|
||||
|
||||
TestParser.TestSpec("# Heading 1", "<h1>Heading 1</h1>");
|
||||
TestParser.TestSpec("# Heading 1", "<p># Heading 1</p>", noHeadingsPipeline);
|
||||
|
||||
// Does not also disable link reference definitions
|
||||
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>");
|
||||
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>", noHeadingsPipeline);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanOpenAutoLinksInNewWindow()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
|
||||
var newWindowPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { OpenInNewWindow = true }).Build();
|
||||
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\" target=\"blank\">www.foo.bar</a></p>", newWindowPipeline);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanUseHttpsPrefixForWWWAutoLinks()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
|
||||
var httpsPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { UseHttpsForWWWLinks = true }).Build();
|
||||
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"https://www.foo.bar\">www.foo.bar</a></p>", httpsPipeline);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Markdig.Extensions.AutoLinks;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class MiscTests
|
||||
{
|
||||
[Test]
|
||||
public void LinkWithInvalidNonAsciiDomainNameIsIgnored()
|
||||
{
|
||||
// Url from https://github.com/lunet-io/markdig/issues/438
|
||||
_ = Markdown.ToHtml("[minulém díle](http://V%20minulém%20díle%20jsme%20nainstalovali%20SQL%20Server,%20který%20je%20nutný%20pro%20běh%20Configuration%20Manageru.%20Dnes%20nás%20čeká%20instalace%20WSUS,%20což%20je%20produkt,%20jež%20je%20možné%20používat%20i%20jako%20samostatnou%20funkci%20ve%20Windows%20Serveru,%20který%20se%20stará%20o%20stažení%20a%20instalaci%20aktualizací%20z%20Microsoft%20Update%20na%20klientské%20počítače.%20Stejně%20jako%20v%20předchozích%20dílech,%20tak%20i%20v%20tomto%20si%20ukážeme%20obě%20varianty%20instalace%20–%20a%20to%20jak%20instalaci%20z%20PowerShellu,%20tak%20instalaci%20pomocí%20GUI.) ");
|
||||
|
||||
// Valid IDN
|
||||
TestParser.TestSpec("[foo](http://ünicode.com)", "<p><a href=\"http://xn--nicode-2ya.com\">foo</a></p>");
|
||||
TestParser.TestSpec("[foo](http://ünicode.ünicode.com)", "<p><a href=\"http://xn--nicode-2ya.xn--nicode-2ya.com\">foo</a></p>");
|
||||
|
||||
// Invalid IDN
|
||||
TestParser.TestSpec("[foo](http://ünicode..com)", "<p><a href=\"http://%C3%BCnicode..com\">foo</a></p>");
|
||||
}
|
||||
|
||||
[TestCase("link [foo [bar]]")] // https://spec.commonmark.org/0.29/#example-508
|
||||
[TestCase("link [foo][bar]")]
|
||||
[TestCase("link [][foo][bar][]")]
|
||||
[TestCase("link [][foo][bar][[]]")]
|
||||
[TestCase("link [foo] [bar]")]
|
||||
[TestCase("link [[foo] [] [bar] [[abc]def]]")]
|
||||
[TestCase("[]")]
|
||||
[TestCase("[ ]")]
|
||||
[TestCase("[bar][]")]
|
||||
[TestCase("[bar][ foo]")]
|
||||
[TestCase("[bar][foo ][]")]
|
||||
[TestCase("[bar][fo[ ]o ][][]")]
|
||||
[TestCase("[a]b[c[d[e]f]g]h")]
|
||||
[TestCase("a[b[c[d]e]f[g]h]i foo [j]k[l[m]n]o")]
|
||||
[TestCase("a[b[c[d]e]f[g]h]i[] [][foo][bar][] foo [j]k[l[m]n]o")]
|
||||
[TestCase("a[b[c[d]e]f[g]h]i foo [j]k[l[m]n]o[][]")]
|
||||
public void LinkTextMayContainBalancedBrackets(string linkText)
|
||||
{
|
||||
string markdown = $"[{linkText}](/uri)";
|
||||
string expected = $@"<p><a href=""/uri"">{linkText}</a></p>";
|
||||
|
||||
TestParser.TestSpec(markdown, expected);
|
||||
|
||||
// Make the link text unbalanced
|
||||
foreach (var bracketIndex in linkText
|
||||
.Select((c, i) => new Tuple<char, int>(c, i))
|
||||
.Where(t => t.Item1 == '[' || t.Item1 == ']')
|
||||
.Select(t => t.Item2))
|
||||
{
|
||||
string brokenLinkText = linkText.Remove(bracketIndex, 1);
|
||||
|
||||
markdown = $"[{brokenLinkText}](/uri)";
|
||||
expected = $@"<p><a href=""/uri"">{brokenLinkText}</a></p>";
|
||||
|
||||
string actual = Markdown.ToHtml(markdown);
|
||||
Assert.AreNotEqual(expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsIssue356Corrected()
|
||||
{
|
||||
string input = @"https://foo.bar/path/\#m4mv5W0GYKZpGvfA.97";
|
||||
string expected = @"<p><a href=""https://foo.bar/path/%5C#m4mv5W0GYKZpGvfA.97"">https://foo.bar/path/\#m4mv5W0GYKZpGvfA.97</a></p>";
|
||||
|
||||
TestParser.TestSpec($"<{input}>", expected);
|
||||
TestParser.TestSpec(input, expected, "autolinks|advanced");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsIssue365Corrected()
|
||||
{
|
||||
// The scheme must be escaped too...
|
||||
string input = "";
|
||||
string expected = "<p><img src=\"%22onclick=%22alert&#40;%27click%27&#41;%22://\" alt=\"image\" /></p>";
|
||||
|
||||
TestParser.TestSpec(input, expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAltTextIsCorrectlyEscaped()
|
||||
{
|
||||
TestParser.TestSpec(
|
||||
@"",
|
||||
@"<p><img src=""girl.png"" alt=""This is image alt text with quotation ' and double quotation "hello" world"" /></p>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestChangelogPRLinksMatchDescription()
|
||||
{
|
||||
string solutionFolder = Path.GetFullPath(Path.Combine(TestParser.TestsDirectory, "../.."));
|
||||
string changelogPath = Path.Combine(solutionFolder, "changelog.md");
|
||||
string changelog = File.ReadAllText(changelogPath);
|
||||
var matches = Regex.Matches(changelog, @"\(\[\(PR #(\d+)\)\]\(.*?pull\/(\d+)\)\)");
|
||||
Assert.Greater(matches.Count, 0);
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
Assert.True(int.TryParse(match.Groups[1].Value, out int textNr));
|
||||
Assert.True(int.TryParse(match.Groups[2].Value, out int linkNr));
|
||||
Assert.AreEqual(textNr, linkNr);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFixHang()
|
||||
{
|
||||
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "hang.md"));
|
||||
_ = Markdown.ToHtml(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidHtmlEntity()
|
||||
{
|
||||
var input = "9&ddr;&*&ddr;&de<64><65>__";
|
||||
TestParser.TestSpec(input, "<p>9&ddr;&*&ddr;&de<64><65>__</p>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidCharacterHandling()
|
||||
{
|
||||
var input = File.ReadAllText(Path.Combine(TestParser.TestsDirectory, "ArgumentOutOfRangeException.md"));
|
||||
_ = Markdown.ToHtml(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInvalidCodeEscape()
|
||||
{
|
||||
var input = "```**Header** ";
|
||||
_ = Markdown.ToHtml(input);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestEmphasisAndHtmlEntity()
|
||||
{
|
||||
var markdownText = "*Unlimited-Fun®*®";
|
||||
TestParser.TestSpec(markdownText, "<p><em>Unlimited-Fun®</em>®</p>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestThematicInsideCodeBlockInsideList()
|
||||
{
|
||||
var input = @"1. In the :
|
||||
|
||||
```
|
||||
Id DisplayName Description
|
||||
-- ----------- -----------
|
||||
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
|
||||
```";
|
||||
TestParser.TestSpec(input, @"<ol>
|
||||
<li><p>In the :</p>
|
||||
<pre><code>Id DisplayName Description
|
||||
-- ----------- -----------
|
||||
62375ab9-6b52-47ed-826b-58e47e0e304b Group.Unified ...
|
||||
</code></pre></li>
|
||||
</ol>");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VisualizeMathExpressions()
|
||||
{
|
||||
string math = @"Math expressions
|
||||
|
||||
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
|
||||
|
||||
$$\frac{n!}{k!(n-k)!} = \binom{n}{k}$$
|
||||
|
||||
$$
|
||||
\frac{n!}{k!(n-k)!} = \binom{n}{k}
|
||||
$$
|
||||
|
||||
<div class=""math"">
|
||||
\begin{align}
|
||||
\sqrt{37} & = \sqrt{\frac{73^2-1}{12^2}} \\
|
||||
& = \sqrt{\frac{73^2}{12^2}\cdot\frac{73^2-1}{73^2}} \\
|
||||
& = \sqrt{\frac{73^2}{12^2}}\sqrt{\frac{73^2-1}{73^2}} \\
|
||||
& = \frac{73}{12}\sqrt{1 - \frac{1}{73^2}} \\
|
||||
& \approx \frac{73}{12}\left(1 - \frac{1}{2\cdot73^2}\right)
|
||||
\end{align}
|
||||
</div>
|
||||
";
|
||||
Console.WriteLine("Math Expressions:\n");
|
||||
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
|
||||
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
Console.WriteLine(html);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InlineMathExpression()
|
||||
{
|
||||
string math = @"Math expressions
|
||||
|
||||
$\frac{n!}{k!(n-k)!} = \binom{n}{k}$
|
||||
";
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
|
||||
var 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");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BlockMathExpression()
|
||||
{
|
||||
string math = @"Math expressions
|
||||
|
||||
$$
|
||||
\frac{n!}{k!(n-k)!} = \binom{n}{k}
|
||||
$$
|
||||
";
|
||||
var pl = new MarkdownPipelineBuilder().UseMathematics().Build(); // UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build()
|
||||
|
||||
var html = Markdown.ToHtml(math, pl);
|
||||
Console.WriteLine(html);
|
||||
|
||||
Assert.IsTrue(html.Contains("<div class=\"math\">\n\\["), "Leading bracket missing");
|
||||
Assert.IsTrue(html.Contains("\\]</div>"), "Trailing bracket missing");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanDisableParsingHeadings()
|
||||
{
|
||||
var noHeadingsPipeline = new MarkdownPipelineBuilder().DisableHeadings().Build();
|
||||
|
||||
TestParser.TestSpec("Foo\n===", "<h1>Foo</h1>");
|
||||
TestParser.TestSpec("Foo\n===", "<p>Foo\n===</p>", noHeadingsPipeline);
|
||||
|
||||
TestParser.TestSpec("# Heading 1", "<h1>Heading 1</h1>");
|
||||
TestParser.TestSpec("# Heading 1", "<p># Heading 1</p>", noHeadingsPipeline);
|
||||
|
||||
// Does not also disable link reference definitions
|
||||
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>");
|
||||
TestParser.TestSpec("[Foo]\n\n[Foo]: bar", "<p><a href=\"bar\">Foo</a></p>", noHeadingsPipeline);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanOpenAutoLinksInNewWindow()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
|
||||
var newWindowPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { OpenInNewWindow = true }).Build();
|
||||
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\" target=\"blank\">www.foo.bar</a></p>", newWindowPipeline);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanUseHttpsPrefixForWWWAutoLinks()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAutoLinks().Build();
|
||||
var httpsPipeline = new MarkdownPipelineBuilder().UseAutoLinks(new AutoLinkOptions() { UseHttpsForWWWLinks = true }).Build();
|
||||
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"http://www.foo.bar\">www.foo.bar</a></p>", pipeline);
|
||||
TestParser.TestSpec("www.foo.bar", "<p><a href=\"https://www.foo.bar\">www.foo.bar</a></p>", httpsPipeline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Headings
|
||||
@@ -79,15 +78,15 @@ namespace Markdig.Tests.Specs.Normalize.Headings
|
||||
// Heading
|
||||
// =======
|
||||
//
|
||||
// Text after two newlines
|
||||
// Text after two newlines 1
|
||||
//
|
||||
// Should be rendered as:
|
||||
// # Heading
|
||||
//
|
||||
// Text after two newlines
|
||||
// Text after two newlines 1
|
||||
|
||||
Console.WriteLine("Example 3\nSection Headings\n");
|
||||
TestNormalize.TestSpec("Heading\n=======\n\nText after two newlines", "# Heading\n\nText after two newlines", "");
|
||||
TestNormalize.TestSpec("Heading\n=======\n\nText after two newlines 1", "# Heading\n\nText after two newlines 1", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Headings
|
||||
|
||||
# Headings
|
||||
|
||||
```````````````````````````````` example
|
||||
# Heading 1
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
##### Heading 5
|
||||
|
||||
###### Heading 6
|
||||
````````````````````````````````
|
||||
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
###### Heading
|
||||
|
||||
@@ -34,15 +34,15 @@ Text after two newlines
|
||||
###### Heading
|
||||
|
||||
Text after two newlines
|
||||
````````````````````````````````
|
||||
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
Heading
|
||||
=======
|
||||
|
||||
Text after two newlines
|
||||
Text after two newlines 1
|
||||
.
|
||||
# Heading
|
||||
|
||||
Text after two newlines
|
||||
Text after two newlines 1
|
||||
````````````````````````````````
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Sample
|
||||
|
||||
19
src/Markdig.Tests/Program.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Run NUnit tests runner with this");
|
||||
// Uncomment the following line to debug a specific tests more easily
|
||||
//var tests = new Specs.CommonMarkV_0_29.TestLeafBlocksSetextHeadings();
|
||||
//tests.LeafBlocksSetextHeadings_Example063();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Abbreviations
|
||||
|
||||
@@ -112,9 +112,9 @@ A
|
||||
The longest matching abbreviation should be used
|
||||
|
||||
```````````````````````````````` example
|
||||
*[Foo]: foo
|
||||
*[Foo Bar]: foobar
|
||||
|
||||
*[Foo]: foo
|
||||
*[Foo Bar]: foobar
|
||||
|
||||
Foo B
|
||||
.
|
||||
<p><abbr title="foo">Foo</abbr> B</p>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Auto Identifiers
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Auto Links
|
||||
@@ -23,6 +22,7 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// - `http://` or `https://`
|
||||
// - `ftp://`
|
||||
// - `mailto:`
|
||||
// - `tel:`
|
||||
// - `www.`
|
||||
[Test]
|
||||
public void ExtensionsAutoLinks_Example001()
|
||||
@@ -34,16 +34,18 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// This is a http://www.google.com URL and https://www.google.com
|
||||
// This is a ftp://test.com
|
||||
// And a mailto:email@toto.com
|
||||
// And a tel:+1555123456
|
||||
// And a plain www.google.com
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <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>
|
||||
// This is a <a href="ftp://test.com">ftp://test.com</a>
|
||||
// And a <a href="mailto:email@toto.com">email@toto.com</a>
|
||||
// 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 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 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");
|
||||
}
|
||||
|
||||
// But incomplete links will not be matched:
|
||||
@@ -58,15 +60,17 @@ namespace Markdig.Tests.Specs.AutoLinks
|
||||
// This is not a ftp:/test.com
|
||||
// And not a mailto:emailtoto.com
|
||||
// And not a plain www. or a www.x
|
||||
// And not a tel:
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is not a http:/www.google.com URL and https:/www.google.com
|
||||
// This is not a ftp:/test.com
|
||||
// And not a mailto:emailtoto.com
|
||||
// And not a plain www. or a www.x</p>
|
||||
// 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 ", "<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</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");
|
||||
}
|
||||
|
||||
// Previous character must be a punctuation or a valid space (tab, space, new line):
|
||||
|
||||
@@ -9,17 +9,20 @@ Autolinks will format as a HTML link any string that starts by:
|
||||
- `http://` or `https://`
|
||||
- `ftp://`
|
||||
- `mailto:`
|
||||
- `tel:`
|
||||
- `www.`
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a http://www.google.com URL and https://www.google.com
|
||||
This is a ftp://test.com
|
||||
And a mailto:email@toto.com
|
||||
And a tel:+1555123456
|
||||
And 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>
|
||||
This is a <a href="ftp://test.com">ftp://test.com</a>
|
||||
And a <a href="mailto:email@toto.com">email@toto.com</a>
|
||||
And a <a href="tel:+1555123456">+1555123456</a>
|
||||
And a plain <a href="http://www.google.com">www.google.com</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
@@ -30,11 +33,13 @@ This is not a http:/www.google.com URL and https:/www.google.com
|
||||
This is not a ftp:/test.com
|
||||
And not a mailto:emailtoto.com
|
||||
And not a plain www. or a www.x
|
||||
And not a tel:
|
||||
.
|
||||
<p>This is not a http:/www.google.com URL and https:/www.google.com
|
||||
This is not a ftp:/test.com
|
||||
And not a mailto:emailtoto.com
|
||||
And not a plain www. or a www.x</p>
|
||||
And not a plain www. or a www.x
|
||||
And not a tel:</p>
|
||||
````````````````````````````````
|
||||
|
||||
Previous character must be a punctuation or a valid space (tab, space, new line):
|
||||
@@ -245,18 +250,18 @@ https://github.com:443
|
||||
|
||||
Links with unicode characters in the path / query / fragment are matched and url encoded
|
||||
|
||||
```````````````````````````````` example
|
||||
http://abc.net/☃
|
||||
|
||||
http://abc.net?☃
|
||||
|
||||
http://abc.net#☃
|
||||
|
||||
```````````````````````````````` example
|
||||
http://abc.net/☃
|
||||
|
||||
http://abc.net?☃
|
||||
|
||||
http://abc.net#☃
|
||||
|
||||
http://abc.net/foo#☃
|
||||
.
|
||||
<p><a href="http://abc.net/%E2%98%83">http://abc.net/☃</a></p>
|
||||
<p><a href="http://abc.net?%E2%98%83">http://abc.net?☃</a></p>
|
||||
<p><a href="http://abc.net#%E2%98%83">http://abc.net#☃</a></p>
|
||||
.
|
||||
<p><a href="http://abc.net/%E2%98%83">http://abc.net/☃</a></p>
|
||||
<p><a href="http://abc.net?%E2%98%83">http://abc.net?☃</a></p>
|
||||
<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>
|
||||
````````````````````````````````
|
||||
|
||||
@@ -270,18 +275,18 @@ http://☃.net?☃
|
||||
|
||||
Same goes for regular autolinks
|
||||
|
||||
```````````````````````````````` example
|
||||
<http://abc.net/☃>
|
||||
|
||||
<http://abc.net?☃>
|
||||
|
||||
<http://abc.net#☃>
|
||||
|
||||
```````````````````````````````` example
|
||||
<http://abc.net/☃>
|
||||
|
||||
<http://abc.net?☃>
|
||||
|
||||
<http://abc.net#☃>
|
||||
|
||||
<http://abc.net/foo#☃>
|
||||
.
|
||||
<p><a href="http://abc.net/%E2%98%83">http://abc.net/☃</a></p>
|
||||
<p><a href="http://abc.net?%E2%98%83">http://abc.net?☃</a></p>
|
||||
<p><a href="http://abc.net#%E2%98%83">http://abc.net#☃</a></p>
|
||||
.
|
||||
<p><a href="http://abc.net/%E2%98%83">http://abc.net/☃</a></p>
|
||||
<p><a href="http://abc.net?%E2%98%83">http://abc.net?☃</a></p>
|
||||
<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>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-15 05:23:49
|
||||
|
||||
// --------------------------------
|
||||
// Bootstrap
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-12 15:26:42
|
||||
|
||||
// --------------------------------
|
||||
// CommonMark v. 0.29
|
||||
@@ -14925,6 +14924,29 @@ namespace Markdig.Tests.Specs.CommonMarkV_0_29
|
||||
Console.WriteLine("Example 649\nSection Inlines / Textual content\n");
|
||||
TestParser.TestSpec("Multiple spaces", "<p>Multiple spaces</p>", "");
|
||||
}
|
||||
|
||||
// Within a blockquote a setext heading takes precedence
|
||||
// over a thematic break:
|
||||
[Test]
|
||||
public void InlinesTextualContent_Example650()
|
||||
{
|
||||
// Example 650
|
||||
// Section: Inlines / Textual content
|
||||
//
|
||||
// The following Markdown:
|
||||
// > Foo
|
||||
// > ---
|
||||
// > bar
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <blockquote>
|
||||
// <h2>Foo</h2>
|
||||
// <p>bar</p>
|
||||
// </blockquote>
|
||||
|
||||
Console.WriteLine("Example 650\nSection Inlines / Textual content\n");
|
||||
TestParser.TestSpec("> Foo\n> ---\n> bar", "<blockquote>\n<h2>Foo</h2>\n<p>bar</p>\n</blockquote>", "");
|
||||
}
|
||||
// <!-- END TESTS -->
|
||||
//
|
||||
// # Appendix: A parsing strategy
|
||||
|
||||
@@ -9368,6 +9368,20 @@ Multiple spaces
|
||||
<p>Multiple spaces</p>
|
||||
````````````````````````````````
|
||||
|
||||
Within a blockquote a setext heading takes precedence
|
||||
over a thematic break:
|
||||
|
||||
```````````````````````````````` example
|
||||
> Foo
|
||||
> ---
|
||||
> bar
|
||||
.
|
||||
<blockquote>
|
||||
<h2>Foo</h2>
|
||||
<p>bar</p>
|
||||
</blockquote>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
<!-- END TESTS -->
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Custom Containers
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-15 05:06:35
|
||||
|
||||
// --------------------------------
|
||||
// Definition Lists
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Diagrams
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-15 05:20:50
|
||||
|
||||
// --------------------------------
|
||||
// Emoji
|
||||
@@ -18,7 +17,7 @@ namespace Markdig.Tests.Specs.Emoji
|
||||
//
|
||||
// ## Emoji
|
||||
//
|
||||
// Emoji and smiley can be converted to their respective unicode characters:
|
||||
// Emoji shortcodes and smileys can be converted to their respective unicode characters:
|
||||
[Test]
|
||||
public void ExtensionsEmoji_Example001()
|
||||
{
|
||||
@@ -52,7 +51,7 @@ namespace Markdig.Tests.Specs.Emoji
|
||||
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");
|
||||
}
|
||||
|
||||
// Emoji can be followed by close punctuation (or any other characters):
|
||||
// Emojis can be followed by close punctuation (or any other characters):
|
||||
[Test]
|
||||
public void ExtensionsEmoji_Example003()
|
||||
{
|
||||
@@ -69,7 +68,7 @@ namespace Markdig.Tests.Specs.Emoji
|
||||
TestParser.TestSpec("We all need :), it makes us :muscle:. (and :ok_hand:).", "<p>We all need 😃, it makes us 💪. (and 👌).</p>", "emojis|advanced+emojis");
|
||||
}
|
||||
|
||||
// Sentences can end with Emoji:
|
||||
// Sentences can end with emojis:
|
||||
[Test]
|
||||
public void ExtensionsEmoji_Example004()
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ This section describes the different extensions supported:
|
||||
|
||||
## Emoji
|
||||
|
||||
Emoji and smiley can be converted to their respective unicode characters:
|
||||
Emoji shortcodes and smileys can be converted to their respective unicode characters:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a test with a :) and a :angry: smiley
|
||||
@@ -20,7 +20,7 @@ These are not:) an emoji with a:) x:angry:x
|
||||
<p>These are not:) an emoji with a:) x:angry:x</p>
|
||||
````````````````````````````````
|
||||
|
||||
Emoji can be followed by close punctuation (or any other characters):
|
||||
Emojis can be followed by close punctuation (or any other characters):
|
||||
|
||||
```````````````````````````````` example
|
||||
We all need :), it makes us :muscle:. (and :ok_hand:).
|
||||
@@ -28,7 +28,7 @@ We all need :), it makes us :muscle:. (and :ok_hand:).
|
||||
<p>We all need 😃, it makes us 💪. (and 👌).</p>
|
||||
````````````````````````````````
|
||||
|
||||
Sentences can end with Emoji:
|
||||
Sentences can end with emojis:
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a sentence :ok_hand:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Emphasis Extra
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Figures, Footers and Cites
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-15 05:33:49
|
||||
|
||||
// --------------------------------
|
||||
// Footnotes
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Generic Attributes
|
||||
@@ -79,5 +78,28 @@ namespace Markdig.Tests.Specs.GenericAttributes
|
||||
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");
|
||||
}
|
||||
|
||||
// Attribute values can be one character long
|
||||
[Test]
|
||||
public void ExtensionsGenericAttributes_Example003()
|
||||
{
|
||||
// Example 3
|
||||
// Section: Extensions / Generic Attributes
|
||||
//
|
||||
// The following Markdown:
|
||||
// [Foo](url){data-x=1}
|
||||
//
|
||||
// [Foo](url){data-x='1'}
|
||||
//
|
||||
// [Foo](url){data-x=11}
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p><a href="url" data-x="1">Foo</a></p>
|
||||
// <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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,3 +47,17 @@ This is a fenced with attached attributes
|
||||
<pre><code id="fenced-id" class="fenced-class">This is a fenced with attached attributes
|
||||
</code></pre>
|
||||
````````````````````````````````
|
||||
|
||||
Attribute values can be one character long
|
||||
|
||||
```````````````````````````````` example
|
||||
[Foo](url){data-x=1}
|
||||
|
||||
[Foo](url){data-x='1'}
|
||||
|
||||
[Foo](url){data-x=11}
|
||||
.
|
||||
<p><a href="url" data-x="1">Foo</a></p>
|
||||
<p><a href="url" data-x="1">Foo</a></p>
|
||||
<p><a href="url" data-x="11">Foo</a></p>
|
||||
````````````````````````````````
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-15 05:25:26
|
||||
|
||||
// --------------------------------
|
||||
// Globalization
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Grid Tables
|
||||
@@ -62,8 +61,8 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:50%">
|
||||
// <col style="width:50%">
|
||||
// <col style="width:50%" />
|
||||
// <col style="width:50%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>This is</td>
|
||||
@@ -73,7 +72,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 1\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table |", "<table>\n<col style=\"width:50%\">\n<col style=\"width:50%\">\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table |", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// The following is not a valid row separator
|
||||
@@ -112,9 +111,9 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>Col1
|
||||
@@ -135,7 +134,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 3\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---------+---------+---------+\n| Col1 | Col2 | Col3 |\n| Col1a | Col2a | Col3a |\n| Col1b | Col3b |\n| Col1c |", "<table>\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<tbody>\n<tr>\n<td>Col1\nCol1a</td>\n<td>Col2\nCol2a</td>\n<td>Col3\nCol3a</td>\n</tr>\n<tr>\n<td colspan=\"2\">Col1b</td>\n<td>Col3b</td>\n</tr>\n<tr>\n<td colspan=\"3\">Col1c</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---------+---------+---------+\n| Col1 | Col2 | Col3 |\n| Col1a | Col2a | Col3a |\n| Col1b | Col3b |\n| Col1c |", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td>Col1\nCol1a</td>\n<td>Col2\nCol2a</td>\n<td>Col3\nCol3a</td>\n</tr>\n<tr>\n<td colspan=\"2\">Col1b</td>\n<td>Col3b</td>\n</tr>\n<tr>\n<td colspan=\"3\">Col1c</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// A row header is separated using `+========+` instead of `+---------+`:
|
||||
@@ -152,8 +151,8 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:50%">
|
||||
// <col style="width:50%">
|
||||
// <col style="width:50%" />
|
||||
// <col style="width:50%" />
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>This is</th>
|
||||
@@ -163,7 +162,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 4\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table |\n+=========+=========+", "<table>\n<col style=\"width:50%\">\n<col style=\"width:50%\">\n<thead>\n<tr>\n<th>This is</th>\n<th>a table</th>\n</tr>\n</thead>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table |\n+=========+=========+", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<thead>\n<tr>\n<th>This is</th>\n<th>a table</th>\n</tr>\n</thead>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// The last column separator `|` may be omitted:
|
||||
@@ -179,8 +178,8 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:50%">
|
||||
// <col style="width:50%">
|
||||
// <col style="width:50%" />
|
||||
// <col style="width:50%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>This is</td>
|
||||
@@ -190,7 +189,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 5\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table with a longer text in the second column", "<table>\n<col style=\"width:50%\">\n<col style=\"width:50%\">\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table with a longer text in the second column</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---------+---------+\n| This is | a table with a longer text in the second column", "<table>\n<col style=\"width:50%\" />\n<col style=\"width:50%\" />\n<tbody>\n<tr>\n<td>This is</td>\n<td>a table with a longer text in the second column</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// The respective width of the columns are calculated from the ratio between the total size of the first table row without counting the `+`: `+----+--------+----+` would be divided between:
|
||||
@@ -215,9 +214,9 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:25%">
|
||||
// <col style="width:50%">
|
||||
// <col style="width:25%">
|
||||
// <col style="width:25%" />
|
||||
// <col style="width:50%" />
|
||||
// <col style="width:25%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>A</td>
|
||||
@@ -228,7 +227,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 6\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+----+--------+----+\n| A | B C D | E |\n+----+--------+----+", "<table>\n<col style=\"width:25%\">\n<col style=\"width:50%\">\n<col style=\"width:25%\">\n<tbody>\n<tr>\n<td>A</td>\n<td>B C D</td>\n<td>E</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+----+--------+----+\n| A | B C D | E |\n+----+--------+----+", "<table>\n<col style=\"width:25%\" />\n<col style=\"width:50%\" />\n<col style=\"width:25%\" />\n<tbody>\n<tr>\n<td>A</td>\n<td>B C D</td>\n<td>E</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// Alignment might be specified on the first row using the character `:`:
|
||||
@@ -245,9 +244,9 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>A</td>
|
||||
@@ -258,7 +257,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 7\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+-----+:---:+-----+\n| A | B | C |\n+-----+-----+-----+", "<table>\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<tbody>\n<tr>\n<td>A</td>\n<td style=\"text-align: center;\">B</td>\n<td>C</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+-----+:---:+-----+\n| A | B | C |\n+-----+-----+-----+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td>A</td>\n<td style=\"text-align: center;\">B</td>\n<td>C</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// A grid table may have cells spanning both columns and rows:
|
||||
@@ -279,9 +278,9 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td colspan="2">AAAAA</td>
|
||||
@@ -302,7 +301,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 8\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+---+---+ B +\n| D | E | B |\n+ D +---+---+\n| D | CCCCC |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<tbody>\n<tr>\n<td colspan=\"2\">AAAAA</td>\n<td rowspan=\"2\">B\nB\nB</td>\n</tr>\n<tr>\n<td rowspan=\"2\">D\nD\nD</td>\n<td>E</td>\n</tr>\n<tr>\n<td colspan=\"2\">CCCCC</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+---+---+ B +\n| D | E | B |\n+ D +---+---+\n| D | CCCCC |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td colspan=\"2\">AAAAA</td>\n<td rowspan=\"2\">B\nB\nB</td>\n</tr>\n<tr>\n<td rowspan=\"2\">D\nD\nD</td>\n<td>E</td>\n</tr>\n<tr>\n<td colspan=\"2\">CCCCC</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// A grid table may have cells with both colspan and rowspan:
|
||||
@@ -323,9 +322,9 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%">
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <col style="width:33.33%" />
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td colspan="2" rowspan="2">AAAAA
|
||||
@@ -345,7 +344,7 @@ namespace Markdig.Tests.Specs.GridTables
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Extensions / Grid Table\n");
|
||||
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+ AAAAA +---+\n| AAAAA | C |\n+---+---+---+\n| D | E | F |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<col style=\"width:33.33%\">\n<tbody>\n<tr>\n<td colspan=\"2\" rowspan=\"2\">AAAAA\nAAAAA\nAAAAA</td>\n<td>B</td>\n</tr>\n<tr>\n<td>C</td>\n</tr>\n<tr>\n<td>D</td>\n<td>E</td>\n<td>F</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
TestParser.TestSpec("+---+---+---+\n| AAAAA | B |\n+ AAAAA +---+\n| AAAAA | C |\n+---+---+---+\n| D | E | F |\n+---+---+---+", "<table>\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<col style=\"width:33.33%\" />\n<tbody>\n<tr>\n<td colspan=\"2\" rowspan=\"2\">AAAAA\nAAAAA\nAAAAA</td>\n<td>B</td>\n</tr>\n<tr>\n<td>C</td>\n</tr>\n<tr>\n<td>D</td>\n<td>E</td>\n<td>F</td>\n</tr>\n</tbody>\n</table>", "gridtables|advanced");
|
||||
}
|
||||
|
||||
// A grid table may not have irregularly shaped cells:
|
||||
|
||||
@@ -42,8 +42,8 @@ The following is a valid row separator
|
||||
| This is | a table |
|
||||
.
|
||||
<table>
|
||||
<col style="width:50%">
|
||||
<col style="width:50%">
|
||||
<col style="width:50%" />
|
||||
<col style="width:50%" />
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>This is</td>
|
||||
@@ -74,9 +74,9 @@ A regular row can continue a previous regular row when column separator `|` are
|
||||
| Col1c |
|
||||
.
|
||||
<table>
|
||||
<col style="width:33.33%">
|
||||
<col style="width:33.33%">
|
||||
<col style="width:33.33%">
|
||||
<col style="width:33.33%" />
|
||||
<col style="width:33.33%" />
|
||||
<col style="width:33.33%" />
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Col1
|
||||
@@ -105,8 +105,8 @@ A row header is separated using `+========+` instead of `+---------+`:
|
||||
+=========+=========+
|
||||
.
|
||||
<table>
|
||||
<col style="width:50%">
|
||||
<col style="width:50%">
|
||||
<col style="width:50%" />
|
||||
<col style="width:50%" />
|
||||
<thead>
|
||||
<tr>
|
||||
<th>This is</th>
|
||||
@@ -123,8 +123,8 @@ The last column separator `|` may be omitted:
|
||||
| This is | a table with a longer text in the second column
|
||||
.
|
||||
<table>
|
||||
<col style="width:50%">
|
||||
<col style="width:50%">
|
||||
<col style="width:50%" />
|
||||
<col style="width:50%" />
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>This is</td>
|
||||
@@ -150,9 +150,9 @@ So the width would be 4/16 = 25%, 8/16 = 50%, 4/16 = 25%
|
||||
+----+--------+----+
|
||||
.
|
||||
<table>
|
||||
<col style="width:25%">
|
||||
<col style="width:50%">
|
||||
<col style="width:25%">
|
||||
<col style="width:25%" />
|
||||
<col style="width:50%" />
|
||||
<col style="width:25%" />
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>A</td>
|
||||
@@ -172,9 +172,9 @@ Alignment might be specified on the first row using the character `:`:
|
||||
+-----+-----+-----+
|
||||
.
|
||||
<table>
|
||||
<col style="width:33.33%">
|
||||
<col style="width:33.33%">
|
||||
<col style="width:33.33%">
|
||||
<col style="width:33.33%" />
|
||||
<col style="width:33.33%" />
|
||||
<col style="width:33.33%" />
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>A</td>
|
||||
@@ -197,9 +197,9 @@ Alignment might be specified on the first row using the character `:`:
|
||||
+---+---+---+
|
||||
.
|
||||
<table>
|
||||
<col style="width:33.33%">
|
||||
<col style="width:33.33%">
|
||||
<col style="width:33.33%">
|
||||
<col style="width:33.33%" />
|
||||
<col style="width:33.33%" />
|
||||
<col style="width:33.33%" />
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2">AAAAA</td>
|
||||
@@ -232,9 +232,9 @@ A grid table may have cells with both colspan and rowspan:
|
||||
+---+---+---+
|
||||
.
|
||||
<table>
|
||||
<col style="width:33.33%">
|
||||
<col style="width:33.33%">
|
||||
<col style="width:33.33%">
|
||||
<col style="width:33.33%" />
|
||||
<col style="width:33.33%" />
|
||||
<col style="width:33.33%" />
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2" rowspan="2">AAAAA
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Hardline Breaks
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-15 05:30:00
|
||||
|
||||
// --------------------------------
|
||||
// Jira Links
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// List Extras
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Math
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-29 18:40:06
|
||||
|
||||
// --------------------------------
|
||||
// Media
|
||||
@@ -17,7 +16,7 @@ namespace Markdig.Tests.Specs.Media
|
||||
// Adds support for media links:
|
||||
//
|
||||
// ## Media links
|
||||
//
|
||||
//
|
||||
// Allows to embed audio/video links to popular website:
|
||||
[Test]
|
||||
public void ExtensionsMediaLinks_Example001()
|
||||
@@ -35,7 +34,7 @@ namespace Markdig.Tests.Specs.Media
|
||||
// 
|
||||
//
|
||||
// 
|
||||
//
|
||||
//
|
||||
// 
|
||||
//
|
||||
// 
|
||||
@@ -47,19 +46,19 @@ namespace Markdig.Tests.Specs.Media
|
||||
// 
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100&rel=0" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://www.youtube.com/embed?listType=playlist&list=PLC77007E23FF423C6" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://player.vimeo.com/video/8607834" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" class="youtube" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100" class="youtube" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" class="youtube" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100" class="youtube" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100&rel=0" class="youtube" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://www.youtube.com/embed?listType=playlist&list=PLC77007E23FF423C6" class="youtube" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><iframe src="https://player.vimeo.com/video/8607834" class="vimeo" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <p><video width="500" height="281" controls=""><source type="video/mp4" src="https://sample.com/video.mp4"></source></video></p>
|
||||
// <p><iframe src="https://music.yandex.ru/iframe/#track/4402274/411845/" width="500" height="281" frameborder="0"></iframe></p>
|
||||
// <p><iframe src="https://ok.ru/videoembed/26870090463" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
// <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\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ?start=100&rel=0\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://www.youtube.com/embed?listType=playlist&list=PLC77007E23FF423C6\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n<p><iframe src=\"https://player.vimeo.com/video/8607834\" 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/\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n<p><iframe src=\"https://ok.ru/videoembed/26870090463\" 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Adds support for media links:
|
||||
|
||||
## Media links
|
||||
|
||||
|
||||
Allows to embed audio/video links to popular website:
|
||||
|
||||
```````````````````````````````` example
|
||||
@@ -16,7 +16,7 @@ Allows to embed audio/video links to popular website:
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
@@ -27,14 +27,14 @@ Allows to embed audio/video links to popular website:
|
||||
|
||||

|
||||
.
|
||||
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100&rel=0" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://www.youtube.com/embed?listType=playlist&list=PLC77007E23FF423C6" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://player.vimeo.com/video/8607834" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" class="youtube" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100" class="youtube" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ" class="youtube" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100" class="youtube" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://www.youtube.com/embed/mswPy5bt3TQ?start=100&rel=0" class="youtube" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://www.youtube.com/embed?listType=playlist&list=PLC77007E23FF423C6" class="youtube" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><iframe src="https://player.vimeo.com/video/8607834" class="vimeo" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<p><video width="500" height="281" controls=""><source type="video/mp4" src="https://sample.com/video.mp4"></source></video></p>
|
||||
<p><iframe src="https://music.yandex.ru/iframe/#track/4402274/411845/" width="500" height="281" frameborder="0"></iframe></p>
|
||||
<p><iframe src="https://ok.ru/videoembed/26870090463" width="500" height="281" frameborder="0" allowfullscreen=""></iframe></p>
|
||||
<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>
|
||||
````````````````````````````````
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// No Html
|
||||
|
||||
856
src/Markdig.Tests/Specs/PipeTableGfmSpecs.generated.cs
Normal file
@@ -0,0 +1,856 @@
|
||||
|
||||
// --------------------------------
|
||||
// GFM Pipe Tables
|
||||
// --------------------------------
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests.Specs.GFMPipeTables
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestExtensionsGfmPipeTable
|
||||
{
|
||||
// # Extensions
|
||||
//
|
||||
// This section describes the different extensions supported:
|
||||
//
|
||||
// ## Gfm Pipe Table
|
||||
//
|
||||
// This groups a certain set of behaviors that makes pipe tables adhere more strictly to the GitHub-flavored Markdown specification.
|
||||
//
|
||||
// A pipe table is detected when:
|
||||
//
|
||||
// **Rule #1**
|
||||
// - Each line of a paragraph block have to contain at least a **column delimiter** `|` that is not embedded by either a code inline (backtick \`) or a HTML inline.
|
||||
// - The second row must separate the first header row from sub-sequent rows by containing a **header column separator** for each column separated by a column delimiter. A header column separator is:
|
||||
// - starting by optional spaces
|
||||
// - followed by an optional `:` to specify left align
|
||||
// - followed by a sequence of at least one `-` character
|
||||
// - followed by an optional `:` to specify right align (or center align if left align is also defined)
|
||||
// - ending by optional spaces
|
||||
//
|
||||
// Because a list has a higher precedence than a pipe table, a table header row separator requires at least 2 dashes `--` to start a header row:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example001()
|
||||
{
|
||||
// Example 1
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b
|
||||
// -- | -
|
||||
// 0 | 1
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>0</td>
|
||||
// <td>1</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// The following is also considered as a table, even if the second line starts like a list:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example002()
|
||||
{
|
||||
// Example 2
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b
|
||||
// - | -
|
||||
// 0 | 1
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>0</td>
|
||||
// <td>1</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// A pipe table with only one header row is allowed:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example003()
|
||||
{
|
||||
// Example 3
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b
|
||||
// -- | --
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// After a row separator header, they will be interpreted as plain column:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example004()
|
||||
{
|
||||
// Example 4
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b
|
||||
// -- | --
|
||||
// -- | --
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>--</td>
|
||||
// <td>--</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// 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
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example005()
|
||||
{
|
||||
// Example 5
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a b
|
||||
// c | d
|
||||
// e | f
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>a b
|
||||
// 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");
|
||||
}
|
||||
|
||||
// If a line doesn't have a column delimiter `|` the table is not detected
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example006()
|
||||
{
|
||||
// Example 6
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b
|
||||
// c no d
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <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");
|
||||
}
|
||||
|
||||
// If a row contains more columns than the header row, the extra columns will be ignored:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example007()
|
||||
{
|
||||
// Example 7
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b
|
||||
// -- | --
|
||||
// 0 | 1 | 2
|
||||
// 3 | 4
|
||||
// 5 |
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>0</td>
|
||||
// <td>1</td>
|
||||
// </tr>
|
||||
// <tr>
|
||||
// <td>3</td>
|
||||
// <td>4</td>
|
||||
// </tr>
|
||||
// <tr>
|
||||
// <td>5</td>
|
||||
// <td></td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// **Rule #2**
|
||||
// A pipe table ends after a blank line or the end of the file.
|
||||
//
|
||||
// **Rule #3**
|
||||
// A cell content is trimmed (start and end) from white-spaces.
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example008()
|
||||
{
|
||||
// Example 8
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b |
|
||||
// -- | --
|
||||
// 0 | 1 |
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>0</td>
|
||||
// <td>1</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// **Rule #4**
|
||||
// Column delimiters `|` at the very beginning of a line or just before a line ending with only spaces and/or terminated by a newline can be omitted
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example009()
|
||||
{
|
||||
// Example 9
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b |
|
||||
// -- | --
|
||||
// | 0 | 1
|
||||
// | 2 | 3 |
|
||||
// 4 | 5
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>0</td>
|
||||
// <td>1</td>
|
||||
// </tr>
|
||||
// <tr>
|
||||
// <td>2</td>
|
||||
// <td>3</td>
|
||||
// </tr>
|
||||
// <tr>
|
||||
// <td>4</td>
|
||||
// <td>5</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// A pipe may be present at both the beginning/ending of each line:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example010()
|
||||
{
|
||||
// Example 10
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// |a|b|
|
||||
// |-|-|
|
||||
// |0|1|
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>0</td>
|
||||
// <td>1</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// Or may be omitted on one side:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example011()
|
||||
{
|
||||
// Example 11
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a|b|
|
||||
// -|-|
|
||||
// 0|1|
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>0</td>
|
||||
// <td>1</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example012()
|
||||
{
|
||||
// Example 12
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// |a|b
|
||||
// |-|-
|
||||
// |0|1
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>0</td>
|
||||
// <td>1</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// Single column table can be declared with lines starting only by a column delimiter:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example013()
|
||||
{
|
||||
// Example 13
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// | a
|
||||
// | --
|
||||
// | b
|
||||
// | c
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>b</td>
|
||||
// </tr>
|
||||
// <tr>
|
||||
// <td>c</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// **Rule #5**
|
||||
//
|
||||
// The first row is considered as a **header row** if it is separated from the regular rows by a row containing a **header column separator** for each column. A header column separator is:
|
||||
//
|
||||
// - starting by optional spaces
|
||||
// - followed by an optional `:` to specify left align
|
||||
// - followed by a sequence of at least one `-` character
|
||||
// - followed by an optional `:` to specify right align (or center align if left align is also defined)
|
||||
// - ending by optional spaces
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example014()
|
||||
{
|
||||
// Example 14
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b
|
||||
// -------|-------
|
||||
// 0 | 1
|
||||
// 2 | 3
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>0</td>
|
||||
// <td>1</td>
|
||||
// </tr>
|
||||
// <tr>
|
||||
// <td>2</td>
|
||||
// <td>3</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// 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.
|
||||
// There is no way to define a different alignment for heading and cells (apart from the default).
|
||||
// The text alignment can be changed by using the character `:` with the header column separator:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example015()
|
||||
{
|
||||
// Example 15
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b | c
|
||||
// :------|:-------:| ----:
|
||||
// 0 | 1 | 2
|
||||
// 3 | 4 | 5
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th style="text-align: left;">a</th>
|
||||
// <th style="text-align: center;">b</th>
|
||||
// <th style="text-align: right;">c</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td style="text-align: left;">0</td>
|
||||
// <td style="text-align: center;">1</td>
|
||||
// <td style="text-align: right;">2</td>
|
||||
// </tr>
|
||||
// <tr>
|
||||
// <td style="text-align: left;">3</td>
|
||||
// <td style="text-align: center;">4</td>
|
||||
// <td style="text-align: right;">5</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// Test alignment with starting and ending pipes:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example016()
|
||||
{
|
||||
// Example 16
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// | abc | def | ghi |
|
||||
// |:---:|-----|----:|
|
||||
// | 1 | 2 | 3 |
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th style="text-align: center;">abc</th>
|
||||
// <th>def</th>
|
||||
// <th style="text-align: right;">ghi</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td style="text-align: center;">1</td>
|
||||
// <td>2</td>
|
||||
// <td style="text-align: right;">3</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// The following example shows a non matching header column separator:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example017()
|
||||
{
|
||||
// Example 17
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b
|
||||
// -------|---x---
|
||||
// 0 | 1
|
||||
// 2 | 3
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>a | b
|
||||
// -------|---x---
|
||||
// 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");
|
||||
}
|
||||
|
||||
// **Rule #6**
|
||||
//
|
||||
// A column delimiter has a higher priority than emphasis delimiter
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example018()
|
||||
{
|
||||
// Example 18
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// *a* | b
|
||||
// ----- |-----
|
||||
// 0 | _1_
|
||||
// _2 | 3*
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th><em>a</em></th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>0</td>
|
||||
// <td><em>1</em></td>
|
||||
// </tr>
|
||||
// <tr>
|
||||
// <td>_2</td>
|
||||
// <td>3*</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// **Rule #7**
|
||||
//
|
||||
// A backtick/code delimiter has a higher precedence than a column delimiter `|`:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example019()
|
||||
{
|
||||
// Example 19
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b `
|
||||
// 0 | `
|
||||
//
|
||||
// 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");
|
||||
}
|
||||
|
||||
// **Rule #8**
|
||||
//
|
||||
// A HTML inline has a higher precedence than a column delimiter `|`:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example020()
|
||||
{
|
||||
// Example 20
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a <a href="" title="|"></a> | b
|
||||
// -- | --
|
||||
// 0 | 1
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a <a href="" title="|"></a></th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>0</td>
|
||||
// <td>1</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// **Rule #9**
|
||||
//
|
||||
// Links have a higher precedence than the column delimiter character `|`:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example021()
|
||||
{
|
||||
// Example 21
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b
|
||||
// -- | --
|
||||
// [This is a link with a | inside the label](http://google.com) | 1
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td><a href="http://google.com">This is a link with a | inside the label</a></td>
|
||||
// <td>1</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// **Rule #10**
|
||||
//
|
||||
// It is possible to have a single row header only:
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example022()
|
||||
{
|
||||
// Example 22
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b
|
||||
// -- | --
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example023()
|
||||
{
|
||||
// Example 23
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// |a|b|c
|
||||
// |---|---|---|
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// <th>c</th>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// **Tests**
|
||||
//
|
||||
// Tests trailing spaces after pipes
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example024()
|
||||
{
|
||||
// Example 24
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// | abc | def |
|
||||
// |---|---|
|
||||
// | cde| ddd|
|
||||
// | eee| fff|
|
||||
// | fff | fffff |
|
||||
// |gggg | ffff |
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>abc</th>
|
||||
// <th>def</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>cde</td>
|
||||
// <td>ddd</td>
|
||||
// </tr>
|
||||
// <tr>
|
||||
// <td>eee</td>
|
||||
// <td>fff</td>
|
||||
// </tr>
|
||||
// <tr>
|
||||
// <td>fff</td>
|
||||
// <td>fffff</td>
|
||||
// </tr>
|
||||
// <tr>
|
||||
// <td>gggg</td>
|
||||
// <td>ffff</td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
|
||||
// **Normalized columns count**
|
||||
//
|
||||
// The tables are normalized to the number of columns found in the table header.
|
||||
// Extra columns will be ignored, missing columns will be inserted.
|
||||
[Test]
|
||||
public void ExtensionsGfmPipeTable_Example025()
|
||||
{
|
||||
// Example 25
|
||||
// Section: Extensions / Gfm Pipe Table
|
||||
//
|
||||
// The following Markdown:
|
||||
// a | b
|
||||
// -- | -
|
||||
// 0 | 1 | 2
|
||||
// 3 |
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <table>
|
||||
// <thead>
|
||||
// <tr>
|
||||
// <th>a</th>
|
||||
// <th>b</th>
|
||||
// </tr>
|
||||
// </thead>
|
||||
// <tbody>
|
||||
// <tr>
|
||||
// <td>0</td>
|
||||
// <td>1</td>
|
||||
// </tr>
|
||||
// <tr>
|
||||
// <td>3</td>
|
||||
// <td></td>
|
||||
// </tr>
|
||||
// </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");
|
||||
}
|
||||
}
|
||||
}
|
||||
617
src/Markdig.Tests/Specs/PipeTableGfmSpecs.md
Normal file
@@ -0,0 +1,617 @@
|
||||
# Extensions
|
||||
|
||||
This section describes the different extensions supported:
|
||||
|
||||
## Gfm Pipe Table
|
||||
|
||||
This groups a certain set of behaviors that makes pipe tables adhere more strictly to the GitHub-flavored Markdown specification.
|
||||
|
||||
A pipe table is detected when:
|
||||
|
||||
**Rule #1**
|
||||
- Each line of a paragraph block have to contain at least a **column delimiter** `|` that is not embedded by either a code inline (backtick \`) or a HTML inline.
|
||||
- The second row must separate the first header row from sub-sequent rows by containing a **header column separator** for each column separated by a column delimiter. A header column separator is:
|
||||
- starting by optional spaces
|
||||
- followed by an optional `:` to specify left align
|
||||
- followed by a sequence of at least one `-` character
|
||||
- followed by an optional `:` to specify right align (or center align if left align is also defined)
|
||||
- ending by optional spaces
|
||||
|
||||
Because a list has a higher precedence than a pipe table, a table header row separator requires at least 2 dashes `--` to start a header row:
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
-- | -
|
||||
0 | 1
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
The following is also considered as a table, even if the second line starts like a list:
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
- | -
|
||||
0 | 1
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
A pipe table with only one header row is allowed:
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
-- | --
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
After a row separator header, they will be interpreted as plain column:
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
-- | --
|
||||
-- | --
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>--</td>
|
||||
<td>--</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
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
|
||||
|
||||
```````````````````````````````` example
|
||||
a b
|
||||
c | d
|
||||
e | f
|
||||
.
|
||||
<p>a b
|
||||
c | d
|
||||
e | f</p>
|
||||
````````````````````````````````
|
||||
|
||||
If a line doesn't have a column delimiter `|` the table is not detected
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
c no d
|
||||
.
|
||||
<p>a | b
|
||||
c no d</p>
|
||||
````````````````````````````````
|
||||
|
||||
If a row contains more columns than the header row, the extra columns will be ignored:
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
-- | --
|
||||
0 | 1 | 2
|
||||
3 | 4
|
||||
5 |
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>4</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
**Rule #2**
|
||||
A pipe table ends after a blank line or the end of the file.
|
||||
|
||||
**Rule #3**
|
||||
A cell content is trimmed (start and end) from white-spaces.
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b |
|
||||
-- | --
|
||||
0 | 1 |
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
**Rule #4**
|
||||
Column delimiters `|` at the very beginning of a line or just before a line ending with only spaces and/or terminated by a newline can be omitted
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b |
|
||||
-- | --
|
||||
| 0 | 1
|
||||
| 2 | 3 |
|
||||
4 | 5
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>5</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
A pipe may be present at both the beginning/ending of each line:
|
||||
|
||||
```````````````````````````````` example
|
||||
|a|b|
|
||||
|-|-|
|
||||
|0|1|
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
Or may be omitted on one side:
|
||||
|
||||
```````````````````````````````` example
|
||||
a|b|
|
||||
-|-|
|
||||
0|1|
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
|a|b
|
||||
|-|-
|
||||
|0|1
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
|
||||
|
||||
Single column table can be declared with lines starting only by a column delimiter:
|
||||
|
||||
```````````````````````````````` example
|
||||
| a
|
||||
| --
|
||||
| b
|
||||
| c
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>b</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>c</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
**Rule #5**
|
||||
|
||||
The first row is considered as a **header row** if it is separated from the regular rows by a row containing a **header column separator** for each column. A header column separator is:
|
||||
|
||||
- starting by optional spaces
|
||||
- followed by an optional `:` to specify left align
|
||||
- followed by a sequence of at least one `-` character
|
||||
- followed by an optional `:` to specify right align (or center align if left align is also defined)
|
||||
- ending by optional spaces
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
-------|-------
|
||||
0 | 1
|
||||
2 | 3
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>3</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
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.
|
||||
There is no way to define a different alignment for heading and cells (apart from the default).
|
||||
The text alignment can be changed by using the character `:` with the header column separator:
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b | c
|
||||
:------|:-------:| ----:
|
||||
0 | 1 | 2
|
||||
3 | 4 | 5
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align: left;">a</th>
|
||||
<th style="text-align: center;">b</th>
|
||||
<th style="text-align: right;">c</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="text-align: left;">0</td>
|
||||
<td style="text-align: center;">1</td>
|
||||
<td style="text-align: right;">2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="text-align: left;">3</td>
|
||||
<td style="text-align: center;">4</td>
|
||||
<td style="text-align: right;">5</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
Test alignment with starting and ending pipes:
|
||||
|
||||
```````````````````````````````` example
|
||||
| abc | def | ghi |
|
||||
|:---:|-----|----:|
|
||||
| 1 | 2 | 3 |
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align: center;">abc</th>
|
||||
<th>def</th>
|
||||
<th style="text-align: right;">ghi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="text-align: center;">1</td>
|
||||
<td>2</td>
|
||||
<td style="text-align: right;">3</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
The following example shows a non matching header column separator:
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
-------|---x---
|
||||
0 | 1
|
||||
2 | 3
|
||||
.
|
||||
<p>a | b
|
||||
-------|---x---
|
||||
0 | 1
|
||||
2 | 3</p>
|
||||
````````````````````````````````
|
||||
|
||||
**Rule #6**
|
||||
|
||||
A column delimiter has a higher priority than emphasis delimiter
|
||||
|
||||
```````````````````````````````` example
|
||||
*a* | b
|
||||
----- |-----
|
||||
0 | _1_
|
||||
_2 | 3*
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th><em>a</em></th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td><em>1</em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>_2</td>
|
||||
<td>3*</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
**Rule #7**
|
||||
|
||||
A backtick/code delimiter has a higher precedence than a column delimiter `|`:
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b `
|
||||
0 | `
|
||||
.
|
||||
<p>a | b <code>0 |</code></p>
|
||||
````````````````````````````````
|
||||
|
||||
**Rule #8**
|
||||
|
||||
A HTML inline has a higher precedence than a column delimiter `|`:
|
||||
|
||||
```````````````````````````````` example
|
||||
a <a href="" title="|"></a> | b
|
||||
-- | --
|
||||
0 | 1
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a <a href="" title="|"></a></th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
**Rule #9**
|
||||
|
||||
Links have a higher precedence than the column delimiter character `|`:
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
-- | --
|
||||
[This is a link with a | inside the label](http://google.com) | 1
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><a href="http://google.com">This is a link with a | inside the label</a></td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
**Rule #10**
|
||||
|
||||
It is possible to have a single row header only:
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
-- | --
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
|a|b|c
|
||||
|---|---|---|
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
<th>c</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
**Tests**
|
||||
|
||||
Tests trailing spaces after pipes
|
||||
|
||||
```````````````````````````````` example
|
||||
| abc | def |
|
||||
|---|---|
|
||||
| cde| ddd|
|
||||
| eee| fff|
|
||||
| fff | fffff |
|
||||
|gggg | ffff |
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>abc</th>
|
||||
<th>def</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>cde</td>
|
||||
<td>ddd</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>eee</td>
|
||||
<td>fff</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>fff</td>
|
||||
<td>fffff</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>gggg</td>
|
||||
<td>ffff</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
**Normalized columns count**
|
||||
|
||||
The tables are normalized to the number of columns found in the table header.
|
||||
Extra columns will be ignored, missing columns will be inserted.
|
||||
|
||||
|
||||
```````````````````````````````` example
|
||||
a | b
|
||||
-- | -
|
||||
0 | 1 | 2
|
||||
3 |
|
||||
.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
<th>b</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>0</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
````````````````````````````````
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-15 05:54:35
|
||||
|
||||
// --------------------------------
|
||||
// Pipe Tables
|
||||
@@ -647,7 +646,7 @@ namespace Markdig.Tests.Specs.PipeTables
|
||||
TestParser.TestSpec("a | b `\n0 | ` ", "<p>a | b <code>0 |</code></p> ", "pipetables|advanced");
|
||||
}
|
||||
|
||||
// **Rule #7**
|
||||
// **Rule #8**
|
||||
//
|
||||
// A HTML inline has a higher precedence than a column delimiter `|`:
|
||||
[Test]
|
||||
@@ -681,7 +680,7 @@ namespace Markdig.Tests.Specs.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>", "pipetables|advanced");
|
||||
}
|
||||
|
||||
// **Rule #8**
|
||||
// **Rule #9**
|
||||
//
|
||||
// Links have a higher precedence than the column delimiter character `|`:
|
||||
[Test]
|
||||
@@ -715,7 +714,7 @@ namespace Markdig.Tests.Specs.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>", "pipetables|advanced");
|
||||
}
|
||||
|
||||
// ** Rule #9**
|
||||
// **Rule #10**
|
||||
//
|
||||
// It is possible to have a single row header only:
|
||||
[Test]
|
||||
@@ -767,7 +766,7 @@ namespace Markdig.Tests.Specs.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>", "pipetables|advanced");
|
||||
}
|
||||
|
||||
// ** Tests **
|
||||
// **Tests**
|
||||
//
|
||||
// Tests trailing spaces after pipes
|
||||
[Test]
|
||||
@@ -816,7 +815,7 @@ namespace Markdig.Tests.Specs.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>", "pipetables|advanced");
|
||||
}
|
||||
|
||||
// ** Normalized columns count **
|
||||
// **Normalized columns count**
|
||||
//
|
||||
// The tables are normalized to the maximum number of columns found in a table
|
||||
[Test]
|
||||
|
||||
@@ -463,7 +463,7 @@ a | b `
|
||||
<p>a | b <code>0 |</code></p>
|
||||
````````````````````````````````
|
||||
|
||||
**Rule #7**
|
||||
**Rule #8**
|
||||
|
||||
A HTML inline has a higher precedence than a column delimiter `|`:
|
||||
|
||||
@@ -488,7 +488,7 @@ a <a href="" title="|"></a> | b
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
**Rule #8**
|
||||
**Rule #9**
|
||||
|
||||
Links have a higher precedence than the column delimiter character `|`:
|
||||
|
||||
@@ -513,7 +513,7 @@ a | b
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
** Rule #9**
|
||||
**Rule #10**
|
||||
|
||||
It is possible to have a single row header only:
|
||||
|
||||
@@ -546,7 +546,7 @@ a | b
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
** Tests **
|
||||
**Tests**
|
||||
|
||||
Tests trailing spaces after pipes
|
||||
|
||||
@@ -586,7 +586,7 @@ Tests trailing spaces after pipes
|
||||
</table>
|
||||
````````````````````````````````
|
||||
|
||||
** Normalized columns count **
|
||||
**Normalized columns count**
|
||||
|
||||
The tables are normalized to the maximum number of columns found in a table
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Smarty Pants
|
||||
@@ -140,13 +139,13 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Section: Extensions / SmartyPants Quotes
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a 'text <<with' a another text>>
|
||||
// This is 'a "text 'with" a another text'
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a ‘text <<with’ a another text>></p>
|
||||
// <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");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -156,20 +155,36 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Section: Extensions / SmartyPants Quotes
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a 'text <<with' a another text>>
|
||||
//
|
||||
// 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");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtensionsSmartyPantsQuotes_Example010()
|
||||
{
|
||||
// Example 10
|
||||
// Section: Extensions / SmartyPants Quotes
|
||||
//
|
||||
// The following Markdown:
|
||||
// This is a <<text 'with>> a another text'
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>This is a «text 'with» a another text'</p>
|
||||
|
||||
Console.WriteLine("Example 9\nSection Extensions / SmartyPants Quotes\n");
|
||||
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");
|
||||
}
|
||||
|
||||
// Quotes requires to have the same rules than emphasis `_` regarding left/right frankling rules:
|
||||
[Test]
|
||||
public void ExtensionsSmartyPantsQuotes_Example010()
|
||||
public void ExtensionsSmartyPantsQuotes_Example011()
|
||||
{
|
||||
// Example 10
|
||||
// Example 11
|
||||
// Section: Extensions / SmartyPants Quotes
|
||||
//
|
||||
// The following Markdown:
|
||||
@@ -178,24 +193,8 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>It's not quotes'</p>
|
||||
|
||||
Console.WriteLine("Example 10\nSection Extensions / SmartyPants Quotes\n");
|
||||
TestParser.TestSpec("It's not quotes'", "<p>It's not quotes'</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtensionsSmartyPantsQuotes_Example011()
|
||||
{
|
||||
// Example 11
|
||||
// Section: Extensions / SmartyPants Quotes
|
||||
//
|
||||
// The following Markdown:
|
||||
// They are ' not matching quotes '
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>They are ' not matching quotes '</p>
|
||||
|
||||
Console.WriteLine("Example 11\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("It's not quotes'", "<p>It's not quotes'</p>", "pipetables+smartypants|advanced+smartypants");
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -205,20 +204,36 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Section: Extensions / SmartyPants Quotes
|
||||
//
|
||||
// The following Markdown:
|
||||
// They are ' not matching quotes '
|
||||
//
|
||||
// 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");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtensionsSmartyPantsQuotes_Example013()
|
||||
{
|
||||
// Example 13
|
||||
// Section: Extensions / SmartyPants Quotes
|
||||
//
|
||||
// The following Markdown:
|
||||
// They are' not matching 'quotes
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>They are' not matching 'quotes</p>
|
||||
|
||||
Console.WriteLine("Example 12\nSection Extensions / SmartyPants Quotes\n");
|
||||
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");
|
||||
}
|
||||
|
||||
// An emphasis starting inside left/right quotes will span over the right quote:
|
||||
[Test]
|
||||
public void ExtensionsSmartyPantsQuotes_Example013()
|
||||
public void ExtensionsSmartyPantsQuotes_Example014()
|
||||
{
|
||||
// Example 13
|
||||
// Example 14
|
||||
// Section: Extensions / SmartyPants Quotes
|
||||
//
|
||||
// The following Markdown:
|
||||
@@ -227,9 +242,26 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is “a <em>text” with an emphasis</em></p>
|
||||
|
||||
Console.WriteLine("Example 13\nSection Extensions / SmartyPants Quotes\n");
|
||||
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");
|
||||
}
|
||||
|
||||
// Multiple sets of quotes can be used
|
||||
[Test]
|
||||
public void ExtensionsSmartyPantsQuotes_Example015()
|
||||
{
|
||||
// Example 15
|
||||
// Section: Extensions / SmartyPants Quotes
|
||||
//
|
||||
// The following Markdown:
|
||||
// "aaa" "bbb" "ccc" "ddd"
|
||||
//
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
[TestFixture]
|
||||
@@ -237,9 +269,9 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
{
|
||||
// ## SmartyPants Separators
|
||||
[Test]
|
||||
public void ExtensionsSmartyPantsSeparators_Example014()
|
||||
public void ExtensionsSmartyPantsSeparators_Example016()
|
||||
{
|
||||
// Example 14
|
||||
// Example 16
|
||||
// Section: Extensions / SmartyPants Separators
|
||||
//
|
||||
// The following Markdown:
|
||||
@@ -248,14 +280,14 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a – text</p>
|
||||
|
||||
Console.WriteLine("Example 14\nSection Extensions / SmartyPants Separators\n");
|
||||
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");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtensionsSmartyPantsSeparators_Example015()
|
||||
public void ExtensionsSmartyPantsSeparators_Example017()
|
||||
{
|
||||
// Example 15
|
||||
// Example 17
|
||||
// Section: Extensions / SmartyPants Separators
|
||||
//
|
||||
// The following Markdown:
|
||||
@@ -264,14 +296,14 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a — text</p>
|
||||
|
||||
Console.WriteLine("Example 15\nSection Extensions / SmartyPants Separators\n");
|
||||
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");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExtensionsSmartyPantsSeparators_Example016()
|
||||
public void ExtensionsSmartyPantsSeparators_Example018()
|
||||
{
|
||||
// Example 16
|
||||
// Example 18
|
||||
// Section: Extensions / SmartyPants Separators
|
||||
//
|
||||
// The following Markdown:
|
||||
@@ -280,15 +312,15 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>This is a en ellipsis…</p>
|
||||
|
||||
Console.WriteLine("Example 16\nSection Extensions / SmartyPants Separators\n");
|
||||
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");
|
||||
}
|
||||
|
||||
// Check that a smartypants are not breaking pipetable parsing:
|
||||
[Test]
|
||||
public void ExtensionsSmartyPantsSeparators_Example017()
|
||||
public void ExtensionsSmartyPantsSeparators_Example019()
|
||||
{
|
||||
// Example 17
|
||||
// Example 19
|
||||
// Section: Extensions / SmartyPants Separators
|
||||
//
|
||||
// The following Markdown:
|
||||
@@ -312,15 +344,15 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// </tbody>
|
||||
// </table>
|
||||
|
||||
Console.WriteLine("Example 17\nSection Extensions / SmartyPants Separators\n");
|
||||
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");
|
||||
}
|
||||
|
||||
// Check quotes and dash:
|
||||
[Test]
|
||||
public void ExtensionsSmartyPantsSeparators_Example018()
|
||||
public void ExtensionsSmartyPantsSeparators_Example020()
|
||||
{
|
||||
// Example 18
|
||||
// Example 20
|
||||
// Section: Extensions / SmartyPants Separators
|
||||
//
|
||||
// The following Markdown:
|
||||
@@ -329,7 +361,7 @@ namespace Markdig.Tests.Specs.SmartyPants
|
||||
// Should be rendered as:
|
||||
// <p>A “quote” with a —</p>
|
||||
|
||||
Console.WriteLine("Example 18\nSection Extensions / SmartyPants Separators\n");
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,12 @@ This is a "text 'with" a another text'
|
||||
<p>This is a “text 'with” a another text'</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is 'a "text 'with" a another text'
|
||||
.
|
||||
<p>This is ‘a “text 'with” a another text’</p>
|
||||
````````````````````````````````
|
||||
|
||||
```````````````````````````````` example
|
||||
This is a 'text <<with' a another text>>
|
||||
.
|
||||
@@ -91,6 +97,14 @@ This is "a *text" with an emphasis*
|
||||
<p>This is “a <em>text” with an emphasis</em></p>
|
||||
````````````````````````````````
|
||||
|
||||
Multiple sets of quotes can be used
|
||||
|
||||
```````````````````````````````` example
|
||||
"aaa" "bbb" "ccc" "ddd"
|
||||
.
|
||||
<p>“aaa” “bbb” “ccc” “ddd”</p>
|
||||
````````````````````````````````
|
||||
|
||||
## SmartyPants Separators
|
||||
|
||||
```````````````````````````````` example
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Task Lists
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Generated: 2019-04-05 16:06:14
|
||||
|
||||
// --------------------------------
|
||||
// Yaml
|
||||
|
||||
92
src/Markdig.Tests/TestContainerBlocks.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class TestContainerBlocks
|
||||
{
|
||||
private class MockContainerBlock : ContainerBlock
|
||||
{
|
||||
public MockContainerBlock()
|
||||
: base(null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeCleared()
|
||||
{
|
||||
ContainerBlock container = new MockContainerBlock();
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.Null(container.LastChild);
|
||||
|
||||
var paragraph = new ParagraphBlock();
|
||||
Assert.Null(paragraph.Parent);
|
||||
|
||||
container.Add(paragraph);
|
||||
|
||||
Assert.AreEqual(1, container.Count);
|
||||
Assert.AreSame(container, paragraph.Parent);
|
||||
Assert.AreSame(paragraph, container.LastChild);
|
||||
|
||||
container.Clear();
|
||||
|
||||
Assert.AreEqual(0, container.Count);
|
||||
Assert.Null(container.LastChild);
|
||||
Assert.Null(paragraph.Parent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeInsertedInto()
|
||||
{
|
||||
ContainerBlock container = new MockContainerBlock();
|
||||
|
||||
var one = new ParagraphBlock();
|
||||
container.Insert(0, one);
|
||||
Assert.AreEqual(1, container.Count);
|
||||
Assert.AreSame(container[0], one);
|
||||
Assert.AreSame(container, one.Parent);
|
||||
|
||||
var two = new ParagraphBlock();
|
||||
container.Insert(1, two);
|
||||
Assert.AreEqual(2, container.Count);
|
||||
Assert.AreSame(container[0], one);
|
||||
Assert.AreSame(container[1], two);
|
||||
Assert.AreSame(container, two.Parent);
|
||||
|
||||
var three = new ParagraphBlock();
|
||||
container.Insert(0, three);
|
||||
Assert.AreEqual(3, container.Count);
|
||||
Assert.AreSame(container[0], three);
|
||||
Assert.AreSame(container[1], one);
|
||||
Assert.AreSame(container[2], two);
|
||||
Assert.AreSame(container, three.Parent);
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => container.Insert(0, null));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => container.Insert(4, new ParagraphBlock()));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => container.Insert(-1, new ParagraphBlock()));
|
||||
Assert.Throws<ArgumentException>(() => container.Insert(0, one)); // one already has a parent
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeSet()
|
||||
{
|
||||
ContainerBlock container = new MockContainerBlock();
|
||||
|
||||
var one = new ParagraphBlock();
|
||||
container.Insert(0, one);
|
||||
Assert.AreEqual(1, container.Count);
|
||||
Assert.AreSame(container[0], one);
|
||||
Assert.AreSame(container, one.Parent);
|
||||
|
||||
var two = new ParagraphBlock();
|
||||
container[0] = two;
|
||||
Assert.AreSame(container, two.Parent);
|
||||
Assert.Null(one.Parent);
|
||||
|
||||
Assert.Throws<ArgumentException>(() => container[0] = two); // two already has a parent
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/Markdig.Tests/TestContainerInlines.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class TestContainerInlines
|
||||
{
|
||||
private class MockLeafBlock : LeafBlock
|
||||
{
|
||||
public MockLeafBlock()
|
||||
: base(null)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CanBeAddedToLeafBlock()
|
||||
{
|
||||
var leafBlock1 = new MockLeafBlock();
|
||||
|
||||
var one = new ContainerInline();
|
||||
Assert.Null(one.ParentBlock);
|
||||
|
||||
leafBlock1.Inline = one;
|
||||
Assert.AreSame(leafBlock1, one.ParentBlock);
|
||||
|
||||
var two = new ContainerInline();
|
||||
Assert.Null(two.ParentBlock);
|
||||
|
||||
leafBlock1.Inline = two;
|
||||
Assert.AreSame(leafBlock1, two.ParentBlock);
|
||||
Assert.Null(one.ParentBlock);
|
||||
|
||||
var leafBlock2 = new MockLeafBlock();
|
||||
Assert.Throws<ArgumentException>(() => leafBlock2.Inline = two);
|
||||
}
|
||||
}
|
||||
}
|
||||
128
src/Markdig.Tests/TestCustomEmojis.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Markdig.Extensions.Emoji;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestCustomEmojis
|
||||
{
|
||||
[Test]
|
||||
[TestCase(":smiley:", "<p>♥</p>\n")]
|
||||
[TestCase(":confused:", "<p>:confused:</p>\n")] // default emoji does not work
|
||||
[TestCase(":/", "<p>:/</p>\n")] // default smiley does not work
|
||||
public void TestCustomEmoji(string input, string expected)
|
||||
{
|
||||
var emojiToUnicode = new Dictionary<string, string>();
|
||||
var smileyToEmoji = new Dictionary<string, string>();
|
||||
|
||||
emojiToUnicode[":smiley:"] = "♥";
|
||||
|
||||
var customMapping = new EmojiMapping(emojiToUnicode, smileyToEmoji);
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmojiAndSmiley(customEmojiMapping: customMapping)
|
||||
.Build();
|
||||
|
||||
var actual = Markdown.ToHtml(input, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(":testheart:", "<p>♥</p>\n")]
|
||||
[TestCase("hello", "<p>♥</p>\n")]
|
||||
[TestCase(":confused:", "<p>:confused:</p>\n")] // default emoji does not work
|
||||
[TestCase(":/", "<p>:/</p>\n")] // default smiley does not work
|
||||
public void TestCustomSmiley(string input, string expected)
|
||||
{
|
||||
var emojiToUnicode = new Dictionary<string, string>();
|
||||
var smileyToEmoji = new Dictionary<string, string>();
|
||||
|
||||
emojiToUnicode[":testheart:"] = "♥";
|
||||
smileyToEmoji["hello"] = ":testheart:";
|
||||
|
||||
var customMapping = new EmojiMapping(emojiToUnicode, smileyToEmoji);
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmojiAndSmiley(customEmojiMapping: customMapping)
|
||||
.Build();
|
||||
|
||||
var actual = Markdown.ToHtml(input, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(":smiley:", "<p>♥</p>\n")]
|
||||
[TestCase(":)", "<p>♥</p>\n")]
|
||||
[TestCase(":confused:", "<p>😕</p>\n")] // default emoji still works
|
||||
[TestCase(":/", "<p>😕</p>\n")] // default smiley still works
|
||||
public void TestOverrideDefaultWithCustomEmoji(string input, string expected)
|
||||
{
|
||||
var emojiToUnicode = EmojiMapping.GetDefaultEmojiShortcodeToUnicode();
|
||||
var smileyToEmoji = EmojiMapping.GetDefaultSmileyToEmojiShortcode();
|
||||
|
||||
emojiToUnicode[":smiley:"] = "♥";
|
||||
|
||||
var customMapping = new EmojiMapping(emojiToUnicode, smileyToEmoji);
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmojiAndSmiley(customEmojiMapping: customMapping)
|
||||
.Build();
|
||||
|
||||
var actual = Markdown.ToHtml(input, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(":testheart:", "<p>♥</p>\n")]
|
||||
[TestCase("hello", "<p>♥</p>\n")]
|
||||
[TestCase(":confused:", "<p>😕</p>\n")] // default emoji still works
|
||||
[TestCase(":/", "<p>😕</p>\n")] // default smiley still works
|
||||
public void TestOverrideDefaultWithCustomSmiley(string input, string expected)
|
||||
{
|
||||
var emojiToUnicode = EmojiMapping.GetDefaultEmojiShortcodeToUnicode();
|
||||
var smileyToEmoji = EmojiMapping.GetDefaultSmileyToEmojiShortcode();
|
||||
|
||||
emojiToUnicode[":testheart:"] = "♥";
|
||||
smileyToEmoji["hello"] = ":testheart:";
|
||||
|
||||
var customMapping = new EmojiMapping(emojiToUnicode, smileyToEmoji);
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseEmojiAndSmiley(customEmojiMapping: customMapping)
|
||||
.Build();
|
||||
|
||||
var actual = Markdown.ToHtml(input, pipeline);
|
||||
Assert.AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCustomEmojiValidation()
|
||||
{
|
||||
var emojiToUnicode = new Dictionary<string, string>();
|
||||
var smileyToEmoji = new Dictionary<string, string>();
|
||||
|
||||
Assert.Throws<ArgumentNullException>(() => new EmojiMapping(null, smileyToEmoji));
|
||||
Assert.Throws<ArgumentNullException>(() => new EmojiMapping(emojiToUnicode, null));
|
||||
|
||||
emojiToUnicode.Add("null-value", null);
|
||||
Assert.Throws<ArgumentException>(() => new EmojiMapping(emojiToUnicode, smileyToEmoji));
|
||||
emojiToUnicode.Clear();
|
||||
|
||||
smileyToEmoji.Add("null-value", null);
|
||||
Assert.Throws<ArgumentException>(() => new EmojiMapping(emojiToUnicode, smileyToEmoji));
|
||||
smileyToEmoji.Clear();
|
||||
|
||||
smileyToEmoji.Add("foo", "something-that-does-not-exist-in-emojiToUnicode");
|
||||
Assert.Throws<ArgumentException>(() => new EmojiMapping(emojiToUnicode, smileyToEmoji));
|
||||
smileyToEmoji.Clear();
|
||||
|
||||
emojiToUnicode.Add("a", "aaa");
|
||||
emojiToUnicode.Add("b", "bbb");
|
||||
emojiToUnicode.Add("c", "ccc");
|
||||
smileyToEmoji.Add("a", "c"); // "a" already exists in emojiToUnicode
|
||||
Assert.Throws<ArgumentException>(() => new EmojiMapping(emojiToUnicode, smileyToEmoji));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +1,152 @@
|
||||
using NUnit.Framework;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestDescendantsOrder
|
||||
{
|
||||
[Test]
|
||||
public void TestSchemas()
|
||||
{
|
||||
foreach (var markdown in TestParser.SpecsMarkdown)
|
||||
{
|
||||
AssertSameDescendantsOrder(markdown);
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertSameDescendantsOrder(string markdown)
|
||||
{
|
||||
var syntaxTree = Markdown.Parse(markdown, new MarkdownPipelineBuilder().UseAdvancedExtensions().Build());
|
||||
|
||||
var descendants_legacy = Descendants_Legacy(syntaxTree).ToList();
|
||||
var descendants_new = syntaxTree.Descendants().ToList();
|
||||
|
||||
Assert.AreEqual(descendants_legacy.Count, descendants_new.Count);
|
||||
|
||||
for (int i = 0; i < descendants_legacy.Count; i++)
|
||||
{
|
||||
Assert.AreSame(descendants_legacy[i], descendants_new[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<MarkdownObject> Descendants_Legacy(MarkdownObject markdownObject)
|
||||
{
|
||||
// TODO: implement a recursiveless method
|
||||
|
||||
var block = markdownObject as ContainerBlock;
|
||||
if (block != null)
|
||||
{
|
||||
foreach (var subBlock in block)
|
||||
{
|
||||
yield return subBlock;
|
||||
|
||||
foreach (var sub in Descendants_Legacy(subBlock))
|
||||
{
|
||||
yield return sub;
|
||||
}
|
||||
|
||||
// Visit leaf block that have inlines
|
||||
var leafBlock = subBlock as LeafBlock;
|
||||
if (leafBlock?.Inline != null)
|
||||
{
|
||||
foreach (var subInline in Descendants_Legacy(leafBlock.Inline))
|
||||
{
|
||||
yield return subInline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var inline = markdownObject as ContainerInline;
|
||||
if (inline != null)
|
||||
{
|
||||
var child = inline.FirstChild;
|
||||
while (child != null)
|
||||
{
|
||||
var next = child.NextSibling;
|
||||
yield return child;
|
||||
|
||||
foreach (var sub in Descendants_Legacy(child))
|
||||
{
|
||||
yield return sub;
|
||||
}
|
||||
|
||||
child = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using NUnit.Framework;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestDescendantsOrder
|
||||
{
|
||||
public static void TestSchemas(MarkdownDocument[] specsSyntaxTrees)
|
||||
{
|
||||
foreach (var syntaxTree in specsSyntaxTrees)
|
||||
{
|
||||
AssertIEnumerablesAreEqual(
|
||||
Descendants_Legacy(syntaxTree),
|
||||
syntaxTree.Descendants());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
syntaxTree.Descendants().OfType<ParagraphBlock>(),
|
||||
syntaxTree.Descendants<ParagraphBlock>());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
syntaxTree.Descendants().OfType<ParagraphBlock>(),
|
||||
(syntaxTree as ContainerBlock).Descendants<ParagraphBlock>());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
syntaxTree.Descendants().OfType<LiteralInline>(),
|
||||
syntaxTree.Descendants<LiteralInline>());
|
||||
|
||||
foreach (LiteralInline literalInline in syntaxTree.Descendants<LiteralInline>())
|
||||
{
|
||||
Assert.AreSame(ArrayHelper.Empty<ListBlock>(), literalInline.Descendants<ListBlock>());
|
||||
Assert.AreSame(ArrayHelper.Empty<ParagraphBlock>(), literalInline.Descendants<ParagraphBlock>());
|
||||
Assert.AreSame(ArrayHelper.Empty<ContainerInline>(), literalInline.Descendants<ContainerInline>());
|
||||
}
|
||||
|
||||
foreach (ContainerInline containerInline in syntaxTree.Descendants<ContainerInline>())
|
||||
{
|
||||
AssertIEnumerablesAreEqual(
|
||||
containerInline.FindDescendants<LiteralInline>(),
|
||||
containerInline.Descendants<LiteralInline>());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
containerInline.FindDescendants<LiteralInline>(),
|
||||
(containerInline as MarkdownObject).Descendants<LiteralInline>());
|
||||
|
||||
if (containerInline.FirstChild is null)
|
||||
{
|
||||
Assert.AreSame(ArrayHelper.Empty<LiteralInline>(), containerInline.Descendants<LiteralInline>());
|
||||
Assert.AreSame(ArrayHelper.Empty<LiteralInline>(), containerInline.FindDescendants<LiteralInline>());
|
||||
Assert.AreSame(ArrayHelper.Empty<LiteralInline>(), (containerInline as MarkdownObject).Descendants<LiteralInline>());
|
||||
}
|
||||
|
||||
Assert.AreSame(ArrayHelper.Empty<ListBlock>(), containerInline.Descendants<ListBlock>());
|
||||
Assert.AreSame(ArrayHelper.Empty<ParagraphBlock>(), containerInline.Descendants<ParagraphBlock>());
|
||||
}
|
||||
|
||||
foreach (ParagraphBlock paragraphBlock in syntaxTree.Descendants<ParagraphBlock>())
|
||||
{
|
||||
AssertIEnumerablesAreEqual(
|
||||
(paragraphBlock as MarkdownObject).Descendants<LiteralInline>(),
|
||||
paragraphBlock.Descendants<LiteralInline>());
|
||||
|
||||
Assert.AreSame(ArrayHelper.Empty<ParagraphBlock>(), paragraphBlock.Descendants<ParagraphBlock>());
|
||||
}
|
||||
|
||||
foreach (ContainerBlock containerBlock in syntaxTree.Descendants<ContainerBlock>())
|
||||
{
|
||||
AssertIEnumerablesAreEqual(
|
||||
containerBlock.Descendants<LiteralInline>(),
|
||||
(containerBlock as MarkdownObject).Descendants<LiteralInline>());
|
||||
|
||||
AssertIEnumerablesAreEqual(
|
||||
containerBlock.Descendants<ParagraphBlock>(),
|
||||
(containerBlock as MarkdownObject).Descendants<ParagraphBlock>());
|
||||
|
||||
if (containerBlock.Count == 0)
|
||||
{
|
||||
Assert.AreSame(ArrayHelper.Empty<LiteralInline>(), containerBlock.Descendants<LiteralInline>());
|
||||
Assert.AreSame(ArrayHelper.Empty<LiteralInline>(), (containerBlock as Block).Descendants<LiteralInline>());
|
||||
Assert.AreSame(ArrayHelper.Empty<LiteralInline>(), (containerBlock as MarkdownObject).Descendants<LiteralInline>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertIEnumerablesAreEqual<T>(IEnumerable<T> first, IEnumerable<T> second)
|
||||
{
|
||||
var firstList = new List<T>(first);
|
||||
var secondList = new List<T>(second);
|
||||
|
||||
Assert.AreEqual(firstList.Count, secondList.Count);
|
||||
|
||||
for (int i = 0; i < firstList.Count; i++)
|
||||
{
|
||||
Assert.AreSame(firstList[i], secondList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<MarkdownObject> Descendants_Legacy(MarkdownObject markdownObject)
|
||||
{
|
||||
// TODO: implement a recursiveless method
|
||||
|
||||
var block = markdownObject as ContainerBlock;
|
||||
if (block != null)
|
||||
{
|
||||
foreach (var subBlock in block)
|
||||
{
|
||||
yield return subBlock;
|
||||
|
||||
foreach (var sub in Descendants_Legacy(subBlock))
|
||||
{
|
||||
yield return sub;
|
||||
}
|
||||
|
||||
// Visit leaf block that have inlines
|
||||
var leafBlock = subBlock as LeafBlock;
|
||||
if (leafBlock?.Inline != null)
|
||||
{
|
||||
foreach (var subInline in Descendants_Legacy(leafBlock.Inline))
|
||||
{
|
||||
yield return subInline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var inline = markdownObject as ContainerInline;
|
||||
if (inline != null)
|
||||
{
|
||||
var child = inline.FirstChild;
|
||||
while (child != null)
|
||||
{
|
||||
var next = child.NextSibling;
|
||||
yield return child;
|
||||
|
||||
foreach (var sub in Descendants_Legacy(child))
|
||||
{
|
||||
yield return sub;
|
||||
}
|
||||
|
||||
child = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,168 +1,169 @@
|
||||
using Markdig.Parsers.Inlines;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestEmphasisExtended
|
||||
{
|
||||
class EmphasisTestExtension : IMarkdownExtension
|
||||
{
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
var emphasisParser = pipeline.InlineParsers.Find<EmphasisInlineParser>();
|
||||
Debug.Assert(emphasisParser != null);
|
||||
|
||||
foreach (var emphasis in EmphasisTestDescriptors)
|
||||
{
|
||||
emphasisParser.EmphasisDescriptors.Add(
|
||||
new EmphasisDescriptor(emphasis.Character, emphasis.Minimum, emphasis.Maximum, true));
|
||||
}
|
||||
emphasisParser.TryCreateEmphasisInlineList.Add((delimiterChar, delimiterCount) =>
|
||||
{
|
||||
return delimiterChar == '*' || delimiterChar == '_'
|
||||
? null
|
||||
: new CustomEmphasisInline() { DelimiterChar = delimiterChar, DelimiterCount = delimiterCount };
|
||||
});
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
renderer.ObjectRenderers.Insert(0, new EmphasisRenderer());
|
||||
}
|
||||
|
||||
class EmphasisRenderer : HtmlObjectRenderer<CustomEmphasisInline>
|
||||
{
|
||||
protected override void Write(HtmlRenderer renderer, CustomEmphasisInline obj)
|
||||
{
|
||||
var tag = EmphasisTestDescriptors.First(test => test.Character == obj.DelimiterChar).Tags[obj.DelimiterCount];
|
||||
|
||||
renderer.Write(tag.OpeningTag);
|
||||
renderer.WriteChildren(obj);
|
||||
renderer.Write(tag.ClosingTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
class Tag
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public int Level;
|
||||
#pragma warning restore CS0649
|
||||
public string RawTag;
|
||||
public string OpeningTag;
|
||||
public string ClosingTag;
|
||||
|
||||
public Tag(string tag)
|
||||
{
|
||||
RawTag = tag;
|
||||
OpeningTag = "<" + tag + ">";
|
||||
ClosingTag = "</" + tag + ">";
|
||||
}
|
||||
|
||||
public static implicit operator Tag(string tag)
|
||||
=> new Tag(tag);
|
||||
}
|
||||
class EmphasisTestDescriptor
|
||||
{
|
||||
public char Character;
|
||||
public int Minimum;
|
||||
public int Maximum;
|
||||
public Dictionary<int, Tag> Tags = new Dictionary<int, Tag>();
|
||||
|
||||
private EmphasisTestDescriptor(char character, int min, int max)
|
||||
{
|
||||
Character = character;
|
||||
Minimum = min;
|
||||
Maximum = max;
|
||||
}
|
||||
public EmphasisTestDescriptor(char character, int min, int max, params Tag[] tags)
|
||||
: this(character, min, max)
|
||||
{
|
||||
Debug.Assert(tags.Length == max - min + 1);
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
Tags.Add(min++, tag);
|
||||
}
|
||||
}
|
||||
public EmphasisTestDescriptor(char character, int min, int max, string tag)
|
||||
: this(character, min, max, new Tag(tag)) { }
|
||||
}
|
||||
class CustomEmphasisInline : EmphasisInline { }
|
||||
static readonly EmphasisTestDescriptor[] EmphasisTestDescriptors = new[]
|
||||
{
|
||||
// Min Max
|
||||
new EmphasisTestDescriptor('"', 1, 1, "quotation"),
|
||||
new EmphasisTestDescriptor(',', 1, 2, "comma", "extra-comma"),
|
||||
new EmphasisTestDescriptor('!', 2, 3, "warning", "error"),
|
||||
new EmphasisTestDescriptor('=', 1, 3, "equal", "really-equal", "congruent"),
|
||||
new EmphasisTestDescriptor('1', 1, 1, "one-only"),
|
||||
new EmphasisTestDescriptor('2', 2, 2, "two-only"),
|
||||
new EmphasisTestDescriptor('3', 3, 3, "three-only"),
|
||||
};
|
||||
|
||||
static readonly MarkdownPipeline Pipeline = new MarkdownPipelineBuilder().Use<EmphasisTestExtension>().Build();
|
||||
|
||||
[Test]
|
||||
[TestCase("*foo**", "<em>foo</em>*")]
|
||||
[TestCase("**foo*", "*<em>foo</em>")]
|
||||
[TestCase("***foo***", "<em><strong>foo</strong></em>")]
|
||||
[TestCase("**_foo_**", "<strong><em>foo</em></strong>")]
|
||||
[TestCase("_**foo**_", "<em><strong>foo</strong></em>")]
|
||||
[TestCase("\"foo\"", "<quotation>foo</quotation>")]
|
||||
[TestCase("\"\"foo\"\"", "<quotation><quotation>foo</quotation></quotation>")]
|
||||
[TestCase("\"foo\"\"", "<quotation>foo</quotation>"")]
|
||||
[TestCase("\"\"foo\"", ""<quotation>foo</quotation>")]
|
||||
[TestCase(", foo", ", foo")]
|
||||
[TestCase(", foo,", ", foo,")]
|
||||
[TestCase(",some, foo,", "<comma>some</comma> foo,")]
|
||||
[TestCase(",,foo,,", "<extra-comma>foo</extra-comma>")]
|
||||
[TestCase(",foo,,", "<comma>foo</comma>,")]
|
||||
[TestCase(",,,foo,,,", "<comma><extra-comma>foo</extra-comma></comma>")]
|
||||
[TestCase("!1!", "!1!")]
|
||||
[TestCase("!!2!!", "<warning>2</warning>")]
|
||||
[TestCase("!!!3!!!", "<error>3</error>")]
|
||||
[TestCase("!!!34!!!!", "<error>34</error>!")]
|
||||
[TestCase("!!!!43!!!", "!<error>43</error>")]
|
||||
[TestCase("!!!!44!!!!", "!<error>44!</error>")] // This is a new case - should the second ! be before or after </error>?
|
||||
[TestCase("!!!!!5!!!!!", "<warning><error>5</error></warning>")]
|
||||
[TestCase("!!!!!!6!!!!!!", "<error><error>6</error></error>")]
|
||||
[TestCase("!! !mixed!!!", "!! !mixed!!!")] // can't open the delimiter because of the whitespace
|
||||
[TestCase("=", "=")]
|
||||
[TestCase("==", "==")]
|
||||
[TestCase("====", "====")]
|
||||
[TestCase("=a", "=a")]
|
||||
[TestCase("=a=", "<equal>a</equal>")]
|
||||
[TestCase("==a=", "=<equal>a</equal>")]
|
||||
[TestCase("==a==", "<really-equal>a</really-equal>")]
|
||||
[TestCase("==a===", "<really-equal>a</really-equal>=")]
|
||||
[TestCase("===a===", "<congruent>a</congruent>")]
|
||||
[TestCase("====a====", "<equal><congruent>a</congruent></equal>")]
|
||||
[TestCase("=====a=====", "<really-equal><congruent>a</congruent></really-equal>")]
|
||||
[TestCase("1", "1")]
|
||||
[TestCase("1 1", "1 1")]
|
||||
[TestCase("1Foo1", "<one-only>Foo</one-only>")]
|
||||
[TestCase("1121", "1<one-only>2</one-only>")]
|
||||
[TestCase("22322", "<two-only>3</two-only>")]
|
||||
[TestCase("2232", "2232")]
|
||||
[TestCase("333", "333")]
|
||||
[TestCase("3334333", "<three-only>4</three-only>")]
|
||||
[TestCase("33334333", "3<three-only>4</three-only>")]
|
||||
[TestCase("33343333", "<three-only>4</three-only>3")]
|
||||
[TestCase("122122", "<one-only>22</one-only>22")]
|
||||
[TestCase("221221", "<two-only>1</two-only>1")]
|
||||
[TestCase("122foo221", "<one-only><two-only>foo</two-only></one-only>")]
|
||||
[TestCase("122foo122", "<one-only>22foo</one-only>22")]
|
||||
[TestCase("!!!!!Attention:!! \"==1+1== 2\",but ===333 and 222===, mod 111!!!",
|
||||
"<error><warning>Attention:</warning> <quotation><really-equal><one-only>+</one-only></really-equal> 2</quotation><comma>but <congruent>333 and 222</congruent></comma> mod 111</error>")]
|
||||
public void TestEmphasis(string markdown, string expectedHtml)
|
||||
{
|
||||
TestParser.TestSpec(markdown, "<p>" + expectedHtml + "</p>", Pipeline);
|
||||
}
|
||||
}
|
||||
}
|
||||
using Markdig.Parsers.Inlines;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestEmphasisExtended
|
||||
{
|
||||
class EmphasisTestExtension : IMarkdownExtension
|
||||
{
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
var emphasisParser = pipeline.InlineParsers.Find<EmphasisInlineParser>();
|
||||
Debug.Assert(emphasisParser != null);
|
||||
|
||||
foreach (var emphasis in EmphasisTestDescriptors)
|
||||
{
|
||||
emphasisParser.EmphasisDescriptors.Add(
|
||||
new EmphasisDescriptor(emphasis.Character, emphasis.Minimum, emphasis.Maximum, true));
|
||||
}
|
||||
emphasisParser.TryCreateEmphasisInlineList.Add((delimiterChar, delimiterCount) =>
|
||||
{
|
||||
return delimiterChar == '*' || delimiterChar == '_'
|
||||
? null
|
||||
: new CustomEmphasisInline() { DelimiterChar = delimiterChar, DelimiterCount = delimiterCount };
|
||||
});
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
renderer.ObjectRenderers.Insert(0, new EmphasisRenderer());
|
||||
}
|
||||
|
||||
class EmphasisRenderer : HtmlObjectRenderer<CustomEmphasisInline>
|
||||
{
|
||||
protected override void Write(HtmlRenderer renderer, CustomEmphasisInline obj)
|
||||
{
|
||||
var tag = EmphasisTestDescriptors.First(test => test.Character == obj.DelimiterChar).Tags[obj.DelimiterCount];
|
||||
|
||||
renderer.Write(tag.OpeningTag);
|
||||
renderer.WriteChildren(obj);
|
||||
renderer.Write(tag.ClosingTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
class Tag
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
public int Level;
|
||||
#pragma warning restore CS0649
|
||||
public string RawTag;
|
||||
public string OpeningTag;
|
||||
public string ClosingTag;
|
||||
|
||||
public Tag(string tag)
|
||||
{
|
||||
RawTag = tag;
|
||||
OpeningTag = "<" + tag + ">";
|
||||
ClosingTag = "</" + tag + ">";
|
||||
}
|
||||
|
||||
public static implicit operator Tag(string tag)
|
||||
=> new Tag(tag);
|
||||
}
|
||||
class EmphasisTestDescriptor
|
||||
{
|
||||
public char Character;
|
||||
public int Minimum;
|
||||
public int Maximum;
|
||||
public Dictionary<int, Tag> Tags = new Dictionary<int, Tag>();
|
||||
|
||||
private EmphasisTestDescriptor(char character, int min, int max)
|
||||
{
|
||||
Character = character;
|
||||
Minimum = min;
|
||||
Maximum = max;
|
||||
}
|
||||
public EmphasisTestDescriptor(char character, int min, int max, params Tag[] tags)
|
||||
: this(character, min, max)
|
||||
{
|
||||
Debug.Assert(tags.Length == max - min + 1);
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
Tags.Add(min++, tag);
|
||||
}
|
||||
}
|
||||
public EmphasisTestDescriptor(char character, int min, int max, string tag)
|
||||
: this(character, min, max, new Tag(tag)) { }
|
||||
}
|
||||
class CustomEmphasisInline : EmphasisInline { }
|
||||
static readonly EmphasisTestDescriptor[] EmphasisTestDescriptors = new[]
|
||||
{
|
||||
// Min Max
|
||||
new EmphasisTestDescriptor('"', 1, 1, "quotation"),
|
||||
new EmphasisTestDescriptor(',', 1, 2, "comma", "extra-comma"),
|
||||
new EmphasisTestDescriptor('!', 2, 3, "warning", "error"),
|
||||
new EmphasisTestDescriptor('=', 1, 3, "equal", "really-equal", "congruent"),
|
||||
new EmphasisTestDescriptor('1', 1, 1, "one-only"),
|
||||
new EmphasisTestDescriptor('2', 2, 2, "two-only"),
|
||||
new EmphasisTestDescriptor('3', 3, 3, "three-only"),
|
||||
};
|
||||
|
||||
static readonly MarkdownPipeline Pipeline = new MarkdownPipelineBuilder().Use<EmphasisTestExtension>().Build();
|
||||
|
||||
[Test]
|
||||
[TestCase("*foo**", "<em>foo</em>*")]
|
||||
[TestCase("**foo*", "*<em>foo</em>")]
|
||||
[TestCase("***foo***", "<em><strong>foo</strong></em>")]
|
||||
[TestCase("**_foo_**", "<strong><em>foo</em></strong>")]
|
||||
[TestCase("_**foo**_", "<em><strong>foo</strong></em>")]
|
||||
[TestCase("\"foo\"", "<quotation>foo</quotation>")]
|
||||
[TestCase("\"\"foo\"\"", "<quotation><quotation>foo</quotation></quotation>")]
|
||||
[TestCase("\"foo\"\"", "<quotation>foo</quotation>"")]
|
||||
[TestCase("\"\"foo\"", ""<quotation>foo</quotation>")]
|
||||
[TestCase(", foo", ", foo")]
|
||||
[TestCase(", foo,", ", foo,")]
|
||||
[TestCase(",some, foo,", "<comma>some</comma> foo,")]
|
||||
[TestCase(",,foo,,", "<extra-comma>foo</extra-comma>")]
|
||||
[TestCase(",foo,,", "<comma>foo</comma>,")]
|
||||
[TestCase(",,,foo,,,", "<comma><extra-comma>foo</extra-comma></comma>")]
|
||||
[TestCase("*foo*&_foo_", "<em>foo</em>&<em>foo</em>")]
|
||||
[TestCase("!1!", "!1!")]
|
||||
[TestCase("!!2!!", "<warning>2</warning>")]
|
||||
[TestCase("!!!3!!!", "<error>3</error>")]
|
||||
[TestCase("!!!34!!!!", "<error>34</error>!")]
|
||||
[TestCase("!!!!43!!!", "!<error>43</error>")]
|
||||
[TestCase("!!!!44!!!!", "!<error>44!</error>")] // This is a new case - should the second ! be before or after </error>?
|
||||
[TestCase("!!!!!5!!!!!", "<warning><error>5</error></warning>")]
|
||||
[TestCase("!!!!!!6!!!!!!", "<error><error>6</error></error>")]
|
||||
[TestCase("!! !mixed!!!", "!! !mixed!!!")] // can't open the delimiter because of the whitespace
|
||||
[TestCase("=", "=")]
|
||||
[TestCase("==", "==")]
|
||||
[TestCase("====", "====")]
|
||||
[TestCase("=a", "=a")]
|
||||
[TestCase("=a=", "<equal>a</equal>")]
|
||||
[TestCase("==a=", "=<equal>a</equal>")]
|
||||
[TestCase("==a==", "<really-equal>a</really-equal>")]
|
||||
[TestCase("==a===", "<really-equal>a</really-equal>=")]
|
||||
[TestCase("===a===", "<congruent>a</congruent>")]
|
||||
[TestCase("====a====", "<equal><congruent>a</congruent></equal>")]
|
||||
[TestCase("=====a=====", "<really-equal><congruent>a</congruent></really-equal>")]
|
||||
[TestCase("1", "1")]
|
||||
[TestCase("1 1", "1 1")]
|
||||
[TestCase("1Foo1", "<one-only>Foo</one-only>")]
|
||||
[TestCase("1121", "1<one-only>2</one-only>")]
|
||||
[TestCase("22322", "<two-only>3</two-only>")]
|
||||
[TestCase("2232", "2232")]
|
||||
[TestCase("333", "333")]
|
||||
[TestCase("3334333", "<three-only>4</three-only>")]
|
||||
[TestCase("33334333", "3<three-only>4</three-only>")]
|
||||
[TestCase("33343333", "<three-only>4</three-only>3")]
|
||||
[TestCase("122122", "<one-only>22</one-only>22")]
|
||||
[TestCase("221221", "<two-only>1</two-only>1")]
|
||||
[TestCase("122foo221", "<one-only><two-only>foo</two-only></one-only>")]
|
||||
[TestCase("122foo122", "<one-only>22foo</one-only>22")]
|
||||
[TestCase("!!!!!Attention:!! \"==1+1== 2\",but ===333 and 222===, mod 111!!!",
|
||||
"<error><warning>Attention:</warning> <quotation><really-equal><one-only>+</one-only></really-equal> 2</quotation><comma>but <congruent>333 and 222</congruent></comma> mod 111</error>")]
|
||||
public void TestEmphasis(string markdown, string expectedHtml)
|
||||
{
|
||||
TestParser.TestSpec(markdown, "<p>" + expectedHtml + "</p>", Pipeline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
using NUnit.Framework;
|
||||
using Markdig.Extensions.EmphasisExtras;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestEmphasisExtraOptions
|
||||
{
|
||||
[Test]
|
||||
public void OnlyStrikethrough_Single()
|
||||
{
|
||||
TestParser.TestSpec("~foo~", "<p>~foo~</p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnlyStrikethrough_Double()
|
||||
{
|
||||
TestParser.TestSpec("~~foo~~", "<p><del>foo</del></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnlySubscript_Single()
|
||||
{
|
||||
TestParser.TestSpec("~foo~", "<p><sub>foo</sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnlySubscript_Double()
|
||||
{
|
||||
TestParser.TestSpec("~~foo~~", "<p><sub><sub>foo</sub></sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SubscriptAndStrikethrough_Single()
|
||||
{
|
||||
TestParser.TestSpec("~foo~", "<p><sub>foo</sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough | EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SubscriptAndStrikethrough_Double()
|
||||
{
|
||||
TestParser.TestSpec("~~foo~~", "<p><del>foo</del></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough | EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
}
|
||||
using NUnit.Framework;
|
||||
using Markdig.Extensions.EmphasisExtras;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestEmphasisExtraOptions
|
||||
{
|
||||
[Test]
|
||||
public void OnlyStrikethrough_Single()
|
||||
{
|
||||
TestParser.TestSpec("~foo~", "<p>~foo~</p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnlyStrikethrough_Double()
|
||||
{
|
||||
TestParser.TestSpec("~~foo~~", "<p><del>foo</del></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnlySubscript_Single()
|
||||
{
|
||||
TestParser.TestSpec("~foo~", "<p><sub>foo</sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnlySubscript_Double()
|
||||
{
|
||||
TestParser.TestSpec("~~foo~~", "<p><sub><sub>foo</sub></sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SubscriptAndStrikethrough_Single()
|
||||
{
|
||||
TestParser.TestSpec("~foo~", "<p><sub>foo</sub></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough | EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SubscriptAndStrikethrough_Double()
|
||||
{
|
||||
TestParser.TestSpec("~~foo~~", "<p><del>foo</del></p>", new MarkdownPipelineBuilder().UseEmphasisExtras(EmphasisExtraOptions.Strikethrough | EmphasisExtraOptions.Subscript).Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/Markdig.Tests/TestExceptionNotThrown.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestExceptionNotThrown
|
||||
{
|
||||
[Test]
|
||||
public void DoesNotThrowIndexOutOfRangeException1()
|
||||
{
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
Markdown.ToHtml("+-\n|\n+", pipeline);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoesNotThrowIndexOutOfRangeException2()
|
||||
{
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
Markdown.ToHtml("+--\n|\n+0", pipeline);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoesNotThrowIndexOutOfRangeException3()
|
||||
{
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
Markdown.ToHtml("+-\n|\n+\n0", pipeline);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoesNotThrowIndexOutOfRangeException4()
|
||||
{
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
Markdown.ToHtml("+-\n|\n+0", pipeline);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
using NUnit.Framework;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestImageAltText
|
||||
{
|
||||
[Test]
|
||||
[TestCase("", "")]
|
||||
[TestCase("", "foo")]
|
||||
[TestCase("![][1]\n\n[1]: image.jpg", "")]
|
||||
[TestCase("![bar][1]\n\n[1]: image.jpg", "bar")]
|
||||
[TestCase("", "")]
|
||||
[TestCase("", "foo")]
|
||||
[TestCase("![][1]\n\n[1]: image.jpg 'title'", "")]
|
||||
[TestCase("![bar][1]\n\n[1]: image.jpg 'title'", "bar")]
|
||||
public void TestImageHtmlAltText(string markdown, string expectedAltText)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown);
|
||||
string actualAltText = Regex.Match(html, "alt=\"(.*?)\"").Groups[1].Value;
|
||||
Assert.AreEqual(expectedAltText, actualAltText);
|
||||
}
|
||||
}
|
||||
}
|
||||
using NUnit.Framework;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestImageAltText
|
||||
{
|
||||
[Test]
|
||||
[TestCase("", "")]
|
||||
[TestCase("", "foo")]
|
||||
[TestCase("![][1]\n\n[1]: image.jpg", "")]
|
||||
[TestCase("![bar][1]\n\n[1]: image.jpg", "bar")]
|
||||
[TestCase("", "")]
|
||||
[TestCase("", "foo")]
|
||||
[TestCase("![][1]\n\n[1]: image.jpg 'title'", "")]
|
||||
[TestCase("![bar][1]\n\n[1]: image.jpg 'title'", "bar")]
|
||||
public void TestImageHtmlAltText(string markdown, string expectedAltText)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown);
|
||||
string actualAltText = Regex.Match(html, "alt=\"(.*?)\"").Groups[1].Value;
|
||||
Assert.AreEqual(expectedAltText, actualAltText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using Markdig.Helpers;
|
||||
|
||||
@@ -18,60 +17,60 @@ namespace Markdig.Tests
|
||||
public void TestEmpty()
|
||||
{
|
||||
var lineReader = new LineReader("");
|
||||
Assert.Null(lineReader.ReadLine()?.ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinesOnlyLf()
|
||||
{
|
||||
var lineReader = new LineReader("\n\n\n");
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(1, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(2, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString());
|
||||
Assert.Null(lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinesOnlyCr()
|
||||
{
|
||||
var lineReader = new LineReader("\r\r\r");
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(1, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(2, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString());
|
||||
Assert.Null(lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinesOnlyCrLf()
|
||||
{
|
||||
var lineReader = new LineReader("\r\n\r\n\r\n");
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(2, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine()?.ToString());
|
||||
Assert.Null(lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual(string.Empty, lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNoEndOfLine()
|
||||
{
|
||||
var lineReader = new LineReader("123");
|
||||
Assert.AreEqual("123", lineReader.ReadLine()?.ToString());
|
||||
Assert.Null(lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLf()
|
||||
{
|
||||
var lineReader = new LineReader("123\n");
|
||||
Assert.AreEqual("123", lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.Null(lineReader.ReadLine()?.ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -79,29 +78,29 @@ namespace Markdig.Tests
|
||||
{
|
||||
// When limited == true, we limit the internal buffer exactly after the first new line char '\n'
|
||||
var lineReader = new LineReader("123\n456");
|
||||
Assert.AreEqual("123", lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.AreEqual("456", lineReader.ReadLine()?.ToString());
|
||||
Assert.Null(lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual("456", lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCr()
|
||||
{
|
||||
var lineReader = new LineReader("123\r");
|
||||
Assert.AreEqual("123", lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.Null(lineReader.ReadLine()?.ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCr2()
|
||||
{
|
||||
var lineReader = new LineReader("123\r456");
|
||||
Assert.AreEqual("123", lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(4, lineReader.SourcePosition);
|
||||
Assert.AreEqual("456", lineReader.ReadLine()?.ToString());
|
||||
Assert.Null(lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual("456", lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -110,19 +109,19 @@ namespace Markdig.Tests
|
||||
// When limited == true, we limit the internal buffer exactly after the first new line char '\r'
|
||||
// and we check that we don't get a new line for `\n`
|
||||
var lineReader = new LineReader("123\r\n");
|
||||
Assert.AreEqual("123", lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(5, lineReader.SourcePosition);
|
||||
Assert.Null(lineReader.ReadLine()?.ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCrLf2()
|
||||
{
|
||||
var lineReader = new LineReader("123\r\n456");
|
||||
Assert.AreEqual("123", lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual("123", lineReader.ReadLine().ToString());
|
||||
Assert.AreEqual(5, lineReader.SourcePosition);
|
||||
Assert.AreEqual("456", lineReader.ReadLine()?.ToString());
|
||||
Assert.Null(lineReader.ReadLine()?.ToString());
|
||||
Assert.AreEqual("456", lineReader.ReadLine().ToString());
|
||||
Assert.Null(lineReader.ReadLine().Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System.Security;
|
||||
using NUnit.Framework;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
@@ -16,8 +15,7 @@ namespace Markdig.Tests
|
||||
public void TestUrlSimple()
|
||||
{
|
||||
var text = new StringSlice("toto tutu");
|
||||
string link;
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out link));
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link));
|
||||
Assert.AreEqual("toto", link);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
@@ -26,8 +24,7 @@ namespace Markdig.Tests
|
||||
public void TestUrlUrl()
|
||||
{
|
||||
var text = new StringSlice("http://google.com)");
|
||||
string link;
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out link));
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual(')', text.CurrentChar);
|
||||
}
|
||||
@@ -38,8 +35,7 @@ namespace Markdig.Tests
|
||||
public void TestUrlTrailingFullStop(string uri)
|
||||
{
|
||||
var text = new StringSlice(uri);
|
||||
string link;
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out link, true));
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link, true));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual('.', text.CurrentChar);
|
||||
}
|
||||
@@ -48,8 +44,7 @@ namespace Markdig.Tests
|
||||
public void TestUrlNestedParenthesis()
|
||||
{
|
||||
var text = new StringSlice("(toto)tutu(tata) nooo");
|
||||
string link;
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out link));
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link));
|
||||
Assert.AreEqual("(toto)tutu(tata)", link);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
@@ -58,8 +53,7 @@ namespace Markdig.Tests
|
||||
public void TestUrlAlternate()
|
||||
{
|
||||
var text = new StringSlice("<toto_tata_tutu> nooo");
|
||||
string link;
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out link));
|
||||
Assert.True(LinkHelper.TryParseUrl(ref text, out string link));
|
||||
Assert.AreEqual("toto_tata_tutu", link);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
@@ -68,16 +62,14 @@ namespace Markdig.Tests
|
||||
public void TestUrlAlternateInvalid()
|
||||
{
|
||||
var text = new StringSlice("<toto_tata_tutu");
|
||||
string link;
|
||||
Assert.False(LinkHelper.TryParseUrl(ref text, out link));
|
||||
Assert.False(LinkHelper.TryParseUrl(ref text, out string link));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTitleSimple()
|
||||
{
|
||||
var text = new StringSlice(@"'tata\tutu\''");
|
||||
string title;
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out title));
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title));
|
||||
Assert.AreEqual(@"tata\tutu'", title);
|
||||
}
|
||||
|
||||
@@ -85,8 +77,7 @@ namespace Markdig.Tests
|
||||
public void TestTitleSimpleAlternate()
|
||||
{
|
||||
var text = new StringSlice(@"""tata\tutu\"""" ");
|
||||
string title;
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out title));
|
||||
Assert.True(LinkHelper.TryParseTitle(ref text, out string title));
|
||||
Assert.AreEqual(@"tata\tutu""", title);
|
||||
Assert.AreEqual(' ', text.CurrentChar);
|
||||
}
|
||||
@@ -97,11 +88,7 @@ namespace Markdig.Tests
|
||||
// 0 1 2 3
|
||||
// 0123456789012345678901234567890123456789
|
||||
var text = new StringSlice(@"(http://google.com 'this is a title')ABC");
|
||||
string link;
|
||||
string title;
|
||||
SourceSpan linkSpan;
|
||||
SourceSpan titleSpan;
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out link, out title, out linkSpan, out titleSpan));
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual("this is a title", title);
|
||||
Assert.AreEqual(new SourceSpan(1, 17), linkSpan);
|
||||
@@ -114,11 +101,7 @@ namespace Markdig.Tests
|
||||
{
|
||||
// 01234
|
||||
var text = new StringSlice(@"(<>)A");
|
||||
string link;
|
||||
string title;
|
||||
SourceSpan linkSpan;
|
||||
SourceSpan titleSpan;
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out link, out title, out linkSpan, out titleSpan));
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(string.Empty, link);
|
||||
Assert.AreEqual(string.Empty, title);
|
||||
Assert.AreEqual(new SourceSpan(1, 2), linkSpan);
|
||||
@@ -131,11 +114,7 @@ namespace Markdig.Tests
|
||||
{
|
||||
// 012345
|
||||
var text = new StringSlice(@"( <> )A");
|
||||
string link;
|
||||
string title;
|
||||
SourceSpan linkSpan;
|
||||
SourceSpan titleSpan;
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out link, out title, out linkSpan, out titleSpan));
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(string.Empty, link);
|
||||
Assert.AreEqual(string.Empty, title);
|
||||
Assert.AreEqual(new SourceSpan(2, 3), linkSpan);
|
||||
@@ -150,11 +129,7 @@ namespace Markdig.Tests
|
||||
// 0 1 2
|
||||
// 0123456789012345678901234567
|
||||
var text = new StringSlice(@"( <> 'toto' )A");
|
||||
string link;
|
||||
string title;
|
||||
SourceSpan linkSpan;
|
||||
SourceSpan titleSpan;
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out link, out title, out linkSpan, out titleSpan));
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(string.Empty, link);
|
||||
Assert.AreEqual("toto", title);
|
||||
Assert.AreEqual(new SourceSpan(4, 5), linkSpan);
|
||||
@@ -166,11 +141,7 @@ namespace Markdig.Tests
|
||||
public void TestUrlEmpty()
|
||||
{
|
||||
var text = new StringSlice(@"()A");
|
||||
string link;
|
||||
string title;
|
||||
SourceSpan linkSpan;
|
||||
SourceSpan titleSpan;
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out link, out title, out linkSpan, out titleSpan));
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(string.Empty, link);
|
||||
Assert.AreEqual(string.Empty, title);
|
||||
Assert.AreEqual(SourceSpan.Empty, linkSpan);
|
||||
@@ -184,11 +155,7 @@ namespace Markdig.Tests
|
||||
// 0 1 2 3
|
||||
// 01 2345678901234567890 1234567890123456789
|
||||
var text = new StringSlice("(\n<http://google.com>\n 'toto' )A");
|
||||
string link;
|
||||
string title;
|
||||
SourceSpan linkSpan;
|
||||
SourceSpan titleSpan;
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out link, out title, out linkSpan, out titleSpan));
|
||||
Assert.True(LinkHelper.TryParseInlineLink(ref text, out string link, out string title, out SourceSpan linkSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual("http://google.com", link);
|
||||
Assert.AreEqual("toto", title);
|
||||
Assert.AreEqual(new SourceSpan(2, 20), linkSpan);
|
||||
@@ -201,9 +168,7 @@ namespace Markdig.Tests
|
||||
{
|
||||
// 01234
|
||||
var text = new StringSlice("[foo]");
|
||||
string label;
|
||||
SourceSpan labelSpan;
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out label, out labelSpan));
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out string label, out SourceSpan labelSpan));
|
||||
Assert.AreEqual(new SourceSpan(1, 3), labelSpan);
|
||||
Assert.AreEqual("foo", label);
|
||||
}
|
||||
@@ -213,9 +178,7 @@ namespace Markdig.Tests
|
||||
{
|
||||
// 012345678
|
||||
var text = new StringSlice(@"[fo\[\]o]");
|
||||
string label;
|
||||
SourceSpan labelSpan;
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out label, out labelSpan));
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out string label, out SourceSpan labelSpan));
|
||||
Assert.AreEqual(new SourceSpan(1, 7), labelSpan);
|
||||
Assert.AreEqual(@"fo[]o", label);
|
||||
}
|
||||
@@ -225,9 +188,7 @@ namespace Markdig.Tests
|
||||
{
|
||||
// 0123
|
||||
var text = new StringSlice(@"[\]]");
|
||||
string label;
|
||||
SourceSpan labelSpan;
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out label, out labelSpan));
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out string label, out SourceSpan labelSpan));
|
||||
Assert.AreEqual(new SourceSpan(1, 2), labelSpan);
|
||||
Assert.AreEqual(@"]", label);
|
||||
}
|
||||
@@ -235,8 +196,7 @@ namespace Markdig.Tests
|
||||
[Test]
|
||||
public void TestLabelInvalids()
|
||||
{
|
||||
string label;
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"a"), out label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"a"), out string label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"["), out label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"[\x]"), out label));
|
||||
Assert.False(LinkHelper.TryParseLabel(new StringSlice(@"[[]"), out label));
|
||||
@@ -250,9 +210,7 @@ namespace Markdig.Tests
|
||||
// 0 1 2 3
|
||||
// 0123456789012345678901234567890123456789
|
||||
var text = new StringSlice(@"[ fo o z ]");
|
||||
string label;
|
||||
SourceSpan labelSpan;
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out label, out labelSpan));
|
||||
Assert.True(LinkHelper.TryParseLabel(ref text, out string label, out SourceSpan labelSpan));
|
||||
Assert.AreEqual(new SourceSpan(6, 17), labelSpan);
|
||||
Assert.AreEqual(@"fo o z", label);
|
||||
}
|
||||
@@ -263,13 +221,7 @@ namespace Markdig.Tests
|
||||
// 0 1 2 3
|
||||
// 0123456789012345678901234567890123456789
|
||||
var text = new StringSlice(@"[foo]: /toto 'title'");
|
||||
string label;
|
||||
string url;
|
||||
string title;
|
||||
SourceSpan labelSpan;
|
||||
SourceSpan urlSpan;
|
||||
SourceSpan titleSpan;
|
||||
Assert.True(LinkHelper.TryParseLinkReferenceDefinition(ref text, out label, out url, out title, out labelSpan, out urlSpan, out titleSpan));
|
||||
Assert.True(LinkHelper.TryParseLinkReferenceDefinition(ref text, out string label, out string url, out string title, out SourceSpan labelSpan, out SourceSpan urlSpan, out SourceSpan titleSpan));
|
||||
Assert.AreEqual(@"foo", label);
|
||||
Assert.AreEqual(@"/toto", url);
|
||||
Assert.AreEqual(@"title", title);
|
||||
@@ -283,9 +235,7 @@ namespace Markdig.Tests
|
||||
public void TestAutoLinkUrlSimple()
|
||||
{
|
||||
var text = new StringSlice(@"<http://google.com>");
|
||||
string url;
|
||||
bool isEmail;
|
||||
Assert.True(LinkHelper.TryParseAutolink(ref text, out url, out isEmail));
|
||||
Assert.True(LinkHelper.TryParseAutolink(ref text, out string url, out bool isEmail));
|
||||
Assert.False(isEmail);
|
||||
Assert.AreEqual("http://google.com", url);
|
||||
}
|
||||
@@ -294,9 +244,7 @@ namespace Markdig.Tests
|
||||
public void TestAutoLinkEmailSimple()
|
||||
{
|
||||
var text = new StringSlice(@"<user@host.com>");
|
||||
string email;
|
||||
bool isEmail;
|
||||
Assert.True(LinkHelper.TryParseAutolink(ref text, out email, out isEmail));
|
||||
Assert.True(LinkHelper.TryParseAutolink(ref text, out string email, out bool isEmail));
|
||||
Assert.True(isEmail);
|
||||
Assert.AreEqual("user@host.com", email);
|
||||
}
|
||||
@@ -304,9 +252,7 @@ namespace Markdig.Tests
|
||||
[Test]
|
||||
public void TestAutolinkInvalid()
|
||||
{
|
||||
string text;
|
||||
bool isEmail;
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@""), out text, out isEmail));
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@""), out string text, out bool isEmail));
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@"<"), out text, out isEmail));
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@"<ab"), out text, out isEmail));
|
||||
Assert.False(LinkHelper.TryParseAutolink(new StringSlice(@"<user@>"), out text, out isEmail));
|
||||
@@ -322,6 +268,16 @@ namespace Markdig.Tests
|
||||
Assert.AreEqual(expectedResult, LinkHelper.Urilize(input, false));
|
||||
}
|
||||
|
||||
[TestCase("Header identifiers in HTML", "header-identifiers-in-html")]
|
||||
[TestCase("* Dogs*?--in *my* house?", "-dogs--in-my-house")]
|
||||
[TestCase("[HTML], [S5], or [RTF]?", "html-s5-or-rtf")]
|
||||
[TestCase("3. Applications", "3-applications")]
|
||||
[TestCase("33", "33")]
|
||||
public void TestUrilizeGfm(string input, string expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, LinkHelper.UrilizeAsGfm(input));
|
||||
}
|
||||
|
||||
[TestCase("abc", "abc")]
|
||||
[TestCase("a-c", "a-c")]
|
||||
[TestCase("a c", "a-c")]
|
||||
@@ -411,9 +367,9 @@ namespace Markdig.Tests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUnicodeInDomainNameOfLinkReferenceDefinition()
|
||||
{
|
||||
TestParser.TestSpec("[Foo]\n\n[Foo]: http://ünicode.com", "<p><a href=\"http://xn--nicode-2ya.com\">Foo</a></p>");
|
||||
public void TestUnicodeInDomainNameOfLinkReferenceDefinition()
|
||||
{
|
||||
TestParser.TestSpec("[Foo]\n\n[Foo]: http://ünicode.com", "<p><a href=\"http://xn--nicode-2ya.com\">Foo</a></p>");
|
||||
}
|
||||
}
|
||||
}
|
||||
149
src/Markdig.Tests/TestMarkdigCoreApi.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class TestMarkdigCoreApi
|
||||
{
|
||||
[Test]
|
||||
public void TestToHtml()
|
||||
{
|
||||
string html = Markdown.ToHtml("This is a text with some *emphasis*");
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
|
||||
html = Markdown.ToHtml("This is a text with a https://link.tld/");
|
||||
Assert.AreNotEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToHtmlWithPipeline()
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.Build();
|
||||
|
||||
string html = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
|
||||
html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
|
||||
Assert.AreNotEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
|
||||
pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
html = Markdown.ToHtml("This is a text with a https://link.tld/", pipeline);
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToHtmlWithWriter()
|
||||
{
|
||||
StringWriter writer = new StringWriter();
|
||||
|
||||
_ = Markdown.ToHtml("This is a text with some *emphasis*", writer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
|
||||
writer = new StringWriter();
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
_ = Markdown.ToHtml("This is a text with a https://link.tld/", writer, pipeline);
|
||||
html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConvert()
|
||||
{
|
||||
StringWriter writer = new StringWriter();
|
||||
HtmlRenderer renderer = new HtmlRenderer(writer);
|
||||
|
||||
_ = Markdown.Convert("This is a text with some *emphasis*", renderer);
|
||||
string html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with some <em>emphasis</em></p>\n", html);
|
||||
|
||||
writer = new StringWriter();
|
||||
renderer = new HtmlRenderer(writer);
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
_ = Markdown.Convert("This is a text with a https://link.tld/", renderer, pipeline);
|
||||
html = writer.ToString();
|
||||
Assert.AreEqual("<p>This is a text with a <a href=\"https://link.tld/\">https://link.tld/</a></p>\n", html);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestParse()
|
||||
{
|
||||
const string markdown = "This is a text with some *emphasis*";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UsePreciseSourceLocation()
|
||||
.Build();
|
||||
|
||||
MarkdownDocument document = Markdown.Parse(markdown, pipeline);
|
||||
|
||||
Assert.AreEqual(1, document.LineCount);
|
||||
Assert.AreEqual(markdown.Length, document.Span.Length);
|
||||
Assert.AreEqual(1, document.LineStartIndexes.Count);
|
||||
Assert.AreEqual(0, document.LineStartIndexes[0]);
|
||||
|
||||
Assert.AreEqual(1, document.Count);
|
||||
ParagraphBlock paragraph = document[0] as ParagraphBlock;
|
||||
Assert.NotNull(paragraph);
|
||||
Assert.AreEqual(markdown.Length, paragraph.Span.Length);
|
||||
LiteralInline literal = paragraph.Inline.FirstChild as LiteralInline;
|
||||
Assert.NotNull(literal);
|
||||
Assert.AreEqual("This is a text with some ", literal.ToString());
|
||||
EmphasisInline emphasis = literal.NextSibling as EmphasisInline;
|
||||
Assert.NotNull(emphasis);
|
||||
Assert.AreEqual("*emphasis*".Length, emphasis.Span.Length);
|
||||
LiteralInline emphasisLiteral = emphasis.FirstChild as LiteralInline;
|
||||
Assert.NotNull(emphasisLiteral);
|
||||
Assert.AreEqual("emphasis", emphasisLiteral.ToString());
|
||||
Assert.Null(emphasisLiteral.NextSibling);
|
||||
Assert.Null(emphasis.NextSibling);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormalize()
|
||||
{
|
||||
string normalized = Markdown.Normalize("Heading\n=======");
|
||||
Assert.AreEqual("# Heading", normalized);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormalizeWithWriter()
|
||||
{
|
||||
StringWriter writer = new StringWriter();
|
||||
|
||||
_ = Markdown.Normalize("Heading\n=======", writer);
|
||||
string normalized = writer.ToString();
|
||||
Assert.AreEqual("# Heading", normalized);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToPlainText()
|
||||
{
|
||||
string plainText = Markdown.ToPlainText("*Hello*, [world](http://example.com)!");
|
||||
Assert.AreEqual("Hello, world!\n", plainText);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToPlainTextWithWriter()
|
||||
{
|
||||
StringWriter writer = new StringWriter();
|
||||
|
||||
_ = Markdown.ToPlainText("*Hello*, [world](http://example.com)!", writer);
|
||||
string plainText = writer.ToString();
|
||||
Assert.AreEqual("Hello, world!\n", plainText);
|
||||
}
|
||||
}
|
||||
}
|
||||
104
src/Markdig.Tests/TestMediaLinks.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using Markdig.Extensions.MediaLinks;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestMediaLinks
|
||||
{
|
||||
private MarkdownPipeline GetPipeline(MediaOptions options = null)
|
||||
{
|
||||
return new MarkdownPipelineBuilder()
|
||||
.UseMediaLinks(options)
|
||||
.Build();
|
||||
}
|
||||
|
||||
private MarkdownPipeline GetPipelineWithBootstrap(MediaOptions options = null)
|
||||
{
|
||||
return new MarkdownPipelineBuilder()
|
||||
.UseBootstrap()
|
||||
.UseMediaLinks(options)
|
||||
.Build();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("", "<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"https://sample.com/video.mp4\"></source></video></p>\n")]
|
||||
[TestCase("", "<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"//sample.com/video.mp4\"></source></video></p>\n")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
[TestCase("", "<p><iframe src=\"https://music.yandex.ru/iframe/#track/4402274/411845/\" class=\"yandex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n")]
|
||||
[TestCase("", "<p><iframe src=\"https://player.vimeo.com/video/8607834\" class=\"vimeo\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
[TestCase("", "<p><iframe src=\"https://ok.ru/videoembed/26870090463\" class=\"odnoklassniki\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
[TestCase("", "<p><iframe src=\"https://ok.ru/videoembed/26870090463\" class=\"odnoklassniki\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
public void TestBuiltInHosts(string markdown, string expected)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipeline());
|
||||
Assert.AreEqual(html, expected);
|
||||
}
|
||||
|
||||
private class TestHostProvider : IHostProvider
|
||||
{
|
||||
public string Class { get; } = "regex";
|
||||
public bool AllowFullScreen { get; }
|
||||
|
||||
public bool TryHandle(Uri mediaUri, bool isSchemaRelative, out string iframeUrl)
|
||||
{
|
||||
iframeUrl = null;
|
||||
var uri = isSchemaRelative ? "//" + mediaUri.GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Scheme, UriFormat.UriEscaped) : mediaUri.ToString();
|
||||
if (!matcher.IsMatch(uri))
|
||||
return false;
|
||||
iframeUrl = matcher.Replace(uri, replacement);
|
||||
return true;
|
||||
}
|
||||
|
||||
private Regex matcher;
|
||||
private string replacement;
|
||||
|
||||
public TestHostProvider(string provider, string replace)
|
||||
{
|
||||
matcher = new Regex(provider);
|
||||
replacement = replace;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("", "<p><iframe src=\"https://example.com/video.mp4\" class=\"regex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n", @"^https?://sample.com/(.+)$", @"https://example.com/$1")]
|
||||
[TestCase("", "<p><iframe src=\"https://example.com/video.mp4\" class=\"regex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n", @"^//sample.com/(.+)$", @"https://example.com/$1")]
|
||||
[TestCase("", "<p><iframe src=\"https://example.com/video.mp4?token=aaabbb\" class=\"regex\" width=\"500\" height=\"281\" frameborder=\"0\"></iframe></p>\n", @"^https?://sample.com/(.+)$", @"https://example.com/$1?token=aaabbb")]
|
||||
public void TestCustomHostProvider(string markdown, string expected, string provider, string replace)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipeline(new MediaOptions
|
||||
{
|
||||
Hosts =
|
||||
{
|
||||
new TestHostProvider(provider, replace),
|
||||
}
|
||||
}));
|
||||
Assert.AreEqual(html, expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("", "<p><video width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"//sample.com/video.mp4\"></source></video></p>\n", "")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n", "")]
|
||||
[TestCase("", "<p><video class=\"k\" width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"//sample.com/video.mp4\"></source></video></p>\n", "k")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"k youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n", "k")]
|
||||
public void TestCustomClass(string markdown, string expected, string klass)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipeline(new MediaOptions
|
||||
{
|
||||
Class = klass,
|
||||
}));
|
||||
Assert.AreEqual(html, expected);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase("", "<p><video class=\"img-fluid\" width=\"500\" height=\"281\" controls=\"\"><source type=\"video/mp4\" src=\"//sample.com/video.mp4\"></source></video></p>\n")]
|
||||
[TestCase(@"", "<p><iframe src=\"https://www.youtube.com/embed/mswPy5bt3TQ\" class=\"img-fluid youtube\" width=\"500\" height=\"281\" frameborder=\"0\" allowfullscreen=\"\"></iframe></p>\n")]
|
||||
public void TestWithBootstrap(string markdown, string expected)
|
||||
{
|
||||
string html = Markdown.ToHtml(markdown, GetPipelineWithBootstrap());
|
||||
Assert.AreEqual(html, expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,171 +1,178 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Markdig.Extensions.JiraLinks;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class TestParser
|
||||
{
|
||||
[Test]
|
||||
public void EnsureSpecsAreUpToDate()
|
||||
{
|
||||
// In CI, SpecFileGen is guaranteed to run
|
||||
if (IsContinuousIntegration)
|
||||
return;
|
||||
|
||||
foreach (var specFilePath in SpecsFilePaths)
|
||||
{
|
||||
string testFilePath = Path.ChangeExtension(specFilePath, ".generated.cs");
|
||||
|
||||
Assert.True(File.Exists(testFilePath),
|
||||
"A new specification file has been added. Add the spec to the list in SpecFileGen and regenerate the tests.");
|
||||
|
||||
DateTime specTime = File.GetLastWriteTimeUtc(specFilePath);
|
||||
DateTime testTime = File.GetLastWriteTimeUtc(testFilePath);
|
||||
|
||||
// If file creation times aren't preserved by git, add some leeway
|
||||
// If specs have come from git, assume that they were regenerated since CI would fail otherwise
|
||||
testTime = testTime.AddSeconds(2);
|
||||
|
||||
// This might not catch a changed spec every time, but should most of the time. Otherwise CI will catch it
|
||||
|
||||
// This could also trigger, if a user has modified the spec file but reverted the change - can't think of a good workaround
|
||||
Assert.Less(specTime, testTime,
|
||||
$"{Path.GetFileName(specFilePath)} has been modified. Run SpecFileGen to regenerate the tests. " +
|
||||
"If you have modified a specification file, but reverted all changes, ignore this error or revert the 'changed' timestamp metadata on the file.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null, bool plainText = false)
|
||||
{
|
||||
foreach (var pipeline in GetPipeline(extensions))
|
||||
{
|
||||
Console.WriteLine($"Pipeline configured with extensions: {pipeline.Key}");
|
||||
TestSpec(inputText, expectedOutputText, pipeline.Value, plainText);
|
||||
}
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, MarkdownPipeline pipeline, bool plainText = false)
|
||||
{
|
||||
// Uncomment this line to get more debug information for process inlines.
|
||||
//pipeline.DebugLog = Console.Out;
|
||||
var result = plainText ? Markdown.ToPlainText(inputText, pipeline) : Markdown.ToHtml(inputText, pipeline);
|
||||
|
||||
result = Compact(result);
|
||||
expectedOutputText = Compact(expectedOutputText);
|
||||
|
||||
PrintAssertExpected(inputText, result, expectedOutputText);
|
||||
}
|
||||
|
||||
public static void PrintAssertExpected(string source, string result, string expected)
|
||||
{
|
||||
Console.WriteLine("```````````````````Source");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(source));
|
||||
Console.WriteLine("```````````````````Result");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(result));
|
||||
Console.WriteLine("```````````````````Expected");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(expected));
|
||||
Console.WriteLine("```````````````````");
|
||||
Console.WriteLine();
|
||||
TextAssert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
public static IEnumerable<KeyValuePair<string, MarkdownPipeline>> GetPipeline(string extensionsGroupText)
|
||||
{
|
||||
// For the standard case, we make sure that both the CommmonMark core and Extra/Advanced are CommonMark compliant!
|
||||
if (string.IsNullOrEmpty(extensionsGroupText))
|
||||
{
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>("default", new MarkdownPipelineBuilder().Build());
|
||||
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>("advanced", new MarkdownPipelineBuilder() // Use similar to advanced extension without auto-identifier
|
||||
.UseAbbreviations()
|
||||
//.UseAutoIdentifiers()
|
||||
.UseCitations()
|
||||
.UseCustomContainers()
|
||||
.UseDefinitionLists()
|
||||
.UseEmphasisExtras()
|
||||
.UseFigures()
|
||||
.UseFooters()
|
||||
.UseFootnotes()
|
||||
.UseGridTables()
|
||||
.UseMathematics()
|
||||
.UseMediaLinks()
|
||||
.UsePipeTables()
|
||||
.UseListExtras()
|
||||
.UseGenericAttributes().Build());
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
var extensionGroups = extensionsGroupText.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var extensionsText in extensionGroups)
|
||||
{
|
||||
var builder = new MarkdownPipelineBuilder();
|
||||
builder.DebugLog = Console.Out;
|
||||
if (extensionsText == "jiralinks")
|
||||
{
|
||||
builder.UseJiraLinks(new JiraLinkOptions("http://your.company.abc"));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder = extensionsText == "self" ? builder.UseSelfPipeline() : builder.Configure(extensionsText);
|
||||
}
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>(extensionsText, builder.Build());
|
||||
}
|
||||
}
|
||||
|
||||
public static string DisplaySpaceAndTabs(string text)
|
||||
{
|
||||
// Output special characters to check correctly the results
|
||||
return text.Replace('\t', '→').Replace(' ', '·');
|
||||
}
|
||||
|
||||
private static string Compact(string html)
|
||||
{
|
||||
// Normalize the output to make it compatible with CommonMark specs
|
||||
html = html.Replace("\r\n", "\n").Replace(@"\r", @"\n").Trim();
|
||||
html = Regex.Replace(html, @"\s+</li>", "</li>");
|
||||
html = Regex.Replace(html, @"<li>\s+", "<li>");
|
||||
html = html.Normalize(NormalizationForm.FormKD);
|
||||
return html;
|
||||
}
|
||||
|
||||
public static readonly bool IsContinuousIntegration = Environment.GetEnvironmentVariable("CI") != null;
|
||||
|
||||
public static readonly string TestsDirectory =
|
||||
Path.GetFullPath(Path.Combine(Path.GetDirectoryName(typeof(TestParser).Assembly.Location), "../../.."));
|
||||
|
||||
/// <summary>
|
||||
/// Contains absolute paths to specification markdown files (order is the same as in <see cref="SpecsMarkdown"/>)
|
||||
/// </summary>
|
||||
public static readonly string[] SpecsFilePaths;
|
||||
/// <summary>
|
||||
/// Contains the markdown source for specification files (order is the same as in <see cref="SpecsFilePaths"/>)
|
||||
/// </summary>
|
||||
public static readonly string[] SpecsMarkdown;
|
||||
static TestParser()
|
||||
{
|
||||
SpecsFilePaths = Directory.GetDirectories(TestsDirectory)
|
||||
.Where(dir => dir.EndsWith("Specs"))
|
||||
.SelectMany(dir => Directory.GetFiles(dir)
|
||||
.Where(file => file.EndsWith(".md", StringComparison.OrdinalIgnoreCase))
|
||||
.Where(file => file.IndexOf("readme", StringComparison.OrdinalIgnoreCase) == -1))
|
||||
.ToArray();
|
||||
|
||||
SpecsMarkdown = new string[SpecsFilePaths.Length];
|
||||
|
||||
for (int i = 0; i < SpecsFilePaths.Length; i++)
|
||||
{
|
||||
SpecsMarkdown[i] = File.ReadAllText(SpecsFilePaths[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Markdig.Extensions.JiraLinks;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class TestParser
|
||||
{
|
||||
[Test]
|
||||
public void EnsureSpecsAreUpToDate()
|
||||
{
|
||||
// In CI, SpecFileGen is guaranteed to run
|
||||
if (IsContinuousIntegration)
|
||||
return;
|
||||
|
||||
var specsFilePaths = Directory.GetDirectories(TestsDirectory)
|
||||
.Where(dir => dir.EndsWith("Specs"))
|
||||
.SelectMany(dir => Directory.GetFiles(dir)
|
||||
.Where(file => file.EndsWith(".md", StringComparison.OrdinalIgnoreCase))
|
||||
.Where(file => file.IndexOf("readme", StringComparison.OrdinalIgnoreCase) == -1))
|
||||
.ToArray();
|
||||
|
||||
var specsMarkdown = new string[specsFilePaths.Length];
|
||||
var specsSyntaxTrees = new MarkdownDocument[specsFilePaths.Length];
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAdvancedExtensions()
|
||||
.Build();
|
||||
|
||||
for (int i = 0; i < specsFilePaths.Length; i++)
|
||||
{
|
||||
string markdown = specsMarkdown[i] = File.ReadAllText(specsFilePaths[i]);
|
||||
specsSyntaxTrees[i] = Markdown.Parse(markdown, pipeline);
|
||||
}
|
||||
|
||||
foreach (var specFilePath in specsFilePaths)
|
||||
{
|
||||
string testFilePath = Path.ChangeExtension(specFilePath, ".generated.cs");
|
||||
|
||||
Assert.True(File.Exists(testFilePath),
|
||||
"A new specification file has been added. Add the spec to the list in SpecFileGen and regenerate the tests.");
|
||||
|
||||
DateTime specTime = File.GetLastWriteTimeUtc(specFilePath);
|
||||
DateTime testTime = File.GetLastWriteTimeUtc(testFilePath);
|
||||
|
||||
// If file creation times aren't preserved by git, add some leeway
|
||||
// If specs have come from git, assume that they were regenerated since CI would fail otherwise
|
||||
testTime = testTime.AddMinutes(3);
|
||||
|
||||
// This might not catch a changed spec every time, but should at least sometimes. Otherwise CI will catch it
|
||||
|
||||
// This could also trigger, if a user has modified the spec file but reverted the change - can't think of a good workaround
|
||||
Assert.Less(specTime, testTime,
|
||||
$"{Path.GetFileName(specFilePath)} has been modified. Run SpecFileGen to regenerate the tests. " +
|
||||
"If you have modified a specification file, but reverted all changes, ignore this error or revert the 'changed' timestamp metadata on the file.");
|
||||
}
|
||||
|
||||
TestDescendantsOrder.TestSchemas(specsSyntaxTrees);
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null, bool plainText = false)
|
||||
{
|
||||
foreach (var pipeline in GetPipeline(extensions))
|
||||
{
|
||||
Console.WriteLine($"Pipeline configured with extensions: {pipeline.Key}");
|
||||
TestSpec(inputText, expectedOutputText, pipeline.Value, plainText);
|
||||
}
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, MarkdownPipeline pipeline, bool plainText = false)
|
||||
{
|
||||
// Uncomment this line to get more debug information for process inlines.
|
||||
//pipeline.DebugLog = Console.Out;
|
||||
var result = plainText ? Markdown.ToPlainText(inputText, pipeline) : Markdown.ToHtml(inputText, pipeline);
|
||||
|
||||
result = Compact(result);
|
||||
expectedOutputText = Compact(expectedOutputText);
|
||||
|
||||
PrintAssertExpected(inputText, result, expectedOutputText);
|
||||
}
|
||||
|
||||
public static void PrintAssertExpected(string source, string result, string expected)
|
||||
{
|
||||
Console.WriteLine("```````````````````Source");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(source));
|
||||
Console.WriteLine("```````````````````Result");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(result));
|
||||
Console.WriteLine("```````````````````Expected");
|
||||
Console.WriteLine(DisplaySpaceAndTabs(expected));
|
||||
Console.WriteLine("```````````````````");
|
||||
Console.WriteLine();
|
||||
TextAssert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
public static IEnumerable<KeyValuePair<string, MarkdownPipeline>> GetPipeline(string extensionsGroupText)
|
||||
{
|
||||
// For the standard case, we make sure that both the CommmonMark core and Extra/Advanced are CommonMark compliant!
|
||||
if (string.IsNullOrEmpty(extensionsGroupText))
|
||||
{
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>("default", new MarkdownPipelineBuilder().Build());
|
||||
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>("advanced", new MarkdownPipelineBuilder() // Use similar to advanced extension without auto-identifier
|
||||
.UseAbbreviations()
|
||||
//.UseAutoIdentifiers()
|
||||
.UseCitations()
|
||||
.UseCustomContainers()
|
||||
.UseDefinitionLists()
|
||||
.UseEmphasisExtras()
|
||||
.UseFigures()
|
||||
.UseFooters()
|
||||
.UseFootnotes()
|
||||
.UseGridTables()
|
||||
.UseMathematics()
|
||||
.UseMediaLinks()
|
||||
.UsePipeTables()
|
||||
.UseListExtras()
|
||||
.UseGenericAttributes().Build());
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
var extensionGroups = extensionsGroupText.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var extensionsText in extensionGroups)
|
||||
{
|
||||
var builder = new MarkdownPipelineBuilder();
|
||||
builder.DebugLog = Console.Out;
|
||||
if (extensionsText == "jiralinks")
|
||||
{
|
||||
builder.UseJiraLinks(new JiraLinkOptions("http://your.company.abc"));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder = extensionsText == "self" ? builder.UseSelfPipeline() : builder.Configure(extensionsText);
|
||||
}
|
||||
yield return new KeyValuePair<string, MarkdownPipeline>(extensionsText, builder.Build());
|
||||
}
|
||||
}
|
||||
|
||||
public static string DisplaySpaceAndTabs(string text)
|
||||
{
|
||||
// Output special characters to check correctly the results
|
||||
return text.Replace('\t', '→').Replace(' ', '·');
|
||||
}
|
||||
|
||||
private static string Compact(string html)
|
||||
{
|
||||
// Normalize the output to make it compatible with CommonMark specs
|
||||
html = html.Replace("\r\n", "\n").Replace(@"\r", @"\n").Trim();
|
||||
html = Regex.Replace(html, @"\s+</li>", "</li>");
|
||||
html = Regex.Replace(html, @"<li>\s+", "<li>");
|
||||
html = html.Normalize(NormalizationForm.FormKD);
|
||||
return html;
|
||||
}
|
||||
|
||||
public static readonly bool IsContinuousIntegration = Environment.GetEnvironmentVariable("CI") != null;
|
||||
|
||||
public static readonly string TestsDirectory = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(typeof(TestParser).Assembly.Location), "../../.."));
|
||||
|
||||
static TestParser()
|
||||
{
|
||||
const string RunningInsideVisualStudioPath = "\\src\\.vs\\markdig\\";
|
||||
int index = TestsDirectory.IndexOf(RunningInsideVisualStudioPath);
|
||||
if (index != -1)
|
||||
{
|
||||
TestsDirectory = TestsDirectory.Substring(0, index) + "\\src\\Markdig.Tests";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,9 +35,9 @@ namespace Markdig.Tests
|
||||
TestParser.TestSpec(markdownText, expected, extensions, plainText: true);
|
||||
}
|
||||
|
||||
public static void TestSpec(string markdownText, string expected, string extensions)
|
||||
{
|
||||
TestParser.TestSpec(markdownText, expected, extensions, plainText: true);
|
||||
public static void TestSpec(string markdownText, string expected, string extensions)
|
||||
{
|
||||
TestParser.TestSpec(markdownText, expected, extensions, plainText: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
using System.Linq;
|
||||
@@ -19,7 +19,7 @@ namespace Markdig.Tests
|
||||
var link = doc.Descendants<ParagraphBlock>().SelectMany(x => x.Inline.Descendants<LinkInline>()).FirstOrDefault(l => l.IsImage);
|
||||
Assert.AreEqual("/yoyo", link?.Url);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void TestListBug2()
|
||||
{
|
||||
@@ -53,11 +53,11 @@ Later in a text we are using HTML and it becomes an abbr tag HTML
|
||||
[Test]
|
||||
public void TestEmptyLiteral()
|
||||
{
|
||||
var text = @"> *some text*
|
||||
var text = @"> *some text*
|
||||
> some other text";
|
||||
var doc = Markdown.Parse(text);
|
||||
|
||||
Assert.True(doc.Descendants().OfType<LiteralInline>().All(x => !x.Content.IsEmpty),
|
||||
Assert.True(doc.Descendants<LiteralInline>().All(x => !x.Content.IsEmpty),
|
||||
"There should not have any empty literals");
|
||||
}
|
||||
|
||||
@@ -195,8 +195,8 @@ Paragraph
|
||||
";
|
||||
|
||||
var expected = @"<table class=""table"">
|
||||
<col style=""width:50%"">
|
||||
<col style=""width:50%"">
|
||||
<col style=""width:50%"" />
|
||||
<col style=""width:50%"" />
|
||||
<thead>
|
||||
<tr>
|
||||
<th>a</th>
|
||||
@@ -219,7 +219,7 @@ Paragraph
|
||||
{
|
||||
var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
|
||||
|
||||
// Reuse the same pipeline
|
||||
// Reuse the same pipeline
|
||||
var result1 = Markdown.ToHtml("This is a \"\"citation\"\"", pipeline);
|
||||
var result2 = Markdown.ToHtml("This is a \"\"citation\"\"", pipeline);
|
||||
|
||||
@@ -269,7 +269,7 @@ Paragraph
|
||||
//| Yes |
|
||||
//| ``` |
|
||||
//+===================================+======================================+
|
||||
//| This is a second line |
|
||||
//| This is a second line |
|
||||
//+-----------------------------------+--------------------------------------+
|
||||
|
||||
//:::spoiler {#yessss}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Syntax;
|
||||
using NUnit.Framework;
|
||||
|
||||
|
||||
71
src/Markdig.Tests/TestReferralLinks.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class TestReferralLinks
|
||||
{
|
||||
[Test]
|
||||
public void TestLinksWithNoFollowRel()
|
||||
{
|
||||
var markdown = "[world](http://example.com)";
|
||||
var expected = "nofollow";
|
||||
|
||||
#pragma warning disable 0618
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseNoFollowLinks()
|
||||
#pragma warning restore 0618
|
||||
.Build();
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
Assert.That(html, Contains.Substring($"rel=\"{expected}\""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(new[] { "nofollow" }, "nofollow")]
|
||||
[TestCase(new[] { "noopener" }, "noopener")]
|
||||
[TestCase(new[] { "nofollow", "noopener"}, "nofollow noopener")]
|
||||
public void TestLinksWithCustomRel(string[] rels, string expected)
|
||||
{
|
||||
var markdown = "[world](http://example.com)";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseReferralLinks(rels)
|
||||
.Build();
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
Assert.That(html, Contains.Substring($"rel=\"{expected}\""));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void TestLinksWithNullRel()
|
||||
{
|
||||
var markdown = "[world](http://example.com)";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseReferralLinks(null)
|
||||
.Build();
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
Assert.That(html, !Contains.Substring("rel="));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase(new[] { "noopener" }, "noopener")]
|
||||
[TestCase(new[] { "nofollow" }, "nofollow")]
|
||||
[TestCase(new[] { "nofollow", "noopener" }, "nofollow noopener")]
|
||||
public void TestAutoLinksWithCustomRel(string[] rels, string expected)
|
||||
{
|
||||
var markdown = "http://example.com";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseAutoLinks()
|
||||
.UseReferralLinks(rels)
|
||||
.Build();
|
||||
var html = Markdown.ToHtml(markdown, pipeline);
|
||||
|
||||
Assert.That(html, Contains.Substring($"rel=\"{expected}\""));
|
||||
}
|
||||
}
|
||||
}
|
||||
36
src/Markdig.Tests/TestSmartyPants.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using Markdig.Extensions.SmartyPants;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class TestSmartyPants
|
||||
{
|
||||
[Test]
|
||||
public void MappingCanBeReconfigured()
|
||||
{
|
||||
SmartyPantOptions options = new SmartyPantOptions();
|
||||
options.Mapping[SmartyPantType.LeftAngleQuote] = "foo";
|
||||
options.Mapping[SmartyPantType.RightAngleQuote] = "bar";
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseSmartyPants(options)
|
||||
.Build();
|
||||
|
||||
TestParser.TestSpec("<<test>>", "<p>footestbar</p>", pipeline);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MappingCanBeReconfigured_HandlesRemovedMappings()
|
||||
{
|
||||
SmartyPantOptions options = new SmartyPantOptions();
|
||||
options.Mapping.Remove(SmartyPantType.LeftAngleQuote);
|
||||
options.Mapping.Remove(SmartyPantType.RightAngleQuote);
|
||||
|
||||
var pipeline = new MarkdownPipelineBuilder()
|
||||
.UseSmartyPants(options)
|
||||
.Build();
|
||||
|
||||
TestParser.TestSpec("<<test>>", "<p>«test»</p>", pipeline);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Markdig.Extensions.Footnotes;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Extensions.Footnotes;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
@@ -141,48 +140,48 @@ literal ( 0, 2) 2-3
|
||||
emphasis ( 0, 4) 4-9
|
||||
literal ( 0, 6) 6-7
|
||||
");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFootnoteLinkReferenceDefinition()
|
||||
{
|
||||
// 01 2 345678
|
||||
var footnote = Markdown.Parse("0\n\n [^1]:", new MarkdownPipelineBuilder().UsePreciseSourceLocation().UseFootnotes().Build()).Descendants().OfType<FootnoteLinkReferenceDefinition>().FirstOrDefault();
|
||||
Assert.NotNull(footnote);
|
||||
|
||||
Assert.AreEqual(2, footnote.Line);
|
||||
Assert.AreEqual(new SourceSpan(4, 7), footnote.Span);
|
||||
Assert.AreEqual(new SourceSpan(5, 6), footnote.LabelSpan);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinkReferenceDefinition1()
|
||||
{
|
||||
// 0 1
|
||||
// 0123456789012345
|
||||
var link = Markdown.Parse("[234]: /56 'yo' ", new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants().OfType<LinkReferenceDefinition>().FirstOrDefault();
|
||||
Assert.NotNull(link);
|
||||
|
||||
Assert.AreEqual(0, link.Line);
|
||||
Assert.AreEqual(new SourceSpan(0, 14), link.Span);
|
||||
Assert.AreEqual(new SourceSpan(1, 3), link.LabelSpan);
|
||||
Assert.AreEqual(new SourceSpan(7, 9), link.UrlSpan);
|
||||
Assert.AreEqual(new SourceSpan(11, 14), link.TitleSpan);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinkReferenceDefinition2()
|
||||
public void TestFootnoteLinkReferenceDefinition()
|
||||
{
|
||||
// 01 2 345678
|
||||
var footnote = Markdown.Parse("0\n\n [^1]:", new MarkdownPipelineBuilder().UsePreciseSourceLocation().UseFootnotes().Build()).Descendants<FootnoteLinkReferenceDefinition>().FirstOrDefault();
|
||||
Assert.NotNull(footnote);
|
||||
|
||||
Assert.AreEqual(2, footnote.Line);
|
||||
Assert.AreEqual(new SourceSpan(4, 7), footnote.Span);
|
||||
Assert.AreEqual(new SourceSpan(5, 6), footnote.LabelSpan);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinkReferenceDefinition1()
|
||||
{
|
||||
// 0 1
|
||||
// 0123456789012345
|
||||
var link = Markdown.Parse("[234]: /56 'yo' ", new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants<LinkReferenceDefinition>().FirstOrDefault();
|
||||
Assert.NotNull(link);
|
||||
|
||||
Assert.AreEqual(0, link.Line);
|
||||
Assert.AreEqual(new SourceSpan(0, 14), link.Span);
|
||||
Assert.AreEqual(new SourceSpan(1, 3), link.LabelSpan);
|
||||
Assert.AreEqual(new SourceSpan(7, 9), link.UrlSpan);
|
||||
Assert.AreEqual(new SourceSpan(11, 14), link.TitleSpan);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLinkReferenceDefinition2()
|
||||
{
|
||||
// 0 1
|
||||
// 01 2 34567890123456789
|
||||
var link = Markdown.Parse("0\n\n [234]: /56 'yo' ", new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants().OfType<LinkReferenceDefinition>().FirstOrDefault();
|
||||
Assert.NotNull(link);
|
||||
|
||||
Assert.AreEqual(2, link.Line);
|
||||
var link = Markdown.Parse("0\n\n [234]: /56 'yo' ", new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants<LinkReferenceDefinition>().FirstOrDefault();
|
||||
Assert.NotNull(link);
|
||||
|
||||
Assert.AreEqual(2, link.Line);
|
||||
Assert.AreEqual(new SourceSpan(4, 18), link.Span);
|
||||
Assert.AreEqual(new SourceSpan(5, 7), link.LabelSpan);
|
||||
Assert.AreEqual(new SourceSpan(11, 13), link.UrlSpan);
|
||||
Assert.AreEqual(new SourceSpan(15, 18), link.TitleSpan);
|
||||
Assert.AreEqual(new SourceSpan(15, 18), link.TitleSpan);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -213,7 +212,7 @@ literal ( 0, 4) 4-5
|
||||
{
|
||||
// 0 1
|
||||
// 01 2 3456789012345
|
||||
var link = Markdown.Parse("0\n\n01 [234](/56)", new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants().OfType<LinkInline>().FirstOrDefault();
|
||||
var link = Markdown.Parse("0\n\n01 [234](/56)", new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants<LinkInline>().FirstOrDefault();
|
||||
Assert.NotNull(link);
|
||||
|
||||
Assert.AreEqual(new SourceSpan(7, 9), link.LabelSpan);
|
||||
@@ -226,7 +225,7 @@ literal ( 0, 4) 4-5
|
||||
{
|
||||
// 0 1
|
||||
// 01 2 34567890123456789
|
||||
var link = Markdown.Parse("0\n\n01 [234](/56 'yo')", new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants().OfType<LinkInline>().FirstOrDefault();
|
||||
var link = Markdown.Parse("0\n\n01 [234](/56 'yo')", new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants<LinkInline>().FirstOrDefault();
|
||||
Assert.NotNull(link);
|
||||
|
||||
Assert.AreEqual(new SourceSpan(7, 9), link.LabelSpan);
|
||||
@@ -240,7 +239,7 @@ literal ( 0, 4) 4-5
|
||||
{
|
||||
// 0 1
|
||||
// 01 2 3456789012345
|
||||
var link = Markdown.Parse("0\n\n01", new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants().OfType<LinkInline>().FirstOrDefault();
|
||||
var link = Markdown.Parse("0\n\n01", new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants<LinkInline>().FirstOrDefault();
|
||||
Assert.NotNull(link);
|
||||
|
||||
Assert.AreEqual(new SourceSpan(5, 15), link.Span);
|
||||
@@ -396,42 +395,42 @@ listitem ( 1, 0) 4-6
|
||||
paragraph ( 1, 2) 6-6
|
||||
literal ( 1, 2) 6-6
|
||||
");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestListBlock2()
|
||||
public void TestListBlock2()
|
||||
{
|
||||
string test = @"
|
||||
1. Foo
|
||||
9. Bar
|
||||
5. Foo
|
||||
6. Bar
|
||||
1. Foo
|
||||
9. Bar
|
||||
5. Foo
|
||||
6. Bar
|
||||
987123. FooBar";
|
||||
test = test.Replace("\r\n", "\n");
|
||||
var list = Markdown.Parse(test, new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants().OfType<ListBlock>().FirstOrDefault();
|
||||
Assert.NotNull(list);
|
||||
|
||||
Assert.AreEqual(1, list.Line);
|
||||
Assert.True(list.IsOrdered);
|
||||
List<ListItemBlock> items = list.Cast<ListItemBlock>().ToList();
|
||||
Assert.AreEqual(5, items.Count);
|
||||
|
||||
// Test orders
|
||||
Assert.AreEqual(1, items[0].Order);
|
||||
var list = Markdown.Parse(test, new MarkdownPipelineBuilder().UsePreciseSourceLocation().Build()).Descendants<ListBlock>().FirstOrDefault();
|
||||
Assert.NotNull(list);
|
||||
|
||||
Assert.AreEqual(1, list.Line);
|
||||
Assert.True(list.IsOrdered);
|
||||
List<ListItemBlock> items = list.Cast<ListItemBlock>().ToList();
|
||||
Assert.AreEqual(5, items.Count);
|
||||
|
||||
// Test orders
|
||||
Assert.AreEqual(1, items[0].Order);
|
||||
Assert.AreEqual(9, items[1].Order);
|
||||
Assert.AreEqual(5, items[2].Order);
|
||||
Assert.AreEqual(6, items[3].Order);
|
||||
Assert.AreEqual(987123, items[4].Order);
|
||||
|
||||
// Test positions
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
Assert.AreEqual(i + 1, items[i].Line);
|
||||
Assert.AreEqual(1 + (i * 7), items[i].Span.Start);
|
||||
Assert.AreEqual(6, items[i].Span.Length);
|
||||
}
|
||||
Assert.AreEqual(5, items[4].Line);
|
||||
Assert.AreEqual(new SourceSpan(29, 42), items[4].Span);
|
||||
Assert.AreEqual(987123, items[4].Order);
|
||||
|
||||
// Test positions
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
Assert.AreEqual(i + 1, items[i].Line);
|
||||
Assert.AreEqual(1 + (i * 7), items[i].Span.Start);
|
||||
Assert.AreEqual(6, items[i].Span.Length);
|
||||
}
|
||||
Assert.AreEqual(5, items[4].Line);
|
||||
Assert.AreEqual(new SourceSpan(29, 42), items[4].Span);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using System.Text;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
using System;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
@@ -27,6 +26,8 @@ namespace Markdig.Tests
|
||||
|
||||
var chars = ToString(text.ToCharIterator());
|
||||
TextAssert.AreEqual("ABC\nE\nF", chars.ToString());
|
||||
|
||||
TextAssert.AreEqual("ABC\nE\nF", text.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -81,8 +82,18 @@ namespace Markdig.Tests
|
||||
public void TestSkipWhitespaces()
|
||||
{
|
||||
var text = new StringLineGroup(" ABC").ToCharIterator();
|
||||
Assert.True(text.TrimStart());
|
||||
Assert.False(text.TrimStart());
|
||||
Assert.AreEqual('A', text.CurrentChar);
|
||||
|
||||
text = new StringLineGroup(" ").ToCharIterator();
|
||||
Assert.True(text.TrimStart());
|
||||
Assert.AreEqual('\0', text.CurrentChar);
|
||||
|
||||
var slice = new StringSlice(" ABC");
|
||||
Assert.False(slice.TrimStart());
|
||||
|
||||
slice = new StringSlice(" ");
|
||||
Assert.True(slice.TrimStart());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -116,5 +127,31 @@ namespace Markdig.Tests
|
||||
var result = ToString(text);
|
||||
TextAssert.AreEqual("ABC \n DEF ", result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStringLineGroupIteratorPeekChar()
|
||||
{
|
||||
var iterator = new StringLineGroup(4)
|
||||
{
|
||||
new StringSlice("ABC"),
|
||||
new StringSlice("E"),
|
||||
new StringSlice("F")
|
||||
}.ToCharIterator();
|
||||
|
||||
Assert.AreEqual('A', iterator.CurrentChar);
|
||||
Assert.AreEqual('A', iterator.PeekChar(0));
|
||||
Assert.AreEqual('B', iterator.PeekChar());
|
||||
Assert.AreEqual('B', iterator.PeekChar(1));
|
||||
Assert.AreEqual('C', iterator.PeekChar(2));
|
||||
Assert.AreEqual('\n', iterator.PeekChar(3));
|
||||
Assert.AreEqual('E', iterator.PeekChar(4));
|
||||
Assert.AreEqual('\n', iterator.PeekChar(5));
|
||||
Assert.AreEqual('F', iterator.PeekChar(6));
|
||||
Assert.AreEqual('\0', iterator.PeekChar(7)); // There is no \n appended to the last line
|
||||
Assert.AreEqual('\0', iterator.PeekChar(8));
|
||||
Assert.AreEqual('\0', iterator.PeekChar(100));
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => iterator.PeekChar(-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace Markdig.WebApp
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// GET api/to_html?text=xxx&extensions=advanced
|
||||
// GET api/to_html?text=xxx&extension=advanced
|
||||
[Route("api/to_html")]
|
||||
[HttpGet()]
|
||||
public object Get([FromQuery] string text, [FromQuery] string extension)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion>
|
||||
<ApplicationInsightsResourceId>/subscriptions/b6745039-70e7-4641-994b-5457cb220e2a/resourcegroups/Default-ApplicationInsights-EastUS/providers/microsoft.insights/components/Markdig.WebApp</ApplicationInsightsResourceId>
|
||||
<ApplicationInsightsAnnotationResourceId>/subscriptions/b6745039-70e7-4641-994b-5457cb220e2a/resourcegroups/Default-ApplicationInsights-EastUS/providers/microsoft.insights/components/Markdig.WebApp</ApplicationInsightsAnnotationResourceId>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -22,16 +23,16 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.12.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.IISIntegration" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
@@ -13,7 +9,6 @@ namespace Markdig.WebApp
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = new WebHostBuilder()
|
||||
.UseApplicationInsights()
|
||||
.UseKestrel()
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
@@ -43,8 +39,8 @@ namespace Markdig.WebApp
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||
{
|
||||
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
||||
loggerFactory.AddDebug();
|
||||
//loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
||||
//loggerFactory.AddDebug();
|
||||
|
||||
app.UseMvc();
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-*" PrivateAssets="All"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -12,7 +12,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
/// <summary>
|
||||
/// An abbreviation object stored at the document level. See extension methods in <see cref="AbbreviationHelper"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Syntax.LeafBlock" />
|
||||
/// <seealso cref="LeafBlock" />
|
||||
[DebuggerDisplay("Abbr {Label} => {Text}")]
|
||||
public class Abbreviation : LeafBlock
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
/// <summary>
|
||||
/// Extension to allow abbreviations.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.IMarkdownExtension" />
|
||||
/// <seealso cref="IMarkdownExtension" />
|
||||
public class AbbreviationExtension : IMarkdownExtension
|
||||
{
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
@@ -19,8 +19,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null && !htmlRenderer.ObjectRenderers.Contains<HtmlAbbreviationRenderer>())
|
||||
if (renderer is HtmlRenderer htmlRenderer && !htmlRenderer.ObjectRenderers.Contains<HtmlAbbreviationRenderer>())
|
||||
{
|
||||
// Must be inserted before CodeBlockRenderer
|
||||
htmlRenderer.ObjectRenderers.Insert(0, new HtmlAbbreviationRenderer());
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Extensions.Abbreviations
|
||||
@@ -21,9 +22,9 @@ namespace Markdig.Extensions.Abbreviations
|
||||
|
||||
public static void AddAbbreviation(this MarkdownDocument document, string label, Abbreviation abbr)
|
||||
{
|
||||
if (document == null) throw new ArgumentNullException(nameof(document));
|
||||
if (label == null) throw new ArgumentNullException(nameof(label));
|
||||
if (abbr == null) throw new ArgumentNullException(nameof(abbr));
|
||||
if (document == null) ThrowHelper.ArgumentNullException(nameof(document));
|
||||
if (label == null) ThrowHelper.ArgumentNullException_label();
|
||||
if (abbr == null) ThrowHelper.ArgumentNullException(nameof(abbr));
|
||||
|
||||
var map = document.GetAbbreviations();
|
||||
if (map == null)
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
/// <summary>
|
||||
/// The inline abbreviation.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Syntax.Inlines.LeafInline" />
|
||||
/// <seealso cref="LeafInline" />
|
||||
[DebuggerDisplay("{Abbreviation}")]
|
||||
public class AbbreviationInline : LeafInline
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
@@ -39,8 +41,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
SourceSpan labelSpan;
|
||||
if (!LinkHelper.TryParseLabel(ref slice, out string label, out labelSpan))
|
||||
if (!LinkHelper.TryParseLabel(ref slice, out string label, out SourceSpan labelSpan))
|
||||
{
|
||||
return BlockState.None;
|
||||
}
|
||||
@@ -98,29 +99,29 @@ namespace Markdig.Extensions.Abbreviations
|
||||
var text = content.Text;
|
||||
|
||||
for (int i = content.Start; i <= content.End; i++)
|
||||
{
|
||||
// Abbreviation must be a whole word == start at the start of a line or after a whitespace
|
||||
if (i != 0)
|
||||
{
|
||||
for (i = i - 1; i <= content.End; i++)
|
||||
{
|
||||
if (text[i].IsWhitespace())
|
||||
{
|
||||
i++;
|
||||
goto ValidAbbreviationStart;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
// Abbreviation must be a whole word == start at the start of a line or after a whitespace
|
||||
if (i != 0)
|
||||
{
|
||||
for (i = i - 1; i <= content.End; i++)
|
||||
{
|
||||
if (text[i].IsWhitespace())
|
||||
{
|
||||
i++;
|
||||
goto ValidAbbreviationStart;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ValidAbbreviationStart:;
|
||||
|
||||
if (prefixTree.TryMatchLongest(text, i, content.End - i + 1, out KeyValuePair<string, Abbreviation> abbreviationMatch))
|
||||
if (prefixTree.TryMatchLongest(text.AsSpan(i, content.End - i + 1), out KeyValuePair<string, Abbreviation> abbreviationMatch))
|
||||
{
|
||||
var match = abbreviationMatch.Key;
|
||||
if (!IsValidAbbreviationEnding(match, content, i))
|
||||
if (!IsValidAbbreviationEnding(match, content, i))
|
||||
{
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
|
||||
var indexAfterMatch = i + match.Length;
|
||||
@@ -150,7 +151,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
|
||||
// Append the previous literal
|
||||
if (i > content.Start && literal.Parent == null)
|
||||
{
|
||||
{
|
||||
container.AppendChild(literal);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
|
||||
@@ -9,7 +10,7 @@ namespace Markdig.Extensions.Abbreviations
|
||||
/// <summary>
|
||||
/// A HTML renderer for a <see cref="AbbreviationInline"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Renderers.Html.HtmlObjectRenderer{CustomContainer}" />
|
||||
/// <seealso cref="HtmlObjectRenderer{CustomContainer}" />
|
||||
public class HtmlAbbreviationRenderer : HtmlObjectRenderer<AbbreviationInline>
|
||||
{
|
||||
protected override void Write(HtmlRenderer renderer, AbbreviationInline obj)
|
||||
|
||||
@@ -1,199 +1,212 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.AutoIdentifiers
|
||||
{
|
||||
/// <summary>
|
||||
/// The auto-identifier extension
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.IMarkdownExtension" />
|
||||
public class AutoIdentifierExtension : IMarkdownExtension
|
||||
{
|
||||
private const string AutoIdentifierKey = "AutoIdentifier";
|
||||
private readonly AutoIdentifierOptions options;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AutoIdentifierExtension"/> class.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
public AutoIdentifierExtension(AutoIdentifierOptions options)
|
||||
{
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
var headingBlockParser = pipeline.BlockParsers.Find<HeadingBlockParser>();
|
||||
if (headingBlockParser != null)
|
||||
{
|
||||
// Install a hook on the HeadingBlockParser when a HeadingBlock is actually processed
|
||||
headingBlockParser.Closed -= HeadingBlockParser_Closed;
|
||||
headingBlockParser.Closed += HeadingBlockParser_Closed;
|
||||
}
|
||||
var paragraphBlockParser = pipeline.BlockParsers.FindExact<ParagraphBlockParser>();
|
||||
if (paragraphBlockParser != null)
|
||||
{
|
||||
// Install a hook on the ParagraphBlockParser when a HeadingBlock is actually processed as a Setex heading
|
||||
paragraphBlockParser.Closed -= HeadingBlockParser_Closed;
|
||||
paragraphBlockParser.Closed += HeadingBlockParser_Closed;
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process on a new <see cref="HeadingBlock"/>
|
||||
/// </summary>
|
||||
/// <param name="processor">The processor.</param>
|
||||
/// <param name="block">The heading block.</param>
|
||||
private void HeadingBlockParser_Closed(BlockProcessor processor, Block block)
|
||||
{
|
||||
// We may have a ParagraphBlock here as we have a hook on the ParagraphBlockParser
|
||||
var headingBlock = block as HeadingBlock;
|
||||
if (headingBlock == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the AutoLink options is set, we register a LinkReferenceDefinition at the document level
|
||||
if ((options & AutoIdentifierOptions.AutoLink) != 0)
|
||||
{
|
||||
var headingLine = headingBlock.Lines.Lines[0];
|
||||
|
||||
var text = headingLine.ToString();
|
||||
|
||||
var linkRef = new HeadingLinkReferenceDefinition()
|
||||
{
|
||||
Heading = headingBlock,
|
||||
CreateLinkInline = CreateLinkInlineForHeading
|
||||
};
|
||||
|
||||
var doc = processor.Document;
|
||||
var dictionary = doc.GetData(this) as Dictionary<string, HeadingLinkReferenceDefinition>;
|
||||
if (dictionary == null)
|
||||
{
|
||||
dictionary = new Dictionary<string, HeadingLinkReferenceDefinition>();
|
||||
doc.SetData(this, dictionary);
|
||||
doc.ProcessInlinesBegin += DocumentOnProcessInlinesBegin;
|
||||
}
|
||||
dictionary[text] = linkRef;
|
||||
}
|
||||
|
||||
// Then we register after inline have been processed to actually generate the proper #id
|
||||
headingBlock.ProcessInlinesEnd += HeadingBlock_ProcessInlinesEnd;
|
||||
}
|
||||
|
||||
private void DocumentOnProcessInlinesBegin(InlineProcessor processor, Inline inline)
|
||||
{
|
||||
var doc = processor.Document;
|
||||
doc.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;
|
||||
var dictionary = (Dictionary<string, HeadingLinkReferenceDefinition>)doc.GetData(this);
|
||||
foreach (var keyPair in dictionary)
|
||||
{
|
||||
// Here we make sure that auto-identifiers will not override an existing link definition
|
||||
// defined in the document
|
||||
// If it is the case, we skip the auto identifier for the Heading
|
||||
if (!doc.TryGetLinkReferenceDefinition(keyPair.Key, out var linkDef))
|
||||
{
|
||||
doc.SetLinkReferenceDefinition(keyPair.Key, keyPair.Value);
|
||||
}
|
||||
}
|
||||
// Once we are done, we don't need to keep the intermediate dictionary around
|
||||
doc.RemoveData(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback when there is a reference to found to a heading.
|
||||
/// Note that reference are only working if they are declared after.
|
||||
/// </summary>
|
||||
private Inline CreateLinkInlineForHeading(InlineProcessor inlineState, LinkReferenceDefinition linkRef, Inline child)
|
||||
{
|
||||
var headingRef = (HeadingLinkReferenceDefinition) linkRef;
|
||||
return new LinkInline()
|
||||
{
|
||||
// Use GetDynamicUrl to allow late binding of the Url (as a link may occur before the heading is declared and
|
||||
// the inlines of the heading are actually processed by HeadingBlock_ProcessInlinesEnd)
|
||||
GetDynamicUrl = () => HtmlHelper.Unescape("#" + headingRef.Heading.GetAttributes().Id),
|
||||
Title = HtmlHelper.Unescape(linkRef.Title),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the inlines of the heading to create a unique identifier
|
||||
/// </summary>
|
||||
/// <param name="processor">The processor.</param>
|
||||
/// <param name="inline">The inline.</param>
|
||||
private void HeadingBlock_ProcessInlinesEnd(InlineProcessor processor, Inline inline)
|
||||
{
|
||||
var identifiers = processor.Document.GetData(AutoIdentifierKey) as HashSet<string>;
|
||||
if (identifiers == null)
|
||||
{
|
||||
identifiers = new HashSet<string>();
|
||||
processor.Document.SetData(AutoIdentifierKey, identifiers);
|
||||
}
|
||||
|
||||
var headingBlock = (HeadingBlock) processor.Block;
|
||||
if (headingBlock.Inline == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If id is already set, don't try to modify it
|
||||
var attributes = processor.Block.GetAttributes();
|
||||
if (attributes.Id != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Use internally a HtmlRenderer to strip links from a heading
|
||||
var headingWriter = new StringWriter();
|
||||
var stripRenderer = new HtmlRenderer(headingWriter)
|
||||
{
|
||||
// Set to false both to avoid having any HTML tags in the output
|
||||
EnableHtmlForInline = false,
|
||||
EnableHtmlEscape = false
|
||||
};
|
||||
|
||||
stripRenderer.Render(headingBlock.Inline);
|
||||
var headingText = headingWriter.ToString();
|
||||
headingWriter.GetStringBuilder().Length = 0;
|
||||
|
||||
// Urilize the link
|
||||
headingText = (options & AutoIdentifierOptions.GitHub) != 0
|
||||
? LinkHelper.UrilizeAsGfm(headingText)
|
||||
: LinkHelper.Urilize(headingText, (options & AutoIdentifierOptions.AllowOnlyAscii) != 0);
|
||||
|
||||
// If the heading is empty, use the word "section" instead
|
||||
var baseHeadingId = string.IsNullOrEmpty(headingText) ? "section" : headingText;
|
||||
|
||||
// Add a trailing -1, -2, -3...etc. in case of collision
|
||||
int index = 0;
|
||||
var headingId = baseHeadingId;
|
||||
var headingBuffer = StringBuilderCache.Local();
|
||||
while (!identifiers.Add(headingId))
|
||||
{
|
||||
index++;
|
||||
headingBuffer.Append(baseHeadingId);
|
||||
headingBuffer.Append('-');
|
||||
headingBuffer.Append(index);
|
||||
headingId = headingBuffer.ToString();
|
||||
headingBuffer.Length = 0;
|
||||
}
|
||||
|
||||
attributes.Id = headingId;
|
||||
}
|
||||
}
|
||||
// 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.Collections.Generic;
|
||||
using System.IO;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.AutoIdentifiers
|
||||
{
|
||||
/// <summary>
|
||||
/// The auto-identifier extension
|
||||
/// </summary>
|
||||
/// <seealso cref="IMarkdownExtension" />
|
||||
public class AutoIdentifierExtension : IMarkdownExtension
|
||||
{
|
||||
private const string AutoIdentifierKey = "AutoIdentifier";
|
||||
private readonly AutoIdentifierOptions options;
|
||||
private readonly StripRendererCache rendererCache = new StripRendererCache();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AutoIdentifierExtension"/> class.
|
||||
/// </summary>
|
||||
/// <param name="options">The options.</param>
|
||||
public AutoIdentifierExtension(AutoIdentifierOptions options)
|
||||
{
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
var headingBlockParser = pipeline.BlockParsers.Find<HeadingBlockParser>();
|
||||
if (headingBlockParser != null)
|
||||
{
|
||||
// Install a hook on the HeadingBlockParser when a HeadingBlock is actually processed
|
||||
headingBlockParser.Closed -= HeadingBlockParser_Closed;
|
||||
headingBlockParser.Closed += HeadingBlockParser_Closed;
|
||||
}
|
||||
var paragraphBlockParser = pipeline.BlockParsers.FindExact<ParagraphBlockParser>();
|
||||
if (paragraphBlockParser != null)
|
||||
{
|
||||
// Install a hook on the ParagraphBlockParser when a HeadingBlock is actually processed as a Setex heading
|
||||
paragraphBlockParser.Closed -= HeadingBlockParser_Closed;
|
||||
paragraphBlockParser.Closed += HeadingBlockParser_Closed;
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process on a new <see cref="HeadingBlock"/>
|
||||
/// </summary>
|
||||
/// <param name="processor">The processor.</param>
|
||||
/// <param name="block">The heading block.</param>
|
||||
private void HeadingBlockParser_Closed(BlockProcessor processor, Block block)
|
||||
{
|
||||
// We may have a ParagraphBlock here as we have a hook on the ParagraphBlockParser
|
||||
if (!(block is HeadingBlock headingBlock))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the AutoLink options is set, we register a LinkReferenceDefinition at the document level
|
||||
if ((options & AutoIdentifierOptions.AutoLink) != 0)
|
||||
{
|
||||
var headingLine = headingBlock.Lines.Lines[0];
|
||||
|
||||
var text = headingLine.ToString();
|
||||
|
||||
var linkRef = new HeadingLinkReferenceDefinition()
|
||||
{
|
||||
Heading = headingBlock,
|
||||
CreateLinkInline = CreateLinkInlineForHeading
|
||||
};
|
||||
|
||||
var doc = processor.Document;
|
||||
var dictionary = doc.GetData(this) as Dictionary<string, HeadingLinkReferenceDefinition>;
|
||||
if (dictionary == null)
|
||||
{
|
||||
dictionary = new Dictionary<string, HeadingLinkReferenceDefinition>();
|
||||
doc.SetData(this, dictionary);
|
||||
doc.ProcessInlinesBegin += DocumentOnProcessInlinesBegin;
|
||||
}
|
||||
dictionary[text] = linkRef;
|
||||
}
|
||||
|
||||
// Then we register after inline have been processed to actually generate the proper #id
|
||||
headingBlock.ProcessInlinesEnd += HeadingBlock_ProcessInlinesEnd;
|
||||
}
|
||||
|
||||
private void DocumentOnProcessInlinesBegin(InlineProcessor processor, Inline inline)
|
||||
{
|
||||
var doc = processor.Document;
|
||||
doc.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;
|
||||
var dictionary = (Dictionary<string, HeadingLinkReferenceDefinition>)doc.GetData(this);
|
||||
foreach (var keyPair in dictionary)
|
||||
{
|
||||
// Here we make sure that auto-identifiers will not override an existing link definition
|
||||
// defined in the document
|
||||
// If it is the case, we skip the auto identifier for the Heading
|
||||
if (!doc.TryGetLinkReferenceDefinition(keyPair.Key, out var linkDef))
|
||||
{
|
||||
doc.SetLinkReferenceDefinition(keyPair.Key, keyPair.Value);
|
||||
}
|
||||
}
|
||||
// Once we are done, we don't need to keep the intermediate dictionary around
|
||||
doc.RemoveData(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback when there is a reference to found to a heading.
|
||||
/// Note that reference are only working if they are declared after.
|
||||
/// </summary>
|
||||
private Inline CreateLinkInlineForHeading(InlineProcessor inlineState, LinkReferenceDefinition linkRef, Inline child)
|
||||
{
|
||||
var headingRef = (HeadingLinkReferenceDefinition) linkRef;
|
||||
return new LinkInline()
|
||||
{
|
||||
// Use GetDynamicUrl to allow late binding of the Url (as a link may occur before the heading is declared and
|
||||
// the inlines of the heading are actually processed by HeadingBlock_ProcessInlinesEnd)
|
||||
GetDynamicUrl = () => HtmlHelper.Unescape("#" + headingRef.Heading.GetAttributes().Id),
|
||||
Title = HtmlHelper.Unescape(linkRef.Title),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the inlines of the heading to create a unique identifier
|
||||
/// </summary>
|
||||
/// <param name="processor">The processor.</param>
|
||||
/// <param name="inline">The inline.</param>
|
||||
private void HeadingBlock_ProcessInlinesEnd(InlineProcessor processor, Inline inline)
|
||||
{
|
||||
var identifiers = processor.Document.GetData(AutoIdentifierKey) as HashSet<string>;
|
||||
if (identifiers == null)
|
||||
{
|
||||
identifiers = new HashSet<string>();
|
||||
processor.Document.SetData(AutoIdentifierKey, identifiers);
|
||||
}
|
||||
|
||||
var headingBlock = (HeadingBlock) processor.Block;
|
||||
if (headingBlock.Inline == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If id is already set, don't try to modify it
|
||||
var attributes = processor.Block.GetAttributes();
|
||||
if (attributes.Id != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Use internally a HtmlRenderer to strip links from a heading
|
||||
var stripRenderer = rendererCache.Get();
|
||||
|
||||
stripRenderer.Render(headingBlock.Inline);
|
||||
var headingText = stripRenderer.Writer.ToString();
|
||||
rendererCache.Release(stripRenderer);
|
||||
|
||||
// Urilize the link
|
||||
headingText = (options & AutoIdentifierOptions.GitHub) != 0
|
||||
? LinkHelper.UrilizeAsGfm(headingText)
|
||||
: LinkHelper.Urilize(headingText, (options & AutoIdentifierOptions.AllowOnlyAscii) != 0);
|
||||
|
||||
// If the heading is empty, use the word "section" instead
|
||||
var baseHeadingId = string.IsNullOrEmpty(headingText) ? "section" : headingText;
|
||||
|
||||
// Add a trailing -1, -2, -3...etc. in case of collision
|
||||
int index = 0;
|
||||
var headingId = baseHeadingId;
|
||||
var headingBuffer = StringBuilderCache.Local();
|
||||
while (!identifiers.Add(headingId))
|
||||
{
|
||||
index++;
|
||||
headingBuffer.Append(baseHeadingId);
|
||||
headingBuffer.Append('-');
|
||||
headingBuffer.Append(index);
|
||||
headingId = headingBuffer.ToString();
|
||||
headingBuffer.Length = 0;
|
||||
}
|
||||
|
||||
attributes.Id = headingId;
|
||||
}
|
||||
|
||||
private sealed class StripRendererCache : ObjectCache<HtmlRenderer>
|
||||
{
|
||||
protected override HtmlRenderer NewInstance()
|
||||
{
|
||||
var headingWriter = new StringWriter();
|
||||
var stripRenderer = new HtmlRenderer(headingWriter)
|
||||
{
|
||||
// Set to false both to avoid having any HTML tags in the output
|
||||
EnableHtmlForInline = false,
|
||||
EnableHtmlEscape = false
|
||||
};
|
||||
return stripRenderer;
|
||||
}
|
||||
|
||||
protected override void Reset(HtmlRenderer instance)
|
||||
{
|
||||
instance.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Markdig.Extensions.AutoIdentifiers
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Extensions.AutoIdentifiers
|
||||
@@ -8,7 +9,7 @@ namespace Markdig.Extensions.AutoIdentifiers
|
||||
/// <summary>
|
||||
/// A link reference definition to a <see cref="HeadingBlock"/> stored at the <see cref="MarkdownDocument"/> level.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Syntax.LinkReferenceDefinition" />
|
||||
/// <seealso cref="LinkReferenceDefinition" />
|
||||
public class HeadingLinkReferenceDefinition : LinkReferenceDefinition
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Normalize;
|
||||
using Markdig.Renderers.Normalize.Inlines;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.AutoLinks
|
||||
{
|
||||
@@ -16,9 +17,9 @@ namespace Markdig.Extensions.AutoLinks
|
||||
{
|
||||
public readonly AutoLinkOptions Options;
|
||||
|
||||
public AutoLinkExtension(AutoLinkOptions options)
|
||||
{
|
||||
Options = options ?? new AutoLinkOptions();
|
||||
public AutoLinkExtension(AutoLinkOptions options)
|
||||
{
|
||||
Options = options ?? new AutoLinkOptions();
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
namespace Markdig.Extensions.AutoLinks
|
||||
{
|
||||
public class AutoLinkOptions
|
||||
{
|
||||
public AutoLinkOptions()
|
||||
{
|
||||
ValidPreviousCharacters = "*_~(";
|
||||
}
|
||||
|
||||
public string ValidPreviousCharacters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the link open in a new window when clicked (false by default)
|
||||
/// </summary>
|
||||
public bool OpenInNewWindow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should a www link be prefixed with https:// instead of http:// (false by default)
|
||||
/// </summary>
|
||||
public bool UseHttpsForWWWLinks { get; set; }
|
||||
}
|
||||
}
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
namespace Markdig.Extensions.AutoLinks
|
||||
{
|
||||
public class AutoLinkOptions
|
||||
{
|
||||
public AutoLinkOptions()
|
||||
{
|
||||
ValidPreviousCharacters = "*_~(";
|
||||
}
|
||||
|
||||
public string ValidPreviousCharacters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the link open in a new window when clicked (false by default)
|
||||
/// </summary>
|
||||
public bool OpenInNewWindow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should a www link be prefixed with https:// instead of http:// (false by default)
|
||||
/// </summary>
|
||||
public bool UseHttpsForWWWLinks { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Renderers.Html;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.AutoLinks
|
||||
@@ -14,7 +14,7 @@ namespace Markdig.Extensions.AutoLinks
|
||||
/// <summary>
|
||||
/// The inline parser used to for autolinks.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Parsers.InlineParser" />
|
||||
/// <seealso cref="InlineParser" />
|
||||
public class AutoLinkParser : InlineParser
|
||||
{
|
||||
/// <summary>
|
||||
@@ -29,12 +29,17 @@ namespace Markdig.Extensions.AutoLinks
|
||||
'h', // for http:// and https://
|
||||
'f', // for ftp://
|
||||
'm', // for mailto:
|
||||
't', // for tel:
|
||||
'w', // for www.
|
||||
};
|
||||
|
||||
_listOfCharCache = new ListOfCharCache();
|
||||
}
|
||||
|
||||
public readonly AutoLinkOptions Options;
|
||||
|
||||
private readonly ListOfCharCache _listOfCharCache;
|
||||
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
// Previous char must be a whitespace or a punctuation
|
||||
@@ -44,159 +49,175 @@ namespace Markdig.Extensions.AutoLinks
|
||||
return false;
|
||||
}
|
||||
|
||||
List<char> pendingEmphasis;
|
||||
// Check that an autolink is possible in the current context
|
||||
if (!IsAutoLinkValidInCurrentContext(processor, out pendingEmphasis))
|
||||
List<char> pendingEmphasis = _listOfCharCache.Get();
|
||||
try
|
||||
{
|
||||
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 'w':
|
||||
if (!slice.MatchLowercase("ww.", 1)) // We won't match http:/www. or /www.xxx
|
||||
{
|
||||
return false;
|
||||
}
|
||||
domainOffset = 4; // www.
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse URL
|
||||
string link;
|
||||
if (!LinkHelper.TryParseUrl(ref slice, out link, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// If we have any pending emphasis, remove any pending emphasis characters from the end of the link
|
||||
if (pendingEmphasis != null)
|
||||
{
|
||||
for (int i = link.Length - 1; i >= 0; i--)
|
||||
// Check that an autolink is possible in the current context
|
||||
if (!IsAutoLinkValidInCurrentContext(processor, pendingEmphasis))
|
||||
{
|
||||
if (pendingEmphasis.Contains(link[i]))
|
||||
{
|
||||
slice.Start--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i < link.Length - 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
var startPosition = slice.Start;
|
||||
int domainOffset = 0;
|
||||
|
||||
var c = slice.CurrentChar;
|
||||
// Precheck URL
|
||||
switch (c)
|
||||
{
|
||||
case 'h':
|
||||
if (slice.MatchLowercase("ttp://", 1))
|
||||
{
|
||||
link = link.Substring(0, i + 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;
|
||||
}
|
||||
|
||||
if (!LinkHelper.TryParseUrl(ref slice, out string link, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// If we have any pending emphasis, remove any pending emphasis characters from the end of the link
|
||||
if (pendingEmphasis.Count > 0)
|
||||
{
|
||||
for (int i = link.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (pendingEmphasis.Contains(link[i]))
|
||||
{
|
||||
slice.Start--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i < link.Length - 1)
|
||||
{
|
||||
link = link.Substring(0, i + 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Post-check URL
|
||||
switch (c)
|
||||
{
|
||||
case 'h':
|
||||
if (string.Equals(link, "http://", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(link, "https://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
if (string.Equals(link, "ftp://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
int atIndex = link.IndexOf('@');
|
||||
if (atIndex == -1 ||
|
||||
atIndex == 7) // mailto:@ - no email part
|
||||
{
|
||||
return false;
|
||||
}
|
||||
domainOffset = atIndex + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!LinkHelper.IsValidDomain(link, domainOffset))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var inline = new LinkInline()
|
||||
{
|
||||
Span =
|
||||
// Post-check URL
|
||||
switch (c)
|
||||
{
|
||||
Start = processor.GetSourcePosition(startPosition, out int line, out int column),
|
||||
},
|
||||
Line = line,
|
||||
Column = column,
|
||||
Url = c == 'w' ? ((Options.UseHttpsForWWWLinks ? "https://" : "http://") + link) : link,
|
||||
IsClosed = true,
|
||||
IsAutoLink = true,
|
||||
};
|
||||
case 'h':
|
||||
if (string.Equals(link, "http://", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(link, "https://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
if (string.Equals(link, "ftp://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (string.Equals(link, "tel", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
int atIndex = link.IndexOf('@');
|
||||
if (atIndex == -1 ||
|
||||
atIndex == 7) // mailto:@ - no email part
|
||||
{
|
||||
return false;
|
||||
}
|
||||
domainOffset = atIndex + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
var skipFromBeginning = c == 'm' ? 7 : 0; // For mailto: skip "mailto:" for content
|
||||
// Do not need to check if a telephone number is a valid domain
|
||||
if (c != 't' && !LinkHelper.IsValidDomain(link, domainOffset))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inline.Span.End = inline.Span.Start + link.Length - 1;
|
||||
inline.UrlSpan = inline.Span;
|
||||
inline.AppendChild(new LiteralInline()
|
||||
{
|
||||
Span = inline.Span,
|
||||
Line = line,
|
||||
Column = column,
|
||||
Content = new StringSlice(slice.Text, startPosition + skipFromBeginning, startPosition + link.Length - 1),
|
||||
IsClosed = true
|
||||
});
|
||||
processor.Inline = inline;
|
||||
var inline = new LinkInline()
|
||||
{
|
||||
Span =
|
||||
{
|
||||
Start = processor.GetSourcePosition(startPosition, out int line, out int column),
|
||||
},
|
||||
Line = line,
|
||||
Column = column,
|
||||
Url = c == 'w' ? ((Options.UseHttpsForWWWLinks ? "https://" : "http://") + link) : link,
|
||||
IsClosed = true,
|
||||
IsAutoLink = true,
|
||||
};
|
||||
|
||||
if (Options.OpenInNewWindow)
|
||||
{
|
||||
inline.GetAttributes().AddPropertyIfNotExist("target", "blank");
|
||||
var skipFromBeginning = c == 'm' ? 7 : 0; // For mailto: skip "mailto:" for content
|
||||
skipFromBeginning = c == 't' ? 4 : skipFromBeginning; // See above but for tel:
|
||||
|
||||
inline.Span.End = inline.Span.Start + link.Length - 1;
|
||||
inline.UrlSpan = inline.Span;
|
||||
inline.AppendChild(new LiteralInline()
|
||||
{
|
||||
Span = inline.Span,
|
||||
Line = line,
|
||||
Column = column,
|
||||
Content = new StringSlice(slice.Text, startPosition + skipFromBeginning, startPosition + link.Length - 1),
|
||||
IsClosed = true
|
||||
});
|
||||
processor.Inline = inline;
|
||||
|
||||
if (Options.OpenInNewWindow)
|
||||
{
|
||||
inline.GetAttributes().AddPropertyIfNotExist("target", "blank");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_listOfCharCache.Release(pendingEmphasis);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsAutoLinkValidInCurrentContext(InlineProcessor processor, out List<char> pendingEmphasis)
|
||||
private bool IsAutoLinkValidInCurrentContext(InlineProcessor processor, List<char> pendingEmphasis)
|
||||
{
|
||||
pendingEmphasis = null;
|
||||
|
||||
// Case where there is a pending HtmlInline <a>
|
||||
var currentInline = processor.Inline;
|
||||
while (currentInline != null)
|
||||
{
|
||||
var htmlInline = currentInline as HtmlInline;
|
||||
if (htmlInline != null)
|
||||
if (currentInline is HtmlInline htmlInline)
|
||||
{
|
||||
// If we have a </a> we don't expect nested <a>
|
||||
if (htmlInline.Tag.StartsWith("</a", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -211,7 +232,7 @@ namespace Markdig.Extensions.AutoLinks
|
||||
}
|
||||
}
|
||||
|
||||
// Check previous sibling and parents in the tree
|
||||
// Check previous sibling and parents in the tree
|
||||
currentInline = currentInline.PreviousSibling ?? currentInline.Parent;
|
||||
}
|
||||
|
||||
@@ -221,8 +242,7 @@ namespace Markdig.Extensions.AutoLinks
|
||||
int countBrackets = 0;
|
||||
while (currentInline != null)
|
||||
{
|
||||
var linkDelimiterInline = currentInline as LinkDelimiterInline;
|
||||
if (linkDelimiterInline != null && linkDelimiterInline.IsActive)
|
||||
if (currentInline is LinkDelimiterInline linkDelimiterInline && linkDelimiterInline.IsActive)
|
||||
{
|
||||
if (linkDelimiterInline.Type == DelimiterType.Open)
|
||||
{
|
||||
@@ -236,14 +256,8 @@ namespace Markdig.Extensions.AutoLinks
|
||||
else
|
||||
{
|
||||
// Record all pending characters for emphasis
|
||||
var emphasisDelimiter = currentInline as EmphasisDelimiterInline;
|
||||
if (emphasisDelimiter != null)
|
||||
if (currentInline is EmphasisDelimiterInline emphasisDelimiter)
|
||||
{
|
||||
if (pendingEmphasis == null)
|
||||
{
|
||||
// Not optimized for GC, but we don't expect this case much
|
||||
pendingEmphasis = new List<char>();
|
||||
}
|
||||
if (!pendingEmphasis.Contains(emphasisDelimiter.DelimiterChar))
|
||||
{
|
||||
pendingEmphasis.Add(emphasisDelimiter.DelimiterChar);
|
||||
@@ -255,5 +269,13 @@ namespace Markdig.Extensions.AutoLinks
|
||||
|
||||
return countBrackets <= 0;
|
||||
}
|
||||
|
||||
private sealed class ListOfCharCache : DefaultObjectCache<List<char>>
|
||||
{
|
||||
protected override void Reset(List<char> instance)
|
||||
{
|
||||
instance.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Normalize;
|
||||
using Markdig.Syntax;
|
||||
@@ -11,10 +15,10 @@ namespace Markdig.Extensions.AutoLinks
|
||||
{
|
||||
if (base.Accept(renderer, obj))
|
||||
{
|
||||
var normalizeRenderer = renderer as NormalizeRenderer;
|
||||
var link = obj as LinkInline;
|
||||
|
||||
return normalizeRenderer != null && link != null && !normalizeRenderer.Options.ExpandAutoLinks && link.IsAutoLink;
|
||||
return renderer is NormalizeRenderer normalizeRenderer
|
||||
&& obj is LinkInline link
|
||||
&& !normalizeRenderer.Options.ExpandAutoLinks
|
||||
&& link.IsAutoLink;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Markdig.Extensions.Bootstrap
|
||||
/// <summary>
|
||||
/// Extension for tagging some HTML elements with bootstrap classes.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.IMarkdownExtension" />
|
||||
/// <seealso cref="IMarkdownExtension" />
|
||||
public class BootstrapExtension : IMarkdownExtension
|
||||
{
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
@@ -51,8 +51,7 @@ namespace Markdig.Extensions.Bootstrap
|
||||
}
|
||||
else if (node is Inline)
|
||||
{
|
||||
var link = node as LinkInline;
|
||||
if (link != null && link.IsImage)
|
||||
if (node is LinkInline link && link.IsImage)
|
||||
{
|
||||
link.GetAttributes().AddClass("img-fluid");
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ using Markdig.Parsers.Inlines;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html.Inlines;
|
||||
using Markdig.Syntax.Inlines;
|
||||
using System.Diagnostics;
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Markdig.Extensions.Citations
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Syntax;
|
||||
|
||||
@@ -9,8 +10,8 @@ namespace Markdig.Extensions.CustomContainers
|
||||
/// <summary>
|
||||
/// A block custom container.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Syntax.ContainerBlock" />
|
||||
/// <seealso cref="Markdig.Syntax.IFencedBlock" />
|
||||
/// <seealso cref="ContainerBlock" />
|
||||
/// <seealso cref="IFencedBlock" />
|
||||
public class CustomContainer : ContainerBlock, IFencedBlock
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Parsers.Inlines;
|
||||
using Markdig.Renderers;
|
||||
|
||||
using Markdig.Renderers;
|
||||
|
||||
namespace Markdig.Extensions.CustomContainers
|
||||
{
|
||||
/// <summary>
|
||||
@@ -27,12 +27,12 @@ namespace Markdig.Extensions.CustomContainers
|
||||
{
|
||||
inlineParser.EmphasisDescriptors.Add(new EmphasisDescriptor(':', 2, 2, true));
|
||||
inlineParser.TryCreateEmphasisInlineList.Add((emphasisChar, delimiterCount) =>
|
||||
{
|
||||
if (delimiterCount == 2 && emphasisChar == ':')
|
||||
{
|
||||
return new CustomContainerInline();
|
||||
}
|
||||
return null;
|
||||
{
|
||||
if (delimiterCount == 2 && emphasisChar == ':')
|
||||
{
|
||||
return new CustomContainerInline();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.CustomContainers
|
||||
@@ -8,8 +9,8 @@ namespace Markdig.Extensions.CustomContainers
|
||||
/// <summary>
|
||||
/// An inline custom container
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Syntax.Inlines.ContainerInline" />
|
||||
/// <seealso cref="Markdig.Syntax.Inlines.EmphasisInline" />
|
||||
/// <seealso cref="ContainerInline" />
|
||||
/// <seealso cref="EmphasisInline" />
|
||||
public class CustomContainerInline : EmphasisInline
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Parsers;
|
||||
|
||||
namespace Markdig.Extensions.CustomContainers
|
||||
@@ -8,7 +9,7 @@ namespace Markdig.Extensions.CustomContainers
|
||||
/// <summary>
|
||||
/// The block parser for a <see cref="CustomContainer"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Parsers.FencedBlockParserBase{Markdig.Extensions.CustomContainers.CustomContainer}" />
|
||||
/// <seealso cref="FencedBlockParserBase{CustomContainer}" />
|
||||
public class CustomContainerParser : FencedBlockParserBase<CustomContainer>
|
||||
{
|
||||
/// <summary>
|
||||
|
||||