├── CommonMark.Console
├── Sample.txt
├── App.config
├── App.nuget.config
├── Properties
│ └── AssemblyInfo.cs
├── CommonMark.Console.csproj
└── Program.cs
├── CommonMark
├── Properties
│ ├── public.snk
│ └── AssemblyInfo.cs
├── Syntax
│ ├── ListDelimiter.cs
│ ├── ListType.cs
│ ├── DocumentData.cs
│ ├── HeadingData.cs
│ ├── EmphasisData.cs
│ ├── InlineContentLinkable.cs
│ ├── FencedCodeData.cs
│ ├── StringContentPart.cs
│ ├── HtmlBlockType.cs
│ ├── ListData.cs
│ ├── Reference.cs
│ ├── BlockTag.cs
│ ├── InlineTag.cs
│ ├── EnumeratorEntry.cs
│ ├── Enumerable.cs
│ ├── Inline.cs
│ └── Block.cs
├── CommonMark.NET20.csproj
├── CommonMark.NET45.csproj
├── CommonMark.NET35.csproj
├── CommonMark.NET40.csproj
├── CommonMark.Profile259.csproj
├── Func.cs
├── OutputFormat.cs
├── Parser
│ ├── PositionOffset.cs
│ ├── LineInfo.cs
│ ├── Subject.cs
│ ├── TabTextReader.cs
│ ├── PositionTracker.cs
│ ├── ScannerCharacterMatcher.cs
│ └── InlineStack.cs
├── CommonMarkAdditionalFeatures.cs
├── CommonMark.Targets.csproj
├── Lazy.cs
├── CommonMark.Base.csproj
├── Utilities.cs
├── CommonMarkException.cs
├── Formatters
│ └── HtmlTextWriter.cs
└── CommonMarkSettings.cs
├── global.json
├── appveyor.yml
├── CommonMark.Tests
├── HeadingTests.cs
├── SettingsTests.cs
├── Properties
│ └── AssemblyInfo.cs
├── GeneralTests.cs
├── HtmlTests.cs
├── UrlTests.cs
├── ListTests.cs
├── EmphasisTests.cs
├── EnumeratorTests.cs
├── DelimiterCharTests.cs
├── Helpers.cs
├── StrikethroughTests.cs
├── CommonMark.Tests.csproj
└── Specification
│ └── Specs.tt
├── CommonMark.NETCore
├── project.json
└── CommonMark.NETCore.xproj
├── LICENSE.md
├── CommonMark.NET.nuspec
├── .gitattributes
├── .gitignore
├── README.md
├── runtests.pl
└── CommonMark.sln
/CommonMark.Console/Sample.txt:
--------------------------------------------------------------------------------
1 | [CommonMark] [1]
2 |
3 | [1]: http://commonmark.org/
--------------------------------------------------------------------------------
/CommonMark/Properties/public.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/josephwoodward/CommonMark.NET/master/CommonMark/Properties/public.snk
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "1.0.0-beta8",
4 | "runtime": "coreclr",
5 | "architecture": "x86"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/CommonMark.Console/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 1.0.{build}
2 | os: Visual Studio 2015
3 | configuration:
4 | - Release
5 | - Debug
6 | environment:
7 | DNX_FEED: https://www.nuget.org/api/v2
8 | install:
9 | - cmd: >-
10 | set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH%
11 |
12 | dnvm install latest -r coreclr -a x86 -NoNative
13 |
14 | dnvm install latest -r clr -a x86 -NoNative
15 |
16 | dnu restore
17 | build:
18 | parallel: true
19 | verbosity: minimal
--------------------------------------------------------------------------------
/CommonMark/Syntax/ListDelimiter.cs:
--------------------------------------------------------------------------------
1 | namespace CommonMark.Syntax
2 | {
3 | ///
4 | /// Defines the delimiter used in the source for ordered lists.
5 | ///
6 | public enum ListDelimiter : byte
7 | {
8 | ///
9 | /// The item numbering is followed with a period (1. foo ).
10 | ///
11 | Period = 0,
12 |
13 | ///
14 | /// The item numbering is followed with a closing parenthesis (1) foo ).
15 | ///
16 | Parenthesis
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/CommonMark.Console/App.nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/CommonMark.Tests/HeadingTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 |
3 | namespace CommonMark.Tests
4 | {
5 | [TestClass]
6 | public class HeadingTests
7 | {
8 | [TestMethod]
9 | [TestCategory("Leaf blocks - ATX headings")]
10 | public void HeadingsAndThematicBreaks()
11 | {
12 | // see https://github.com/Knagis/CommonMark.NET/issues/60
13 | Helpers.ExecuteTest("##### A\n---\n\n##### B\n---\n\n##### C\n---", "
A \n \nB \n \nC \n \n");
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/CommonMark/CommonMark.NET20.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | v2.0
5 | {075E7F00-F1C0-49AA-808D-8B15F1A82DDE}
6 | v2.0
7 | OptimizeFor20
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/CommonMark/CommonMark.NET45.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | v4.5
5 | v4.5
6 | {40AB0D9C-9464-45B2-AC79-72A566FA1371}
7 | OptimizeFor45
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/CommonMark/CommonMark.NET35.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | v3.5
5 | {61829669-5091-4F2A-A0B1-7348D7CED840}
6 | v3.5
7 | Client
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/CommonMark/CommonMark.NET40.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | v4.0
5 | v4.0
6 | Client
7 | {0176CB03-177C-464A-A155-089595E77520}
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/CommonMark/CommonMark.Profile259.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | portable_259
5 | v4.5
6 | Profile259
7 | {E42ABBF6-884C-4D2C-B21D-B96D8B42F8FE}
8 | OptimizeFor45
9 |
10 |
11 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/ListType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark.Syntax
6 | {
7 | ///
8 | /// Defines the type of a list block element.
9 | ///
10 | public enum ListType
11 | {
12 | ///
13 | /// The list is unordered and its items are represented with bullets.
14 | ///
15 | Bullet = 0,
16 |
17 | ///
18 | /// The list is ordered and its items are numbered.
19 | ///
20 | Ordered
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/CommonMark/Func.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CommonMark
4 | {
5 | #if v2_0
6 | /// An alternative to System.Func which is not present in .NET 2.0.
7 | public delegate TResult Func();
8 |
9 | /// An alternative to System.Func which is not present in .NET 2.0.
10 | public delegate TResult Func(T arg);
11 |
12 | /// An alternative to System.Action which is not present in .NET 2.0.
13 | public delegate void Action(T1 arg1, T2 arg2, T3 arg3);
14 | #endif
15 | }
16 |
--------------------------------------------------------------------------------
/CommonMark.NETCore/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.12.0",
3 | "frameworks": {
4 | "dnxcore50": {
5 | "compilationOptions": {
6 | "define": [
7 | "NETCore",
8 | "OptimizeFor45"
9 | ]
10 | },
11 | "dependencies": {
12 | "System.IO": "4.0.10",
13 | "System.Runtime": "4.0.20",
14 | "System.Reflection": "4.0.10",
15 | "System.Collections": "4.0.10",
16 | "System.Diagnostics.Debug": "4.0.10",
17 | "System.Globalization.Extensions": "4.0.0"
18 | }
19 | }
20 | },
21 | "compile": "../CommonMark/**/*.cs"
22 | }
--------------------------------------------------------------------------------
/CommonMark/Syntax/DocumentData.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace CommonMark.Syntax
4 | {
5 | ///
6 | /// Contains additional data for document elements. Used in the property.
7 | ///
8 | public class DocumentData
9 | {
10 | ///
11 | /// Gets or sets the dictionary containing resolved link references. Only set on the document node, null
12 | /// and not used for all other elements.
13 | ///
14 | public Dictionary ReferenceMap { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/CommonMark.Tests/SettingsTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.VisualStudio.TestTools.UnitTesting;
7 |
8 | namespace CommonMark.Tests
9 | {
10 | [TestClass]
11 | public class SettingsTests
12 | {
13 | [TestMethod]
14 | [TestCategory("Inlines - Soft line break")]
15 | public void RenderSoftLineBreakAsLineBreak()
16 | {
17 | var settings = CommonMarkSettings.Default.Clone();
18 | settings.RenderSoftLineBreaksAsLineBreaks = true;
19 | Helpers.ExecuteTest("A\nB\nC", "A \nB \nC
", settings);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/HeadingData.cs:
--------------------------------------------------------------------------------
1 | namespace CommonMark.Syntax
2 | {
3 | ///
4 | /// Contains additional data for heading elements. Used in the property.
5 | ///
6 | public struct HeadingData
7 | {
8 | ///
9 | /// Initializes a new instance of the structure.
10 | ///
11 | /// Heading level.
12 | public HeadingData(int level)
13 | {
14 | Level = level <= byte.MaxValue ? (byte)level : byte.MaxValue;
15 | }
16 |
17 | ///
18 | /// Gets or sets the heading level.
19 | ///
20 | public byte Level { get; set; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/EmphasisData.cs:
--------------------------------------------------------------------------------
1 | namespace CommonMark.Syntax
2 | {
3 | ///
4 | /// Contains additional data for emphasis elements. Used in the property.
5 | ///
6 | public struct EmphasisData
7 | {
8 | ///
9 | /// Initializes a new instance of the struccture.
10 | ///
11 | /// Delimiter character.
12 | public EmphasisData(char delimiterCharacter)
13 | {
14 | DelimiterCharacter = delimiterCharacter;
15 | }
16 |
17 | ///
18 | /// Gets or sets the delimiter character.
19 | ///
20 | public char DelimiterCharacter { get; set; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/CommonMark/OutputFormat.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark
6 | {
7 | ///
8 | /// Specifies different formatters supported by the converter.
9 | ///
10 | public enum OutputFormat
11 | {
12 | ///
13 | /// The output is standard HTML format according to the CommonMark specification.
14 | ///
15 | Html,
16 |
17 | ///
18 | /// The output is a debug view of the syntax tree. Usable for debugging.
19 | ///
20 | SyntaxTree,
21 |
22 | ///
23 | /// The output is written using a delegate function specified in .
24 | ///
25 | CustomDelegate
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/CommonMark/Parser/PositionOffset.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark.Parser
6 | {
7 | internal struct PositionOffset
8 | {
9 | public PositionOffset(int position, int offset)
10 | {
11 | this.Position = position;
12 | this.Offset = offset;
13 | }
14 |
15 | public int Position;
16 | public int Offset;
17 |
18 | public override string ToString()
19 | {
20 | if (this.Offset == 0)
21 | return "empty";
22 |
23 | return (this.Offset < 0 ? string.Empty : "+")
24 | + this.Offset.ToString(System.Globalization.CultureInfo.InvariantCulture)
25 | + " chars @ " + this.Position.ToString(System.Globalization.CultureInfo.InvariantCulture);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/InlineContentLinkable.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark.Syntax
6 | {
7 | ///
8 | /// An obsolete class. Used to contain properties specific to link inline elements.
9 | ///
10 | [Obsolete("These properties have been moved directly into the Inline element.")]
11 | [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
12 | public sealed class InlineContentLinkable
13 | {
14 | ///
15 | /// Gets or sets the URL of a link. Moved to .
16 | ///
17 | public string Url { get; set; }
18 |
19 | ///
20 | /// Gets or sets the title of a link. Moved to .
21 | ///
22 | public string Title { get; set; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/CommonMark/CommonMarkAdditionalFeatures.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CommonMark
4 | {
5 | ///
6 | /// Lists additional features that can be enabled in .
7 | /// These features are not part of the standard and should not be used if interoperability with other
8 | /// CommonMark implementations is required.
9 | ///
10 | [Flags]
11 | public enum CommonMarkAdditionalFeatures
12 | {
13 | ///
14 | /// No additional features are enabled. This is the default.
15 | ///
16 | None = 0,
17 |
18 | ///
19 | /// The parser will recognize syntax ~~foo~~ that will be rendered as <del>foo</del> .
20 | ///
21 | StrikethroughTilde = 1,
22 |
23 | ///
24 | /// All additional features are enabled.
25 | ///
26 | All = 0x7FFFFFFF
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/CommonMark/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Runtime.CompilerServices;
4 |
5 | [assembly: AssemblyTitle("CommonMark.NET")]
6 | [assembly: AssemblyDescription("https://github.com/Knagis/CommonMark.NET")]
7 | [assembly: AssemblyConfiguration("")]
8 | [assembly: AssemblyCompany("")]
9 | [assembly: AssemblyProduct("CommonMark.NET")]
10 | [assembly: AssemblyCopyright("Copyright © Kārlis Gaņģis 2014-2015")]
11 | [assembly: AssemblyTrademark("")]
12 | [assembly: AssemblyCulture("")]
13 |
14 | // NETCore project gets the assembly version from project.json file.
15 | #if !NETCore
16 | [assembly: AssemblyVersion("0.1.0.0")]
17 | [assembly: AssemblyFileVersion("0.1.0.0")]
18 | #endif
19 |
20 | [assembly: CLSCompliant(true)]
21 |
22 | [assembly: InternalsVisibleTo("CommonMark.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d3a4c92a527690d5bb3b30c00821eb1346022f309ea9b069e4aae5187bb1336b2c16f4333ec41a71824aecbbb0205a76b4abe426be615d2bc254549a99f5caeda5a682773da4961419520dcafbd61e81b4e85b6a5d2a18b4ff52661cf88548a7559338daff82758d83bd00423fd5a8e4122ba0899e678045e4ff5c1d165475c4")]
--------------------------------------------------------------------------------
/CommonMark/Syntax/FencedCodeData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark.Syntax
6 | {
7 | ///
8 | /// Contains additional data for fenced code blocks. Used in /
9 | ///
10 | public sealed class FencedCodeData
11 | {
12 | ///
13 | /// Gets or sets the number of characters that were used in the opening code fence.
14 | ///
15 | public int FenceLength { get; set; }
16 |
17 | ///
18 | /// Gets or sets the number of spaces the opening fence was indented.
19 | ///
20 | public int FenceOffset { get; set; }
21 |
22 | ///
23 | /// Gets or sets the character that is used in the fences.
24 | ///
25 | public char FenceChar { get; set; }
26 |
27 | ///
28 | /// Gets or sets the additional information that was present in the same line as the opening fence.
29 | ///
30 | public string Info { get; set; }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/StringContentPart.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark.Syntax
6 | {
7 | ///
8 | /// Represents a part of .
9 | ///
10 | internal struct StringPart
11 | {
12 | public StringPart(string source, int startIndex, int length)
13 | {
14 | this.Source = source;
15 | this.StartIndex = startIndex;
16 | this.Length = length;
17 | }
18 |
19 | ///
20 | /// Gets or sets the string object this part is created from.
21 | ///
22 | public string Source;
23 |
24 | ///
25 | /// Gets or sets the index at which this part starts.
26 | ///
27 | public int StartIndex;
28 |
29 | ///
30 | /// Gets or sets the length of the part.
31 | ///
32 | public int Length;
33 |
34 | public override string ToString()
35 | {
36 | if (this.Source == null)
37 | return null;
38 |
39 | return this.Source.Substring(this.StartIndex, this.Length);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/CommonMark/CommonMark.Targets.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | bin\$(BuildMode)\$(Configuration)\
5 | obj\$(BuildMode)\
6 | $(TargetFrameworkVersionEx)
7 | $(TargetFrameworkProfileEx)
8 |
9 | $(OutputPath)\CommonMark.xml
10 | $(DefineConstants);$(BuildMode.Replace('.', '_'));$(DefineConstantsEx)
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/CommonMark.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("CommonMark.Tests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("CommonMark.Tests")]
13 | [assembly: AssemblyCopyright("Copyright © Kārlis Gaņģis 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("63cd64df-e46c-439d-942a-e0c461ff06c6")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/CommonMark.Console/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("CommonMark.NET Console")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("CommonMark.NET")]
13 | [assembly: AssemblyCopyright("Copyright © Kārlis Gaņģis 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("c23df822-e0ff-4545-af75-6db40fa843ce")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014, Kārlis Gaņģis
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in the
12 | documentation and/or other materials provided with the distribution.
13 |
14 | * Neither the name of Kārlis Gaņģis nor the names of other contributors
15 | may be used to endorse or promote products derived from this software
16 | without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/CommonMark/Lazy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CommonMark
4 | {
5 | #if v2_0 || v3_5
6 | enum LazyThreadSafetyMode
7 | {
8 | None,
9 | PublicationOnly,
10 | ExecutionAndPublication
11 | }
12 |
13 | class Lazy
14 | {
15 | private readonly Func valueFactory;
16 | private readonly bool isThreadSafe;
17 | private readonly object _lock = new object();
18 | private T value;
19 |
20 | public Lazy(Func valueFactory)
21 | : this(valueFactory, true)
22 | {
23 | }
24 |
25 | public Lazy(Func valueFactory, LazyThreadSafetyMode mode)
26 | : this(valueFactory, mode != LazyThreadSafetyMode.None)
27 | {
28 | }
29 |
30 | public Lazy(Func valueFactory, bool isThreadSafe)
31 | {
32 | this.valueFactory = valueFactory;
33 | this.isThreadSafe = isThreadSafe;
34 | }
35 |
36 | public T Value
37 | {
38 | get
39 | {
40 | if (value == null)
41 | {
42 | if (!isThreadSafe)
43 | {
44 | return value = valueFactory();
45 | }
46 | lock (_lock)
47 | {
48 | if (value == null)
49 | {
50 | value = valueFactory();
51 | }
52 | }
53 | }
54 | return value;
55 | }
56 | }
57 | }
58 | #endif
59 | }
60 |
--------------------------------------------------------------------------------
/CommonMark.NETCore/CommonMark.NETCore.xproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 14.0
5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
6 |
7 |
8 |
9 | 47c48819-62b7-4bd3-89ff-ba9a0b16c8fb
10 | CommonMark.NETCore
11 | obj\core\
12 | bin\core\
13 |
14 |
15 | CommonMark
16 |
17 |
18 | 2.0
19 |
20 |
21 | True
22 |
23 |
24 | True
25 |
26 |
27 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/HtmlBlockType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark.Syntax
6 | {
7 | ///
8 | /// Specifies the type of the HTML block a instance represents.
9 | /// See http://spec.commonmark.org/0.22/#html-block
10 | ///
11 | public enum HtmlBlockType
12 | {
13 | ///
14 | /// This is not a HTML block.
15 | ///
16 | None = 0,
17 |
18 | ///
19 | /// The HTML block represents script , pre or style element. Unline other HTML tags
20 | /// these are allowed to contain blank lines.
21 | ///
22 | InterruptingBlockWithEmptyLines = 1,
23 |
24 | ///
25 | /// The block represents an HTML comment.
26 | ///
27 | Comment = 2,
28 |
29 | ///
30 | /// The block represents a processing instruction <??>
31 | ///
32 | ProcessingInstruction = 3,
33 |
34 | ///
35 | /// The block represents a doctype element <!...>
36 | ///
37 | DocumentType = 4,
38 |
39 | ///
40 | /// The block represents <![CDATA[...]] element.
41 | ///
42 | CData = 5,
43 |
44 | ///
45 | /// This HTML block can interrupt paragraphs.
46 | ///
47 | InterruptingBlock = 6,
48 |
49 | ///
50 | /// This HTML block cannot interrupt paragraphs.
51 | ///
52 | NonInterruptingBlock = 7,
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/CommonMark/Parser/LineInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark.Parser
6 | {
7 | internal sealed class LineInfo
8 | {
9 | public LineInfo(bool trackPositions)
10 | {
11 | this.IsTrackingPositions = trackPositions;
12 | }
13 |
14 | public readonly bool IsTrackingPositions;
15 |
16 | public string Line;
17 |
18 | ///
19 | /// Gets or sets the offset in the source data at which the current line starts.
20 | ///
21 | public int LineOffset;
22 |
23 | public int LineNumber;
24 |
25 | public PositionOffset[] Offsets = new PositionOffset[20];
26 |
27 | public int OffsetCount;
28 |
29 | public void AddOffset(int position, int offset)
30 | {
31 | if (offset == 0)
32 | return;
33 |
34 | if (this.OffsetCount == this.Offsets.Length)
35 | Array.Resize(ref this.Offsets, this.OffsetCount + 20);
36 |
37 | this.Offsets[this.OffsetCount++] = new PositionOffset(position, offset);
38 | }
39 |
40 | public override string ToString()
41 | {
42 | string ln;
43 | if (this.Line == null)
44 | ln = string.Empty;
45 | else if (this.Line.Length < 50)
46 | ln = this.Line;
47 | else
48 | ln = this.Line.Substring(0, 49) + "…";
49 |
50 | return this.LineNumber.ToString(System.Globalization.CultureInfo.InvariantCulture)
51 | + ": " + ln;
52 | }
53 |
54 | public int CalculateOrigin(int position, bool isStartPosition)
55 | {
56 | return PositionTracker.CalculateOrigin(this.Offsets, this.OffsetCount, this.LineOffset + position, true, isStartPosition);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/CommonMark.Tests/GeneralTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.VisualStudio.TestTools.UnitTesting;
7 |
8 | namespace CommonMark.Tests
9 | {
10 | [TestClass]
11 | public class GeneralTests
12 | {
13 | ///
14 | /// Verifies that the U+0000 characters are removed from the source data.
15 | ///
16 | [TestMethod]
17 | [TestCategory("Security")]
18 | public void TestZeroCharRemoval()
19 | {
20 | Helpers.ExecuteTest("\u0000*foo*\0", "\uFFFDfoo \uFFFD
");
21 | }
22 |
23 | [TestMethod]
24 | [TestCategory("Other")]
25 | public void EmptyString()
26 | {
27 | Helpers.ExecuteTest("", "");
28 | }
29 |
30 | ///
31 | /// Verifies that the U+0000 characters are removed from the source data.
32 | ///
33 | [TestMethod]
34 | [TestCategory("Other")]
35 | public void Version()
36 | {
37 | var version = CommonMarkConverter.Version;
38 |
39 | Assert.AreEqual(0, version.Major, "The version number is incorrect: {0}", version);
40 | Assert.IsTrue(version.Minor > 5, "The version number is incorrect: {0}", version);
41 | }
42 |
43 | [TestMethod]
44 | [TestCategory("CommonMarkConverter")]
45 | public void ConvertShortcutMethod()
46 | {
47 | var expected = "foo
";
48 | var result = CommonMarkConverter.Convert("**foo**");
49 |
50 | // Assert
51 | Helpers.LogValue("Expected", expected);
52 | Helpers.LogValue("Actual", result);
53 | Assert.AreEqual(Helpers.Tidy(expected), Helpers.Tidy(result));
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/ListData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark.Syntax
6 | {
7 | ///
8 | /// Contains additional data for list block elements. Used in property.
9 | ///
10 | public sealed class ListData
11 | {
12 | ///
13 | /// Gets or sets the number of spaces the list markers are indented.
14 | ///
15 | public int MarkerOffset { get; set; }
16 |
17 | ///
18 | /// Gets or sets the position of the list item contents in the source text line.
19 | ///
20 | public int Padding { get; set; }
21 |
22 | ///
23 | /// Gets or sets the number for the first list item if is set to
24 | /// .
25 | ///
26 | public int Start { get; set; }
27 |
28 | ///
29 | /// Gets or sets the character used for unordered lists. Used if is set to
30 | /// .
31 | ///
32 | public char BulletChar { get; set; }
33 |
34 | ///
35 | /// Gets or sets the type (ordered or unordered) of this list.
36 | ///
37 | public ListType ListType { get; set; }
38 |
39 | ///
40 | /// Gets or sets the character that follows the number if is set to
41 | /// .
42 | ///
43 | public ListDelimiter Delimiter { get; set; }
44 |
45 | ///
46 | /// Gets or sets a value indicating whether the list is tight (such list will not render additional explicit
47 | /// paragraph elements).
48 | ///
49 | public bool IsTight { get; set; }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/CommonMark.Tests/HtmlTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.VisualStudio.TestTools.UnitTesting;
7 |
8 | namespace CommonMark.Tests
9 | {
10 | [TestClass]
11 | public class HtmlTests
12 | {
13 | [TestMethod]
14 | [TestCategory("Inlines - Raw HTML")]
15 | public void HtmlTagsWithDigits()
16 | {
17 | Helpers.ExecuteTest("foo asd bar", "foo
asd bar");
18 | }
19 |
20 | [TestMethod]
21 | [TestCategory("Inlines - Raw HTML")]
22 | public void HtmlTagAttributes()
23 | {
24 | Helpers.ExecuteTest(
25 | "foo <čā\uD83D\uDF13\">asd bar",
26 | "foo <čā\uD83D\uDF13\">asd bar
");
27 | }
28 |
29 | [TestMethod]
30 | [TestCategory("Inlines - Raw HTML")]
31 | public void HtmlTagAttributesLink()
32 | {
33 | Helpers.ExecuteTest(
34 | "foo asd bar",
35 | "foo asd bar
");
36 | }
37 |
38 | ///
39 | /// Tests HTML block tag names of various lengths (see https://github.com/Knagis/CommonMark.NET/issues/16)
40 | ///
41 | [TestMethod]
42 | [TestCategory("Leaf blocks - HTML blocks")]
43 | public void HtmlTagNameLengths()
44 | {
45 | var source = "";
46 | var result = "";
47 | foreach (var tag in new[] { "p", "h1", "map", "form", "style", "object", "section", "progress", "progress2", "blockquote", "blockquoteX" })
48 | {
49 | source += "<" + tag + ">\n\t*" + tag + "*\n" + tag + ">\n\n";
50 | result += "<" + tag + ">\n\t*" + tag + "*\n" + tag + ">\n";
51 | }
52 |
53 | Helpers.ExecuteTest(source, result);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/Reference.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark.Syntax
6 | {
7 | ///
8 | /// Represents a parsed reference link definition.
9 | ///
10 | public sealed class Reference
11 | {
12 | ///
13 | /// Initializes a new instance of the class.
14 | ///
15 | public Reference()
16 | {
17 | }
18 |
19 | ///
20 | /// Initializes a new instance of the class.
21 | ///
22 | public Reference(string label, string url, string title)
23 | {
24 | this.Label = label;
25 | this.Url = url;
26 | this.Title = title;
27 | }
28 |
29 | ///
30 | /// Represents the maximum allowed length of a reference definition (foo in [foo]: /url ).
31 | ///
32 | public const int MaximumReferenceLabelLength = 1000;
33 |
34 | ///
35 | /// A special constant reference that represents an collapsed reference link: [foo][]
36 | ///
37 | internal static readonly Reference SelfReference = new Reference();
38 |
39 | ///
40 | /// A special constant reference that signifies that the reference label was not found: [foo][bar]
41 | ///
42 | internal static readonly Reference InvalidReference = new Reference();
43 |
44 | ///
45 | /// Gets or sets the label (the key by which it is referenced in the mapping) of the reference.
46 | ///
47 | public string Label { get; set; }
48 |
49 | ///
50 | /// Gets or sets the URL of the reference.
51 | ///
52 | public string Url { get; set; }
53 |
54 | ///
55 | /// Gets or sets the title of the reference (used in <a title="..."> ).
56 | ///
57 | public string Title { get; set; }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/CommonMark.Tests/UrlTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.VisualStudio.TestTools.UnitTesting;
7 |
8 | namespace CommonMark.Tests
9 | {
10 | [TestClass]
11 | public class UrlTests
12 | {
13 | [TestMethod]
14 | [TestCategory("Inlines - Links")]
15 | public void NewlineInUrl()
16 | {
17 | Helpers.ExecuteTest("[foo](\r\n/url)\r\n\r\n[foo](", "foo
\r\n[foo](
");
18 | }
19 |
20 | [TestMethod]
21 | [TestCategory("Inlines - Links")]
22 | public void UTF32CharacterEscape()
23 | {
24 | Helpers.ExecuteTest("[foo](/\uD835\uDD6B\uD835\uDCB5)", "foo
");
25 | }
26 |
27 | [TestMethod]
28 | [TestCategory("Inlines - Links")]
29 | public void CustomUriResolver()
30 | {
31 | var settings = CommonMarkSettings.Default.Clone();
32 | settings.UriResolver = url => url.Replace("~/", "/app/dir/");
33 | Helpers.ExecuteTest("foo␣**[a](~/temp)**␣[b][c]␣\r\n\r\n[c]:␣~/bar", "foo a b
", settings);
34 | }
35 |
36 | [TestMethod]
37 | [TestCategory("Inlines - Links")]
38 | public void CustomUriResolverException()
39 | {
40 | var settings = CommonMarkSettings.Default.Clone();
41 | settings.UriResolver = url => { throw new ArgumentNullException(); };
42 |
43 | var gotException = false;
44 | try
45 | {
46 | Helpers.ExecuteTest("foo␣**[a](~/temp)**␣[b][c]␣\r\n\r\n[c]:␣~/bar", "foo a b
", settings);
47 | }
48 | catch(CommonMarkException)
49 | {
50 | gotException = true;
51 | }
52 |
53 | Assert.IsTrue(gotException, "A required exception was not thrown.");
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/CommonMark.NET.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CommonMark.NET
5 | 0.12.0
6 | CommonMark.NET
7 | Kārlis Gaņģis
8 | https://raw.githubusercontent.com/Knagis/CommonMark.NET/master/LICENSE.md
9 | https://github.com/Knagis/CommonMark.NET/
10 | https://raw.githubusercontent.com/dcurtis/markdown-mark/master/png/32x20.png
11 | false
12 | Fastest .NET library for converting Markdown documents to HTML.
13 | .NET library for converting Markdown documents to HTML according to the CommonMark specification. Optimized for maximum performance and targets every .NET version since 2.0, including PCL, Mono and .NET Core.
14 | Updated to specification 0.25
15 | Copyright © Kārlis Gaņģis 2014-2015
16 | CommonMark Markdown
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/CommonMark.Tests/ListTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.VisualStudio.TestTools.UnitTesting;
7 |
8 | namespace CommonMark.Tests
9 | {
10 | [TestClass]
11 | public class ListTests
12 | {
13 | [TestMethod]
14 | [TestCategory("Container blocks - Lists")]
15 | public void Example210WithPositionTracking()
16 | {
17 | // Example 210 handles case that relies on Block.SourcePosition property (everything else just sets it)
18 |
19 | var s = CommonMarkSettings.Default.Clone();
20 | s.TrackSourcePosition = true;
21 |
22 | Helpers.Log("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 210, "Container blocks - Lists");
23 | Helpers.ExecuteTest("* a\n*\n\n* c", @"
24 |
25 | a
26 |
27 |
28 |
29 | c
30 |
31 |
32 | ", s);
33 | }
34 |
35 | [TestMethod]
36 | [TestCategory("Container blocks - List items")]
37 | public void ListWithTabs()
38 | {
39 | Helpers.ExecuteTest("*\tbar", "");
40 | }
41 |
42 | [TestMethod]
43 | [TestCategory("Container blocks - List items")]
44 | public void UnicodeBulletEscape()
45 | {
46 | Helpers.ExecuteTest("\\• foo\n\n\\* bar", "• foo
\n* bar
");
47 | }
48 |
49 | [TestMethod]
50 | [TestCategory("Container blocks - List items")]
51 | public void UnicodeBulletList()
52 | {
53 | Helpers.ExecuteTest("• foo\n• bar", "");
54 | }
55 |
56 | [TestMethod]
57 | [TestCategory("Container blocks - List items")]
58 | public void EmptyList1()
59 | {
60 | Helpers.ExecuteTest("1.\n2.", "\n \n \n ");
61 | }
62 |
63 | [TestMethod]
64 | [TestCategory("Container blocks - List items")]
65 | public void EmptyList2()
66 | {
67 | Helpers.ExecuteTest("+\n+", "");
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/BlockTag.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CommonMark.Syntax
4 | {
5 | ///
6 | /// Specifies the element type of a instance.
7 | ///
8 | public enum BlockTag : byte
9 | {
10 | ///
11 | /// The root element that represents the document itself. There should only be one in the tree.
12 | ///
13 | Document,
14 |
15 | ///
16 | /// A block-quote element.
17 | ///
18 | BlockQuote,
19 |
20 | ///
21 | /// A list element. Will contain nested blocks with type of .
22 | ///
23 | List,
24 |
25 | ///
26 | /// An item in a block element of type .
27 | ///
28 | ListItem,
29 |
30 | ///
31 | /// A code block element that was formatted with fences (for example, ~~~\nfoo\n~~~ ).
32 | ///
33 | FencedCode,
34 |
35 | ///
36 | /// A code block element that was formatted by indenting the lines with at least 4 spaces.
37 | ///
38 | IndentedCode,
39 |
40 | ///
41 | /// A raw HTML code block element.
42 | ///
43 | HtmlBlock,
44 |
45 | ///
46 | /// A paragraph block element.
47 | ///
48 | Paragraph,
49 |
50 | ///
51 | /// A heading element that was parsed from an ATX style markup (## heading 2 ).
52 | ///
53 | AtxHeading,
54 |
55 | ///
56 | /// Obsolete. Use instead.
57 | ///
58 | [Obsolete("Use " + nameof(AtxHeading) + " instead.")]
59 | AtxHeader = AtxHeading,
60 |
61 | ///
62 | /// A heading element that was parsed from a Setext style markup (heading\n======== ).
63 | ///
64 | SetextHeading,
65 |
66 | ///
67 | /// Obsolete. Use instead.
68 | ///
69 | [Obsolete("Use " + nameof(SetextHeading) + " instead.")]
70 | SETextHeader = SetextHeading,
71 |
72 | ///
73 | /// A thematic break element.
74 | ///
75 | ThematicBreak,
76 |
77 | ///
78 | /// Obsolete. Use instead.
79 | ///
80 | [Obsolete("Use " + nameof(ThematicBreak) + " instead.")]
81 | HorizontalRuler = ThematicBreak,
82 |
83 | ///
84 | /// A text block that contains only link reference definitions.
85 | ///
86 | ReferenceDefinition
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/CommonMark.Tests/EmphasisTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.VisualStudio.TestTools.UnitTesting;
7 |
8 | namespace CommonMark.Tests
9 | {
10 | [TestClass]
11 | public class EmphasisTests
12 | {
13 | [TestMethod]
14 | [TestCategory("Inlines - Emphasis and strong emphasis")]
15 | public void UnderscoreWithinEmphasis()
16 | {
17 | // See https://github.com/jgm/stmd/issues/51 for additional info
18 | // The rule is that inlines are processed left-to-right
19 |
20 | Helpers.ExecuteTest("*_*_", "_ _
");
21 | }
22 |
23 | [TestMethod]
24 | [TestCategory("Inlines - Emphasis and strong emphasis")]
25 | public void UnderscoreWithinEmphasis2()
26 | {
27 | // See https://github.com/jgm/stmd/issues/51 for additional info
28 |
29 | Helpers.ExecuteTest("*a _b _c d_ e*", "a _b c d e
");
30 | }
31 |
32 | [TestMethod]
33 | [TestCategory("Inlines - Emphasis and strong emphasis")]
34 | public void EmphasisWithCommas()
35 | {
36 | Helpers.ExecuteTest("**foo, *bar*, abc**", "foo, bar , abc
");
37 | }
38 |
39 | [TestMethod]
40 | [TestCategory("Inlines - Emphasis and strong emphasis")]
41 | public void DelayedEmphasisMatch1()
42 | {
43 | // '[' char in the middle will delay the ** match to the post-process phase.
44 | Helpers.ExecuteTest("foo ****ba[r****", "foo ba[r
");
45 | }
46 |
47 | [TestMethod]
48 | [TestCategory("Inlines - Emphasis and strong emphasis")]
49 | public void DelayedEmphasisMatch2()
50 | {
51 | // '[' char in the middle will delay the ** match to the post-process phase.
52 | Helpers.ExecuteTest("foo ****ba[r**", "foo **ba[r
");
53 | }
54 |
55 | [TestMethod]
56 | [TestCategory("Inlines - Emphasis and strong emphasis")]
57 | public void DelayedEmphasisMatch3()
58 | {
59 | // '[' char in the middle will delay the ** match to the post-process phase.
60 | Helpers.ExecuteTest("foo **ba[r****", "foo ba[r **
");
61 | }
62 |
63 | [TestMethod]
64 | [TestCategory("Inlines - Emphasis and strong emphasis")]
65 | public void DelayedEmphasisMatch4()
66 | {
67 | Helpers.ExecuteTest("**[foo* bar", "*[foo bar
");
68 | }
69 |
70 | [TestMethod]
71 | [TestCategory("Inlines - Emphasis and strong emphasis")]
72 | public void DelayedEmphasisMatch5()
73 | {
74 | Helpers.ExecuteTest("[a*b**c*]", "[ab c ]
");
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/CommonMark.Console/CommonMark.Console.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {83F44787-27A2-4C18-97E2-497437C8EF75}
8 | Exe
9 | Properties
10 | CommonMark.Console
11 | CommonMark.Console
12 | v4.5
13 | 512
14 |
15 |
16 |
17 |
18 | AnyCPU
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 | false
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 | false
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | {40ab0d9c-9464-45b2-ac79-72a566fa1371}
56 | CommonMark.NET45
57 |
58 |
59 |
60 |
67 |
--------------------------------------------------------------------------------
/CommonMark.Tests/EnumeratorTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.VisualStudio.TestTools.UnitTesting;
7 |
8 | namespace CommonMark.Tests
9 | {
10 | [TestClass]
11 | public class EnumeratorTests
12 | {
13 | [TestMethod]
14 | [TestCategory("Enumerable iterator")]
15 | public void EnumeratorSimple()
16 | {
17 | var doc = Helpers.ParseDocument("> **foo**");
18 |
19 | var inline = doc.AsEnumerable().FirstOrDefault(o => o.Inline != null && o.Inline.Tag == Syntax.InlineTag.Strong);
20 | Assert.IsNotNull(inline);
21 | Assert.IsTrue(inline.IsOpening);
22 | Assert.IsFalse(inline.IsClosing);
23 | }
24 |
25 | [TestMethod]
26 | [TestCategory("Enumerable iterator")]
27 | public void EnumeratorSimple2()
28 | {
29 | var doc = Helpers.ParseDocument("* > **foo** *bar*");
30 |
31 | var inlines = doc.AsEnumerable().Count(o => o.Inline != null && o.IsOpening);
32 | Assert.AreEqual(5, inlines);
33 | }
34 |
35 | [TestMethod]
36 | [TestCategory("Enumerable iterator")]
37 | public void EnumeratorNoEmptyNodes()
38 | {
39 | var doc = Helpers.ParseDocument("* > *[*foo** *bar*");
40 |
41 | var empty = doc.AsEnumerable().FirstOrDefault(o =>
42 | o.Inline != null
43 | && o.Inline.Tag == Syntax.InlineTag.String
44 | && string.IsNullOrEmpty(o.Inline.LiteralContent));
45 |
46 | Assert.IsNull(empty);
47 | }
48 |
49 | [TestMethod]
50 | [TestCategory("Enumerable iterator")]
51 | public void EnumeratorForChildElement()
52 | {
53 | // tests that the sibling elements for the enumarable root are not returned.
54 | var doc = Helpers.ParseDocument("foo\n\nbar");
55 |
56 | var fooNodes = doc.FirstChild.AsEnumerable();
57 | var foostring = fooNodes.FirstOrDefault(o => o.Inline != null && o.Inline.Tag == Syntax.InlineTag.String && o.Inline.LiteralContent == "foo");
58 |
59 | // 3: open para, string foo, close para
60 | Assert.AreEqual(3, fooNodes.Count());
61 | Assert.IsNotNull(foostring);
62 | }
63 |
64 | [TestMethod]
65 | [TestCategory("Enumerable iterator")]
66 | public void EnumeratorSyncOpenClose()
67 | {
68 | var doc = Helpers.ParseDocument("* > **foo** *bar*\n\n\tqwe\n\n* * [asd](/url)");
69 |
70 | var openInl = doc.AsEnumerable().Count(o => o.Inline != null && o.IsOpening);
71 | var openBlo = doc.AsEnumerable().Count(o => o.Block != null && o.IsOpening);
72 | var closeInl = doc.AsEnumerable().Count(o => o.Inline != null && o.IsClosing);
73 | var closeBlo = doc.AsEnumerable().Count(o => o.Block != null && o.IsClosing);
74 |
75 | Assert.AreEqual(openInl, closeInl, "Inlines - opener and closer cound do not match.");
76 | Assert.AreEqual(openBlo, closeBlo, "Blocks - opener and closer cound do not match.");
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 | .vs/
9 |
10 | # Local-history plugin
11 | .localhistory/
12 |
13 | # stmd-master* files - these are downloads from the reference implementation
14 | stmd-master*
15 |
16 | # Build results
17 |
18 | [Dd]ebug/
19 | [Rr]elease/
20 | x64/
21 | build/
22 | [Bb]in/
23 | [Oo]bj/
24 | *.nupkg
25 |
26 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
27 | !packages/*/build/
28 |
29 | # MSTest test Results
30 | [Tt]est[Rr]esult*/
31 | [Bb]uild[Ll]og.*
32 |
33 | *_i.c
34 | *_p.c
35 | *.ilk
36 | *.meta
37 | *.obj
38 | *.pch
39 | *.pdb
40 | *.pgc
41 | *.pgd
42 | *.rsp
43 | *.sbr
44 | *.tlb
45 | *.tli
46 | *.tlh
47 | *.tmp
48 | *.tmp_proj
49 | *.log
50 | *.vspscc
51 | *.vssscc
52 | .builds
53 | *.pidb
54 | *.log
55 | *.scc
56 |
57 | # Visual C++ cache files
58 | ipch/
59 | *.aps
60 | *.ncb
61 | *.opensdf
62 | *.sdf
63 | *.cachefile
64 |
65 | # Visual Studio profiler
66 | *.psess
67 | *.vsp
68 | *.vspx
69 |
70 | # Guidance Automation Toolkit
71 | *.gpState
72 |
73 | # ReSharper is a .NET coding add-in
74 | _ReSharper*/
75 | *.[Rr]e[Ss]harper
76 |
77 | # TeamCity is a build add-in
78 | _TeamCity*
79 |
80 | # DotCover is a Code Coverage Tool
81 | *.dotCover
82 |
83 | # NCrunch
84 | *.ncrunch*
85 | .*crunch*.local.xml
86 |
87 | # Installshield output folder
88 | [Ee]xpress/
89 |
90 | # DocProject is a documentation generator add-in
91 | DocProject/buildhelp/
92 | DocProject/Help/*.HxT
93 | DocProject/Help/*.HxC
94 | DocProject/Help/*.hhc
95 | DocProject/Help/*.hhk
96 | DocProject/Help/*.hhp
97 | DocProject/Help/Html2
98 | DocProject/Help/html
99 |
100 | # Click-Once directory
101 | publish/
102 |
103 | # Publish Web Output
104 | *.Publish.xml
105 |
106 | # NuGet Packages Directory
107 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
108 | #packages/
109 |
110 | # Windows Azure Build Output
111 | csx
112 | *.build.csdef
113 |
114 | # Windows Store app package directory
115 | AppPackages/
116 |
117 | # Others
118 | sql/
119 | *.Cache
120 | ClientBin/
121 | [Ss]tyle[Cc]op.*
122 | ~$*
123 | *~
124 | *.dbmdl
125 | *.[Pp]ublish.xml
126 | *.pfx
127 | *.publishsettings
128 |
129 | # RIA/Silverlight projects
130 | Generated_Code/
131 |
132 | # Backup & report files from converting an old project file to a newer
133 | # Visual Studio version. Backup files are not needed, because we have git ;-)
134 | _UpgradeReport_Files/
135 | Backup*/
136 | UpgradeLog*.XML
137 | UpgradeLog*.htm
138 |
139 | # SQL Server files
140 | App_Data/*.mdf
141 | App_Data/*.ldf
142 |
143 |
144 | #LightSwitch generated files
145 | GeneratedArtifacts/
146 | _Pvt_Extensions/
147 | ModelManifest.xml
148 |
149 | # =========================
150 | # Windows detritus
151 | # =========================
152 |
153 | # Windows image file caches
154 | Thumbs.db
155 | ehthumbs.db
156 |
157 | # Folder config file
158 | Desktop.ini
159 |
160 | # Recycle Bin used on file shares
161 | $RECYCLE.BIN/
162 |
163 | # Mac desktop service store files
164 | .DS_Store
165 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/InlineTag.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CommonMark.Syntax
4 | {
5 | ///
6 | /// Specifies the element type of an instance.
7 | ///
8 | public enum InlineTag : byte
9 | {
10 | ///
11 | /// Represents a simple literal string content. Uses to specify the data.
12 | /// Cannot contain nested elements.
13 | ///
14 | String = 0,
15 |
16 | ///
17 | /// Represents a soft-break which by default is rendered as a simple newline and thus does not impact
18 | /// the display of the resulting HTML code. The
19 | /// property can be used to override this behavior and render soft-breaks as <br;> HTML
20 | /// elements.
21 | /// Cannot contain literal content or nested elements.
22 | ///
23 | SoftBreak,
24 |
25 | ///
26 | /// Represents a line-break which by default is rendered as a <br;> HTML element.
27 | /// Cannot contain literal content or nested elements.
28 | ///
29 | LineBreak,
30 |
31 | ///
32 | /// Represents an inline code element. Uses to specify the data.
33 | /// Cannot contain nested elements.
34 | ///
35 | Code,
36 |
37 | ///
38 | /// Represents raw HTML code. Uses to specify the data.
39 | /// Cannot contain nested elements.
40 | ///
41 | RawHtml,
42 |
43 | ///
44 | /// Represents an emphasis element. Uses to specify the contents.
45 | /// Cannot contain literal content.
46 | ///
47 | Emphasis,
48 |
49 | ///
50 | /// Represents a strong emphasis element. Uses to specify the contents.
51 | /// Cannot contain literal content.
52 | ///
53 | Strong,
54 |
55 | ///
56 | /// Represents a link element. Uses to specify the content (or label).
57 | /// Uses to specify the target of the link and
58 | /// to store the title of the link.
59 | ///
60 | Link,
61 |
62 | ///
63 | /// Represents an image element. Uses to specify the label (description).
64 | /// Uses to specify the source of the image and
65 | /// to store the title of the image.
66 | ///
67 | Image,
68 |
69 | ///
70 | /// Represents an inline element that has been "removed" (visually represented as strikethrough).
71 | /// Only present if the is enabled.
72 | ///
73 | Strikethrough
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/CommonMark.Tests/DelimiterCharTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 |
3 | namespace CommonMark.Tests
4 | {
5 | [TestClass]
6 | public class DelimiterCharTests
7 | {
8 | public DelimiterCharTests()
9 | {
10 | }
11 |
12 | [TestMethod]
13 | public void EmphasisDelimiterChar()
14 | {
15 | var md = "*foo*";
16 | var doc = Helpers.ParseDocument(md);
17 | Assert.AreEqual('*', doc.FirstChild.InlineContent.Emphasis.DelimiterCharacter);
18 | }
19 |
20 | [TestMethod]
21 | public void EmphasisDelimiterChar2()
22 | {
23 | var md = "_foo_";
24 | var doc = Helpers.ParseDocument(md);
25 | Assert.AreEqual('_', doc.FirstChild.InlineContent.Emphasis.DelimiterCharacter);
26 | }
27 |
28 | [TestMethod]
29 | public void EmphasisDelimiterChar3()
30 | {
31 | var md = "_foo[bar_";
32 | var doc = Helpers.ParseDocument(md);
33 | Assert.AreEqual('_', doc.FirstChild.InlineContent.Emphasis.DelimiterCharacter);
34 | }
35 |
36 | [TestMethod]
37 | public void EmphasisDelimiterChar4()
38 | {
39 | var md = "*foo[bar*";
40 | var doc = Helpers.ParseDocument(md);
41 | Assert.AreEqual('*', doc.FirstChild.InlineContent.Emphasis.DelimiterCharacter);
42 | }
43 |
44 | [TestMethod]
45 | public void DoubleEmphasisDelimiterChar()
46 | {
47 | var md = "_*foo*_";
48 | var doc = Helpers.ParseDocument(md);
49 | Assert.AreEqual('*', doc.FirstChild.InlineContent.FirstChild.Emphasis.DelimiterCharacter);
50 | }
51 |
52 | [TestMethod]
53 | public void DoubleEmphasisDelimiterChar2()
54 | {
55 | var md = "*_foo_*";
56 | var doc = Helpers.ParseDocument(md);
57 | Assert.AreEqual('_', doc.FirstChild.InlineContent.FirstChild.Emphasis.DelimiterCharacter);
58 | }
59 |
60 | [TestMethod]
61 | public void DoubleEmphasisDelimiterChar3()
62 | {
63 | var md = "_*foo[bar*_";
64 | var doc = Helpers.ParseDocument(md);
65 | Assert.AreEqual('*', doc.FirstChild.InlineContent.FirstChild.Emphasis.DelimiterCharacter);
66 | }
67 |
68 | [TestMethod]
69 | public void DoubleEmphasisDelimiterChar4()
70 | {
71 | var md = "*_foo[bar_*";
72 | var doc = Helpers.ParseDocument(md);
73 | Assert.AreEqual('_', doc.FirstChild.InlineContent.FirstChild.Emphasis.DelimiterCharacter);
74 | }
75 |
76 | [TestMethod]
77 | public void DoubleEmphasisDelimiterChar5()
78 | {
79 | var md = "_*foo[bar*baz_";
80 | var doc = Helpers.ParseDocument(md);
81 | Assert.AreEqual('*', doc.FirstChild.InlineContent.FirstChild.Emphasis.DelimiterCharacter);
82 | }
83 |
84 | [TestMethod]
85 | public void DoubleEmphasisDelimiterChar6()
86 | {
87 | var md = "*_foo[bar_baz*";
88 | var doc = Helpers.ParseDocument(md);
89 | Assert.AreEqual('_', doc.FirstChild.InlineContent.FirstChild.Emphasis.DelimiterCharacter);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/CommonMark/Parser/Subject.cs:
--------------------------------------------------------------------------------
1 | using CommonMark.Syntax;
2 | using System.Text;
3 |
4 | namespace CommonMark.Parser
5 | {
6 | [System.Diagnostics.DebuggerDisplay("{DebugToString()}")]
7 | internal sealed class Subject
8 | {
9 | ///
10 | /// Initializes a new instance of the class.
11 | ///
12 | /// Document data.
13 | public Subject(DocumentData documentData)
14 | {
15 | this.DocumentData = documentData;
16 | }
17 |
18 | ///
19 | /// Initializes a new instance of the class.
20 | ///
21 | /// String buffer.
22 | /// Document data.
23 | public Subject(string buffer, DocumentData documentData)
24 | {
25 | this.Buffer = buffer;
26 | this.Length = buffer.Length;
27 | this.DocumentData = documentData;
28 | }
29 |
30 | #if DEBUG
31 | public int DebugStartIndex;
32 | #endif
33 |
34 | ///
35 | /// Gets or sets the whole buffer this instance is created over.
36 | ///
37 | public string Buffer;
38 |
39 | ///
40 | /// Gets or sets the current position in the buffer.
41 | ///
42 | public int Position;
43 |
44 | ///
45 | /// Gets or sets the length of the usable buffer. This can be less than the actual length of the
46 | /// buffer if some characters at the end of the buffer have to be ignored.
47 | ///
48 | public int Length;
49 |
50 | ///
51 | /// The last top-level inline parsed from this subject.
52 | ///
53 | public Inline LastInline;
54 |
55 | ///
56 | /// The last entry of the current stack of possible emphasis openers. Can be null .
57 | ///
58 | public InlineStack LastPendingInline;
59 |
60 | ///
61 | /// The first entry of the current stack of possible emphasis openers. Can be null .
62 | ///
63 | public InlineStack FirstPendingInline;
64 |
65 | ///
66 | /// A reusable StringBuilder that should be used instead of creating new instances to conserve memory.
67 | ///
68 | public StringBuilder ReusableStringBuilder = new StringBuilder();
69 |
70 | ///
71 | /// Additional properties that apply to document nodes.
72 | ///
73 | public readonly DocumentData DocumentData;
74 |
75 | #if !NETCore
76 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Used by [DebuggerDisplay]")]
77 | #endif
78 | // ReSharper disable once UnusedMethodReturnValue.Local
79 | private string DebugToString()
80 | {
81 | var res = this.Buffer.Insert(this.Length, "|");
82 | res = res.Insert(this.Position, "⁞");
83 |
84 | #if DEBUG
85 | res = res.Insert(this.DebugStartIndex, "|");
86 | #endif
87 |
88 | return res;
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/CommonMark/Parser/TabTextReader.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 |
4 | namespace CommonMark.Parser
5 | {
6 | internal sealed class TabTextReader
7 | {
8 | private const int _bufferSize = 4000;
9 | private readonly TextReader _inner;
10 | private readonly char[] _buffer;
11 | private int _bufferLength;
12 | private int _bufferPosition;
13 | private int _previousBufferLength;
14 | private readonly StringBuilder _builder;
15 | private bool _endOfStream;
16 |
17 | public TabTextReader(TextReader inner)
18 | {
19 | this._inner = inner;
20 | this._buffer = new char[_bufferSize];
21 | this._builder = new StringBuilder(256);
22 | }
23 |
24 | private bool ReadBuffer()
25 | {
26 | if (this._endOfStream)
27 | return false;
28 |
29 | this._previousBufferLength += this._bufferLength;
30 | this._bufferLength = this._inner.Read(this._buffer, 0, _bufferSize);
31 | this._endOfStream = this._bufferLength == 0;
32 | this._bufferPosition = 0;
33 | return !this._endOfStream;
34 | }
35 |
36 | public void ReadLine(LineInfo line)
37 | {
38 | line.LineOffset = this._previousBufferLength + this._bufferPosition;
39 | line.LineNumber++;
40 | line.OffsetCount = 0;
41 | line.Line = null;
42 | var tabIncreaseCount = 0;
43 |
44 | if (this._bufferPosition == this._bufferLength && !this.ReadBuffer())
45 | return;
46 |
47 | bool useBuilder = false;
48 | int num;
49 | char c;
50 |
51 | while (true)
52 | {
53 | num = this._bufferPosition;
54 | do
55 | {
56 | c = this._buffer[num];
57 | if (c == '\r' || c == '\n')
58 | goto IL_4A;
59 |
60 | if (c == '\0')
61 | this._buffer[num] = '\uFFFD';
62 |
63 | num++;
64 | }
65 | while (num < this._bufferLength);
66 |
67 | num = this._bufferLength - this._bufferPosition;
68 | if (!useBuilder)
69 | {
70 | useBuilder = true;
71 | this._builder.Length = 0;
72 | }
73 |
74 | this._builder.Append(this._buffer, this._bufferPosition, num);
75 | if (!this.ReadBuffer())
76 | {
77 | this._builder.Append('\n');
78 | line.Line = this._builder.ToString();
79 | return;
80 | }
81 | }
82 |
83 | IL_4A:
84 | string result;
85 | this._buffer[num] = '\n';
86 | if (useBuilder)
87 | {
88 | this._builder.Append(this._buffer, this._bufferPosition, num - this._bufferPosition + 1);
89 | result = this._builder.ToString();
90 | }
91 | else
92 | {
93 | result = new string(this._buffer, this._bufferPosition, num - this._bufferPosition + 1);
94 | }
95 |
96 | this._bufferPosition = num + 1;
97 |
98 | if (c == '\r' && (this._bufferPosition < this._bufferLength || this.ReadBuffer()) && this._buffer[this._bufferPosition] == '\n')
99 | {
100 | if (line.IsTrackingPositions)
101 | line.AddOffset(this._previousBufferLength + this._bufferPosition - 1 + tabIncreaseCount, 1);
102 |
103 | this._bufferPosition++;
104 | }
105 |
106 | line.Line = result;
107 | }
108 |
109 | public bool EndOfStream()
110 | {
111 | return this._endOfStream || (this._bufferPosition == this._bufferLength && !this.ReadBuffer());
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://ci.appveyor.com/project/Knagis/commonmark-net/branch/master)
2 |
3 | # CommonMark.NET
4 |
5 | Implementation of [CommonMark] [1] specification (passes tests from version 0.25) in C# for converting Markdown documents to HTML.
6 |
7 | The current version of the library is also [available on NuGet] [nuget].
8 |
9 | ## Usage
10 |
11 | To convert Markdown data in a stream or file:
12 | ```C#
13 | using (var reader = new System.IO.StreamReader("source.md"))
14 | using (var writer = new System.IO.StreamWriter("result.html"))
15 | {
16 | CommonMark.CommonMarkConverter.Convert(reader, writer);
17 | }
18 | ```
19 |
20 | To convert data stored in a string:
21 | ```C#
22 | var result = CommonMark.CommonMarkConverter.Convert("**Hello world!**");
23 | ```
24 |
25 | See [wiki] [extensibility] for an example how the parser can be extended with additional logic.
26 |
27 | **Important:** The converter [does not include any HTML sanitizing][XSS].
28 |
29 | ## Compatibility
30 |
31 | The library uses no references except for `System` - it has no external dependencies. It is cross compiled to:
32 |
33 | * .NET Framework 2.0
34 | * .NET Framework 3.5 Client Profile
35 | * .NET Framework 4.0 Client Profile
36 | * .NET 4.0 Portable Class Library
37 | * .NET Framework 4.0 Client Profile
38 | * Silverlight 5
39 | * Windows 8
40 | * Windows Phone 8.1
41 | * Windows Phone Silverlight 8
42 | * Xamarin.Android
43 | * Xamarin.iOS
44 | * .NET 4.5 Portable Class Library (Optimized)
45 | * .NET Framework 4.5
46 | * Windows 8
47 | * Windows Phone 8.1
48 | * Windows Phone Silverlight 8
49 | * Xamarin.Android
50 | * Xamarin.iOS
51 | * .NET Framework 4.5 (Optimized)
52 | * .NET Framework 5.0 Core (also known as ASP.NET vNext Core CLR)
53 |
54 | For working with the source code you will need Visual Studio 2015 or newer (Community edition is supported).
55 |
56 | ## Performance
57 |
58 | Using a [simple tool][3] to compare the performance of various Markdown implementations for .NET yields the
59 | following results:
60 |
61 | CommonMark.NET 0.4.1 4 ms 7%
62 |
63 | CommonMarkSharp 0.3.2 30 ms 46%
64 | MarkdownSharp 1.13 55 ms 84% (might not conform to CommonMark specification)
65 | MarkdownDeep 1.5 7 ms 11% (might not conform to CommonMark specification)
66 | MoonShine (sundown) 3 ms 6% (wrapper for a native x86 .dll)
67 | Baseline 65 ms 100% (used to compare results on different machines)
68 |
69 | This benchmark is very simple and tests the processing of the CommonMark specification document itself (a
70 | 115 KB file). The results are provided just for a high-level comparison.
71 |
72 | ## Reliability
73 |
74 | The parser uses algorithms that completely avoid recursion so even specifically crafted input documents
75 | will not cause exceptions and will be rendered correctly. This is important because a `StackOverflowException`
76 | cannot be caught and will bring down the entire process.
77 |
78 | ## References
79 |
80 | This library is based on a port of the reference implementation in C, available on [jgm/cmark repo] [2].
81 | It follows the same approach - the source is parsed into a syntax tree that can be used to add custom
82 | processing if the application needs it and then formatted into HTML.
83 |
84 | ## Running tests
85 |
86 | All tests from `spec.txt` are merged into the unit testing project and can be executed from within Visual Studio.
87 |
88 | The project also includes a slightly modified version of `runtests.pl` for compatibility with the original
89 | implementation. Use `CommonMark.Console.exe --perltest` as the argument to `runtests.pl` so that the
90 | application can correctly handle input from the Perl script.
91 |
92 | [1]: http://spec.commonmark.org/
93 | [2]: https://github.com/jgm/cmark
94 | [3]: https://github.com/Knagis/CommonMarkBenchmark
95 | [extensibility]: https://github.com/Knagis/CommonMark.NET/wiki
96 | [XSS]: http://talk.commonmark.org/t/cross-site-scripting-issue-in-standard-markdown-example-at-try-standardmarkdown-com/55
97 | [nuget]: https://www.nuget.org/packages/CommonMark.NET/
98 |
--------------------------------------------------------------------------------
/CommonMark/Parser/PositionTracker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark.Parser
6 | {
7 | internal sealed class PositionTracker
8 | {
9 | public PositionTracker(int blockOffset)
10 | {
11 | this._blockOffset = blockOffset;
12 | }
13 |
14 | private static readonly PositionOffset EmptyPositionOffset = default(PositionOffset);
15 |
16 | private int _blockOffset;
17 |
18 | public void AddBlockOffset(int offset)
19 | {
20 | this._blockOffset += offset;
21 | }
22 |
23 | public void AddOffset(LineInfo line, int startIndex, int length)
24 | {
25 | if (this.OffsetCount + line.OffsetCount + 2 >= this.Offsets.Length)
26 | Array.Resize(ref this.Offsets, this.Offsets.Length + line.OffsetCount + 20);
27 |
28 | PositionOffset po1, po2;
29 |
30 | if (startIndex > 0)
31 | po1 = new PositionOffset(
32 | CalculateOrigin(line.Offsets, line.OffsetCount, line.LineOffset, false, true),
33 | startIndex);
34 | else
35 | po1 = EmptyPositionOffset;
36 |
37 | if (line.Line.Length - startIndex - length > 0)
38 | po2 = new PositionOffset(
39 | CalculateOrigin(line.Offsets, line.OffsetCount, line.LineOffset + startIndex + length, false, true),
40 | line.Line.Length - startIndex - length);
41 | else
42 | po2 = EmptyPositionOffset;
43 |
44 | var indexAfterLastCopied = 0;
45 |
46 | if (po1.Offset == 0)
47 | {
48 | if (po2.Offset == 0)
49 | goto FINTOTAL;
50 |
51 | po1 = po2;
52 | po2 = EmptyPositionOffset;
53 | }
54 |
55 | for (var i = 0; i < line.OffsetCount; i++)
56 | {
57 | var pc = line.Offsets[i];
58 | if (pc.Position > po1.Position)
59 | {
60 | if (i > indexAfterLastCopied)
61 | {
62 | Array.Copy(line.Offsets, indexAfterLastCopied, this.Offsets, this.OffsetCount, i - indexAfterLastCopied);
63 | this.OffsetCount += i - indexAfterLastCopied;
64 | indexAfterLastCopied = i;
65 | }
66 |
67 | this.Offsets[this.OffsetCount++] = po1;
68 |
69 | po1 = po2;
70 |
71 | if (po1.Offset == 0)
72 | goto FIN;
73 |
74 | po2 = EmptyPositionOffset;
75 | }
76 | }
77 |
78 | FIN:
79 | if (po1.Offset != 0)
80 | this.Offsets[this.OffsetCount++] = po1;
81 |
82 | if (po2.Offset != 0)
83 | this.Offsets[this.OffsetCount++] = po2;
84 |
85 | FINTOTAL:
86 | Array.Copy(line.Offsets, indexAfterLastCopied, this.Offsets, this.OffsetCount, line.OffsetCount - indexAfterLastCopied);
87 | this.OffsetCount += line.OffsetCount - indexAfterLastCopied;
88 | }
89 |
90 | public int CalculateInlineOrigin(int position, bool isStartPosition)
91 | {
92 | return CalculateOrigin(this.Offsets, this.OffsetCount, this._blockOffset + position, true, isStartPosition);
93 | }
94 |
95 | internal static int CalculateOrigin(PositionOffset[] offsets, int offsetCount, int position, bool includeReduce, bool isStart)
96 | {
97 | if (isStart)
98 | position++;
99 |
100 | var minus = 0;
101 | for (var i = 0; i < offsetCount; i++)
102 | {
103 | var po = offsets[i];
104 | if (po.Position < position)
105 | {
106 | if (po.Offset > 0)
107 | position += po.Offset;
108 | else
109 | minus += po.Offset;
110 | }
111 | else
112 | break;
113 | }
114 |
115 | if (includeReduce)
116 | position += minus;
117 |
118 | if (isStart)
119 | position--;
120 |
121 | return position;
122 | }
123 |
124 | private PositionOffset[] Offsets = new PositionOffset[10];
125 | private int OffsetCount;
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/CommonMark.Tests/Helpers.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using System;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Text.RegularExpressions;
6 |
7 | namespace CommonMark.Tests
8 | {
9 | internal static class Helpers
10 | {
11 | public static Syntax.Block ParseDocument(string commonMark, CommonMarkSettings settings = null)
12 | {
13 | using (var reader = new System.IO.StringReader(Helpers.Normalize(commonMark)))
14 | {
15 | return CommonMarkConverter.Parse(reader, settings);
16 | }
17 | }
18 |
19 | public static void ExecuteTest(string commonMark, string html, CommonMarkSettings settings = null)
20 | {
21 | if (settings == null)
22 | settings = CommonMarkSettings.Default.Clone();
23 |
24 | Helpers.LogValue("CommonMark", Denormalize(commonMark));
25 | Helpers.LogValue("Expected", Denormalize(html));
26 |
27 | // Arrange
28 | commonMark = Helpers.Normalize(commonMark);
29 | html = Helpers.Normalize(html);
30 |
31 | string actual;
32 | Syntax.Block document;
33 |
34 | // Act
35 | using (var reader = new System.IO.StringReader(commonMark))
36 | using (var writer = new System.IO.StringWriter())
37 | {
38 | document = CommonMarkConverter.ProcessStage1(reader, settings);
39 | CommonMarkConverter.ProcessStage2(document, settings);
40 | CommonMarkConverter.ProcessStage3(document, writer, settings);
41 | actual = writer.ToString();
42 | }
43 |
44 | // Assert
45 | Helpers.LogValue("Actual", Denormalize(actual));
46 | actual = Helpers.Tidy(actual);
47 | Assert.AreEqual(Helpers.Tidy(html), actual);
48 |
49 | // Verify that the extendable HTML formatter returns the same result
50 | var settingsHtmlFormatter = settings.Clone();
51 | settingsHtmlFormatter.OutputDelegate = (doc, target, stngs) => new Formatters.HtmlFormatter(target, stngs).WriteDocument(doc);
52 | var actual2 = CommonMarkConverter.Convert(commonMark, settingsHtmlFormatter);
53 | Assert.AreEqual(actual, Helpers.Tidy(actual2), "HtmlFormatter returned a different result than HtmlFormatterSlim.");
54 |
55 | // Additionally verify that the parser included source position information.
56 | // This is done here to catch cases during specification tests that might not be
57 | // covered in SourcePositionTests.cs.
58 | var firstFail = document.AsEnumerable().FirstOrDefault(o => o.Inline != null && o.IsOpening && o.Inline.SourceLength <= 0);
59 | if (firstFail != null)
60 | {
61 | Assert.Fail("Incorrect source position: " + firstFail);
62 | }
63 | }
64 |
65 | private static string Denormalize(string value)
66 | {
67 | value = value.Replace('\t', '→');
68 | value = value.Replace(' ', '␣');
69 | return value;
70 | }
71 |
72 | private static string Normalize(string value)
73 | {
74 | value = value.Replace('→', '\t');
75 | value = value.Replace('␣', ' ');
76 | return value;
77 | }
78 |
79 | public static string Tidy(string html)
80 | {
81 | html = html.Replace("\r", "").Trim();
82 |
83 | // collapse spaces and newlines before and after
84 | html = Regex.Replace(html, @"\s+ ", "");
85 | html = Regex.Replace(html, @"\s+", " ");
86 |
87 | // needed to compare UTF-32 characters
88 | html = html.Normalize(NormalizationForm.FormKD);
89 | return html;
90 | }
91 |
92 | public static void Log(string format, params object[] args)
93 | {
94 | if (args != null && args.Length > 0)
95 | Console.WriteLine(format, args);
96 | else
97 | Console.WriteLine(format);
98 | }
99 |
100 | public static void LogValue(string caption, string text)
101 | {
102 | Console.Write(caption);
103 | Console.WriteLine(":");
104 | Console.WriteLine();
105 |
106 | Console.Write(" ");
107 | Console.WriteLine(text.Replace("\n", "\n "));
108 |
109 | Console.WriteLine();
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/runtests.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 | use warnings;
3 | use strict;
4 | use Term::ANSIColor;
5 | use IO::Handle;
6 | use IPC::Open2;
7 |
8 | my $usage="runtests.pl SPEC PROGRAM\nSet ANSI_COLORS_DISABLED=1 if you redirect to a file.\nSet PATT='...' to restrict tests to sections matching a regex.\n";
9 |
10 | my $SPEC = shift @ARGV;
11 | my @PROG = @ARGV;
12 | my $PATT=$ENV{'PATT'};
13 |
14 | if (!(@PROG && defined $SPEC)) {
15 | print STDERR $usage;
16 | exit 1;
17 | }
18 |
19 | my $passed = 0;
20 | my $failed = 0;
21 | my $skipped = 0;
22 |
23 | # Markdown implementations vary on insignificant whitespace.
24 | # Some leave blanks between block elements, others don't.
25 | # This function tries to normalize the output so it can be
26 | # compared with our test. tidy takes two arguments: the
27 | # string containing the actual output, and a pathname of the
28 | # file to which the tidied output is to be saved.
29 | sub tidy
30 | {
31 | my $inpre = 0;
32 | my $out = "";
33 | my $outfh;
34 | open($outfh, '>', \$out);
35 | for (split /^/, $_[0]) {
36 | if (/ in tag
53 | s/ *\/>/\/>/;
54 | # add a newline after tag
55 | s/ (.+)/ \n$1/;
56 | # skip blank line
57 | if (/^$/) {
58 | next;
59 | }
60 | print $outfh $_;
61 | }
62 | }
63 | close $outfh;
64 | return $out;
65 | }
66 |
67 | sub dotest
68 | {
69 | my $markdown = $_[0];
70 | my $html = $_[1];
71 | my $testname = $_[2];
72 | my $actual = "";
73 | # We use → to indicate tab and ␣ space in the spec
74 | $markdown =~ s/→/\t/g;s/␣/ /g;
75 | $html =~ s/→/\t/g;s/␣/ /g;
76 | my $pid = open2(my $out, my $in, @PROG);
77 | print $in $markdown;
78 | close $in;
79 | flush $out;
80 | $actual = do { local $/; <$out>; };
81 | close $out;
82 | waitpid($pid, 0);
83 | $html = &tidy($html);
84 | $actual = &tidy($actual);
85 | $actual =~ s/\'/'/g;
86 |
87 | my $expectedLength = length($html);
88 | my $actualLength = length($actual);
89 | if ($actual eq $html) {
90 | print " PASS";
91 | return 1;
92 | } else {
93 | print "\nFAIL $testname";
94 | print "\n";
95 | #print color "cyan";
96 | print "=== markdown ===============\n";
97 | print $markdown;
98 | print "=== expected $expectedLength ===============\n";
99 | print $html;
100 | print "=== got $actualLength ====================\n";
101 | print $actual;
102 | print "=== fin ====================\n";
103 | #print color "black";
104 | return 0;
105 | }
106 | }
107 |
108 | my $stage = 0;
109 | my $markdown = "";
110 | my $html = "";
111 | my $example = 0;
112 | my $linenum = 0;
113 | my $exampleline = 0;
114 | my @secnums = ();
115 | my $secheading;
116 |
117 | open(SPEC, "<", "$SPEC");
118 | while () {
119 | $linenum++;
120 | if (/^\.$/) {
121 | $stage = ($stage + 1) % 3;
122 | if ($stage == 1) {
123 | $exampleline = $linenum;
124 | }
125 | if ($stage == 0) {
126 | $example++;
127 | if (!$PATT || $secheading =~ /$PATT/) {
128 | if (&dotest($markdown, $html,
129 | "Example $example (line $exampleline)")) {
130 | $passed++;
131 | } else {
132 | $failed++;
133 | }
134 | } else {
135 | $skipped++;
136 | }
137 | $markdown = "";
138 | $html = "";
139 | }
140 | } elsif ($stage == 0 && $_ =~ /^/) {
141 | last;
142 | } elsif ($stage == 0 && $_ =~ /^(#+) +(.*)/) {
143 | my $seclevel = length($1);
144 | $secheading = $2;
145 | if ($#secnums == $seclevel - 1) {
146 | $secnums[$#secnums]++;
147 | } elsif ($#secnums > $seclevel - 1) {
148 | @secnums = @secnums[0..($seclevel - 1)];
149 | $secnums[$#secnums]++;
150 | } else {
151 | while ($#secnums < $seclevel - 1) {
152 | push(@secnums, 1);
153 | }
154 | }
155 | if (!$PATT || $secheading =~ /$PATT/) {
156 | print ("\n", join(".", @secnums) . " " . $secheading, " ");
157 | }
158 | } elsif ($stage == 1) {
159 | $markdown .= $_;
160 | } elsif ($stage == 2) {
161 | $html .= $_;
162 | }
163 | }
164 |
165 | print "\n";
166 | print STDERR "$passed tests passed, $failed failed, $skipped skipped.";
167 | print STDERR "\n";
168 | exit $failed;
169 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/EnumeratorEntry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark.Syntax
6 | {
7 | ///
8 | /// Represents a single element in the document tree when traversing through it with the enumerator.
9 | ///
10 | ///
11 | public sealed class EnumeratorEntry
12 | {
13 | ///
14 | /// Initializes a new instance of the class.
15 | ///
16 | /// Specifies if this instance represents the opening of the block element.
17 | /// Specifies if this instance represents the closing of the block element (returned by the
18 | /// enumerator after the children have been enumerated). Both and
19 | /// can be specified at the same time if there are no children for the element.
20 | /// The block element being returned from the enumerator.
21 | public EnumeratorEntry(bool opening, bool closing, Block block)
22 | {
23 | this.IsOpening = opening;
24 | this.IsClosing = closing;
25 | this.Block = block;
26 | }
27 |
28 | ///
29 | /// Initializes a new instance of the class.
30 | ///
31 | /// Specifies if this instance represents the opening of the inline element.
32 | /// Specifies if this instance represents the closing of the inline element (returned by the
33 | /// enumerator after the children have been enumerated). Both and
34 | /// can be specified at the same time if there are no children for the element.
35 | /// The inlien element being returned from the enumerator.
36 | public EnumeratorEntry(bool opening, bool closing, Inline inline)
37 | {
38 | this.IsOpening = opening;
39 | this.IsClosing = closing;
40 | this.Inline = inline;
41 | }
42 |
43 | ///
44 | /// Gets the value indicating whether this instance represents the opening of the element (returned before enumerating
45 | /// over the children of the element).
46 | ///
47 | public bool IsOpening { get; private set; }
48 |
49 | ///
50 | /// Gets the value indicating whether this instance represents the closing of the element (returned by the
51 | /// enumerator after the children have been enumerated). Both and
52 | /// can be true at the same time if there are no children for the given element.
53 | ///
54 | public bool IsClosing { get; private set; }
55 |
56 | ///
57 | /// Gets the inline element. Can be null if is set.
58 | ///
59 | public Inline Inline { get; private set; }
60 |
61 | ///
62 | /// Gets the inline element. Can be null if is set.
63 | ///
64 | public Block Block { get; private set; }
65 |
66 | ///
67 | /// Returns a string that represents the current object.
68 | ///
69 | public override string ToString()
70 | {
71 | string r;
72 |
73 | if (this.IsOpening && this.IsClosing)
74 | r = "Complete ";
75 | else if (this.IsOpening)
76 | r = "Opening ";
77 | else if (this.IsClosing)
78 | r = "Closing ";
79 | else
80 | r = "Invalid ";
81 |
82 | if (this.Block != null)
83 | r += "block " + this.Block.Tag.ToString();
84 | else if (this.Inline != null)
85 | {
86 | r += "inline " + this.Inline.Tag.ToString();
87 |
88 | if (this.Inline.Tag == InlineTag.String)
89 | {
90 | if (this.Inline.LiteralContent == null)
91 | r += ": ";
92 | else if (this.Inline.LiteralContent.Length < 20)
93 | r += ": " + this.Inline.LiteralContent;
94 | else
95 | r += ": " + this.Inline.LiteralContent.Substring(0, 19) + "…";
96 | }
97 | }
98 | else
99 | r += "empty";
100 |
101 | return r;
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/CommonMark/CommonMark.Base.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | portable
6 | Debug
7 | AnyCPU
8 | {0FD4B1DD-45A8-4F02-BEB0-5881CD512573}
9 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
10 | Library
11 | Properties
12 | CommonMark
13 | CommonMark
14 | 512
15 | v4.0
16 | Profile328
17 | 10.0
18 |
19 |
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | TRACE;DEBUG
25 | prompt
26 | 4
27 |
28 |
29 | false
30 |
31 |
32 | pdbonly
33 | true
34 | bin\Release\
35 | TRACE
36 | prompt
37 | 4
38 | bin\Release\CommonMark.XML
39 | false
40 | AllRules.ruleset
41 |
42 |
43 | true
44 | Properties\public.snk
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | Properties\CommonMark.NET.nuspec
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/CommonMark.Tests/StrikethroughTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.VisualStudio.TestTools.UnitTesting;
7 |
8 | namespace CommonMark.Tests
9 | {
10 | [TestClass]
11 | public class StrikethroughTests
12 | {
13 | private static CommonMarkSettings _settings;
14 | private static CommonMarkSettings Settings
15 | {
16 | get
17 | {
18 | var s = _settings;
19 | if (s == null)
20 | {
21 | s = CommonMarkSettings.Default.Clone();
22 | s.AdditionalFeatures |= CommonMarkAdditionalFeatures.StrikethroughTilde;
23 | _settings = s;
24 | }
25 | return s;
26 | }
27 | }
28 |
29 | [TestMethod]
30 | [TestCategory("Inlines - Strikethrough")]
31 | public void StrikethroughDisabledByDefault()
32 | {
33 | Helpers.ExecuteTest("foo ~~bar~~", "foo ~~bar~~
");
34 | }
35 |
36 | [TestMethod]
37 | [TestCategory("Inlines - Strikethrough")]
38 | public void StrikethroughExample1()
39 | {
40 | Helpers.ExecuteTest("foo ~~bar~~", "foo bar
", Settings);
41 | }
42 |
43 | [TestMethod]
44 | [TestCategory("Inlines - Strikethrough")]
45 | public void StrikethroughExample2()
46 | {
47 | Helpers.ExecuteTest("foo ~~~bar~~", "foo ~bar
", Settings);
48 | }
49 |
50 | [TestMethod]
51 | [TestCategory("Inlines - Strikethrough")]
52 | public void StrikethroughExample3()
53 | {
54 | Helpers.ExecuteTest("foo ~~~~bar~~ asd~~", "foo bar asd
", Settings);
55 | }
56 |
57 | [TestMethod]
58 | [TestCategory("Inlines - Strikethrough")]
59 | public void StrikethroughExample4()
60 | {
61 | Helpers.ExecuteTest("foo ~~*bar~~*", "foo *bar*
", Settings);
62 | }
63 |
64 | [TestMethod]
65 | [TestCategory("Inlines - Strikethrough")]
66 | public void StrikethroughExample5()
67 | {
68 | Helpers.ExecuteTest("foo *~~bar~~*", "foo bar
", Settings);
69 | }
70 |
71 | [TestMethod]
72 | [TestCategory("Inlines - Strikethrough")]
73 | public void StrikethroughExample6()
74 | {
75 | Helpers.ExecuteTest("foo **~~bar**~~", "foo ~~bar ~~
", Settings);
76 | }
77 |
78 | [TestMethod]
79 | [TestCategory("Inlines - Strikethrough")]
80 | public void StrikethroughExample7()
81 | {
82 | Helpers.ExecuteTest("~~bar~~~", "bar~
", Settings);
83 | }
84 |
85 | [TestMethod]
86 | [TestCategory("Inlines - Strikethrough")]
87 | public void StrikethroughExample8()
88 | {
89 | // make sure that the fenced code blocks are not broken because of this feature
90 | Helpers.ExecuteTest("~~~foo\n~~", "~~\n ", Settings);
91 | }
92 |
93 | [TestMethod]
94 | [TestCategory("Inlines - Strikethrough")]
95 | public void StrikethroughExample9()
96 | {
97 | Helpers.ExecuteTest("foo ~~~~bar~~~~", "foo bar
", Settings);
98 | }
99 |
100 | [TestMethod]
101 | [TestCategory("Inlines - Strikethrough")]
102 | public void StrikethroughExample10()
103 | {
104 | // '[' char in the middle will delay the ~~ match to the post-process phase.
105 | Helpers.ExecuteTest("foo ~~~~ba[r~~~~", "foo ba[r
", Settings);
106 | }
107 |
108 | [TestMethod]
109 | [TestCategory("Inlines - Strikethrough")]
110 | public void StrikethroughExample10a()
111 | {
112 | // '[' char in the middle will delay the ~~ match to the post-process phase.
113 | Helpers.ExecuteTest("foo ~~~~ba[r~~", "foo ~~ba[r
", Settings);
114 | }
115 |
116 | [TestMethod]
117 | [TestCategory("Inlines - Strikethrough")]
118 | public void StrikethroughExample10b()
119 | {
120 | // '[' char in the middle will delay the ~~ match to the post-process phase.
121 | Helpers.ExecuteTest("foo ~~ba[r~~~~", "foo ba[r~~
", Settings);
122 | }
123 |
124 | [TestMethod]
125 | [TestCategory("Inlines - Strikethrough")]
126 | public void StrikethroughExample10c()
127 | {
128 | // '[' char in the middle will delay the ~~ match to the post-process phase.
129 | Helpers.ExecuteTest("foo ~~ba[r~~~", "foo ba[r~
", Settings);
130 | }
131 |
132 | [TestMethod]
133 | [TestCategory("Inlines - Strikethrough")]
134 | public void StrikethroughExample10d()
135 | {
136 | // '[' char in the middle will delay the ~~ match to the post-process phase.
137 | Helpers.ExecuteTest("~~~~[foo~~ bar", "~~[foo bar
", Settings);
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/CommonMark.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.23107.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonMark.Base", "CommonMark\CommonMark.Base.csproj", "{0FD4B1DD-45A8-4F02-BEB0-5881CD512573}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonMark.Console", "CommonMark.Console\CommonMark.Console.csproj", "{83F44787-27A2-4C18-97E2-497437C8EF75}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Other", "Other", "{EC5FFA77-658F-4D2F-A5CE-5F1BD44F3FC0}"
11 | ProjectSection(SolutionItems) = preProject
12 | CommonMark.NET.nuspec = CommonMark.NET.nuspec
13 | global.json = global.json
14 | LICENSE.md = LICENSE.md
15 | README.md = README.md
16 | runtests.pl = runtests.pl
17 | EndProjectSection
18 | EndProject
19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonMark.NET20", "CommonMark\CommonMark.NET20.csproj", "{075E7F00-F1C0-49AA-808D-8B15F1A82DDE}"
20 | EndProject
21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonMark.NET35", "CommonMark\CommonMark.NET35.csproj", "{61829669-5091-4F2A-A0B1-7348D7CED840}"
22 | EndProject
23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonMark.NET40", "CommonMark\CommonMark.NET40.csproj", "{0176CB03-177C-464A-A155-089595E77520}"
24 | EndProject
25 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonMark.NET45", "CommonMark\CommonMark.NET45.csproj", "{40AB0D9C-9464-45B2-AC79-72A566FA1371}"
26 | EndProject
27 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonMark.Tests", "CommonMark.Tests\CommonMark.Tests.csproj", "{B3158916-F54D-4F00-B553-E86D4370FF96}"
28 | EndProject
29 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CommonMark.NETCore", "CommonMark.NETCore\CommonMark.NETCore.xproj", "{47C48819-62B7-4BD3-89FF-BA9A0B16C8FB}"
30 | EndProject
31 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonMark.Profile259", "CommonMark\CommonMark.Profile259.csproj", "{E42ABBF6-884C-4D2C-B21D-B96D8B42F8FE}"
32 | EndProject
33 | Global
34 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
35 | Debug|Any CPU = Debug|Any CPU
36 | Release|Any CPU = Release|Any CPU
37 | EndGlobalSection
38 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
39 | {0FD4B1DD-45A8-4F02-BEB0-5881CD512573}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
40 | {0FD4B1DD-45A8-4F02-BEB0-5881CD512573}.Debug|Any CPU.Build.0 = Debug|Any CPU
41 | {0FD4B1DD-45A8-4F02-BEB0-5881CD512573}.Release|Any CPU.ActiveCfg = Release|Any CPU
42 | {0FD4B1DD-45A8-4F02-BEB0-5881CD512573}.Release|Any CPU.Build.0 = Release|Any CPU
43 | {83F44787-27A2-4C18-97E2-497437C8EF75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
44 | {83F44787-27A2-4C18-97E2-497437C8EF75}.Debug|Any CPU.Build.0 = Debug|Any CPU
45 | {83F44787-27A2-4C18-97E2-497437C8EF75}.Release|Any CPU.ActiveCfg = Release|Any CPU
46 | {83F44787-27A2-4C18-97E2-497437C8EF75}.Release|Any CPU.Build.0 = Release|Any CPU
47 | {075E7F00-F1C0-49AA-808D-8B15F1A82DDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
48 | {075E7F00-F1C0-49AA-808D-8B15F1A82DDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
49 | {075E7F00-F1C0-49AA-808D-8B15F1A82DDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
50 | {075E7F00-F1C0-49AA-808D-8B15F1A82DDE}.Release|Any CPU.Build.0 = Release|Any CPU
51 | {61829669-5091-4F2A-A0B1-7348D7CED840}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
52 | {61829669-5091-4F2A-A0B1-7348D7CED840}.Debug|Any CPU.Build.0 = Debug|Any CPU
53 | {61829669-5091-4F2A-A0B1-7348D7CED840}.Release|Any CPU.ActiveCfg = Release|Any CPU
54 | {61829669-5091-4F2A-A0B1-7348D7CED840}.Release|Any CPU.Build.0 = Release|Any CPU
55 | {0176CB03-177C-464A-A155-089595E77520}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
56 | {0176CB03-177C-464A-A155-089595E77520}.Debug|Any CPU.Build.0 = Debug|Any CPU
57 | {0176CB03-177C-464A-A155-089595E77520}.Release|Any CPU.ActiveCfg = Release|Any CPU
58 | {0176CB03-177C-464A-A155-089595E77520}.Release|Any CPU.Build.0 = Release|Any CPU
59 | {40AB0D9C-9464-45B2-AC79-72A566FA1371}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
60 | {40AB0D9C-9464-45B2-AC79-72A566FA1371}.Debug|Any CPU.Build.0 = Debug|Any CPU
61 | {40AB0D9C-9464-45B2-AC79-72A566FA1371}.Release|Any CPU.ActiveCfg = Release|Any CPU
62 | {40AB0D9C-9464-45B2-AC79-72A566FA1371}.Release|Any CPU.Build.0 = Release|Any CPU
63 | {B3158916-F54D-4F00-B553-E86D4370FF96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
64 | {B3158916-F54D-4F00-B553-E86D4370FF96}.Debug|Any CPU.Build.0 = Debug|Any CPU
65 | {B3158916-F54D-4F00-B553-E86D4370FF96}.Release|Any CPU.ActiveCfg = Release|Any CPU
66 | {B3158916-F54D-4F00-B553-E86D4370FF96}.Release|Any CPU.Build.0 = Release|Any CPU
67 | {47C48819-62B7-4BD3-89FF-BA9A0B16C8FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
68 | {47C48819-62B7-4BD3-89FF-BA9A0B16C8FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
69 | {47C48819-62B7-4BD3-89FF-BA9A0B16C8FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
70 | {47C48819-62B7-4BD3-89FF-BA9A0B16C8FB}.Release|Any CPU.Build.0 = Release|Any CPU
71 | {E42ABBF6-884C-4D2C-B21D-B96D8B42F8FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
72 | {E42ABBF6-884C-4D2C-B21D-B96D8B42F8FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
73 | {E42ABBF6-884C-4D2C-B21D-B96D8B42F8FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
74 | {E42ABBF6-884C-4D2C-B21D-B96D8B42F8FE}.Release|Any CPU.Build.0 = Release|Any CPU
75 | EndGlobalSection
76 | GlobalSection(SolutionProperties) = preSolution
77 | HideSolutionNode = FALSE
78 | EndGlobalSection
79 | EndGlobal
80 |
--------------------------------------------------------------------------------
/CommonMark/Utilities.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark
6 | {
7 | ///
8 | /// Reusable utility functions, not directly related to parsing or formatting data.
9 | ///
10 | internal static class Utilities
11 | {
12 | ///
13 | /// Writes a warning to the Debug window.
14 | ///
15 | /// The message with optional formatting placeholders.
16 | /// The arguments for the formatting placeholders.
17 | [System.Diagnostics.Conditional("DEBUG")]
18 | public static void Warning(string message, params object[] args)
19 | {
20 | if (args != null && args.Length > 0)
21 | message = string.Format(System.Globalization.CultureInfo.InvariantCulture, message, args);
22 |
23 | System.Diagnostics.Debug.WriteLine(message, "Warning");
24 | }
25 |
26 | #if OptimizeFor45
27 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
28 | #endif
29 | public static bool IsEscapableSymbol(char c)
30 | {
31 | // char.IsSymbol also works with Unicode symbols that cannot be escaped based on the specification.
32 | return (c > ' ' && c < '0') || (c > '9' && c < 'A') || (c > 'Z' && c < 'a') || (c > 'z' && c < 127) || c == '•';
33 | }
34 |
35 | #if OptimizeFor45
36 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
37 | #endif
38 | public static bool IsAsciiLetter(char c)
39 | {
40 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
41 | }
42 |
43 | #if OptimizeFor45
44 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
45 | #endif
46 | public static bool IsAsciiLetterOrDigit(char c)
47 | {
48 | return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z');
49 | }
50 |
51 | #if OptimizeFor45
52 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
53 | #endif
54 | public static bool IsWhitespace(char c)
55 | {
56 | return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f';
57 | }
58 |
59 | ///
60 | /// Checks if the given character is an Unicode space or punctuation character.
61 | ///
62 | #if OptimizeFor45
63 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
64 | #endif
65 | public static void CheckUnicodeCategory(char c, out bool space, out bool punctuation)
66 | {
67 | // This method does the same as would calling the two built-in methods:
68 | // // space = char.IsWhiteSpace(c);
69 | // // punctuation = char.IsPunctuation(c);
70 | //
71 | // The performance benefit for using this method is ~50% when calling only on ASCII characters
72 | // and ~12% when calling only on Unicode characters.
73 |
74 | if (c <= 'ÿ')
75 | {
76 | space = c == ' ' || (c >= '\t' && c <= '\r') || c == '\u00a0' || c == '\u0085';
77 | punctuation = (c >= 33 && c <= 47) || (c >= 58 && c <= 64) || (c >= 91 && c <= 96) || (c >= 123 && c <= 126);
78 | }
79 | else
80 | {
81 | var category = System.Globalization.CharUnicodeInfo.GetUnicodeCategory(c);
82 | space = category == System.Globalization.UnicodeCategory.SpaceSeparator
83 | || category == System.Globalization.UnicodeCategory.LineSeparator
84 | || category == System.Globalization.UnicodeCategory.ParagraphSeparator;
85 | punctuation = !space &&
86 | (category == System.Globalization.UnicodeCategory.ConnectorPunctuation
87 | || category == System.Globalization.UnicodeCategory.DashPunctuation
88 | || category == System.Globalization.UnicodeCategory.OpenPunctuation
89 | || category == System.Globalization.UnicodeCategory.ClosePunctuation
90 | || category == System.Globalization.UnicodeCategory.InitialQuotePunctuation
91 | || category == System.Globalization.UnicodeCategory.FinalQuotePunctuation
92 | || category == System.Globalization.UnicodeCategory.OtherPunctuation);
93 | }
94 | }
95 |
96 | ///
97 | /// Determines if the first line (ignoring the first ) of a string contains only spaces.
98 | ///
99 | public static bool IsFirstLineBlank(string source, int startIndex)
100 | {
101 | char c;
102 | var lastIndex = source.Length;
103 |
104 | while (startIndex < lastIndex)
105 | {
106 | c = source[startIndex];
107 | if (c == '\n')
108 | return true;
109 |
110 | if (c != ' ')
111 | return false;
112 |
113 | startIndex++;
114 | }
115 |
116 | return true;
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/CommonMark.Tests/CommonMark.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {B3158916-F54D-4F00-B553-E86D4370FF96}
7 | Library
8 | Properties
9 | CommonMark.Tests
10 | CommonMark.Tests
11 | v4.5.1
12 | 512
13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 10.0
15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
17 | False
18 | UnitTest
19 |
20 |
21 | true
22 | full
23 | false
24 | bin\Debug\
25 | DEBUG;TRACE
26 | prompt
27 | 4
28 |
29 |
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 | true
39 |
40 |
41 | ..\CommonMark\Properties\public.snk
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | True
74 | True
75 | Specs.tt
76 |
77 |
78 |
79 |
80 | TextTemplatingFileGenerator
81 | Specs.cs
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | {0fd4b1dd-45a8-4f02-beb0-5881cd512573}
90 | CommonMark.Base
91 |
92 |
93 |
94 |
95 |
96 |
97 | False
98 |
99 |
100 | False
101 |
102 |
103 | False
104 |
105 |
106 | False
107 |
108 |
109 |
110 |
111 |
112 |
113 |
120 |
--------------------------------------------------------------------------------
/CommonMark/CommonMarkException.cs:
--------------------------------------------------------------------------------
1 | using CommonMark.Syntax;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace CommonMark
7 | {
8 | ///
9 | /// An exception that is caught during CommonMark parsing or formatting.
10 | ///
11 | #if v2_0 || v3_5 || v4_0 || v4_5
12 | [Serializable]
13 | #endif
14 | public class CommonMarkException : Exception
15 | {
16 | ///
17 | /// Gets the block that caused the exception. Can be null .
18 | ///
19 | public Block BlockElement { get; private set; }
20 |
21 | ///
22 | /// Gets the inline element that caused the exception. Can be null .
23 | ///
24 | public Inline InlineElement { get; private set; }
25 |
26 | /// Initializes a new instance of the class.
27 | public CommonMarkException() { }
28 |
29 | /// Initializes a new instance of the class with a specified error message.
30 | /// The message that describes the error.
31 | public CommonMarkException(string message) : base(message) { }
32 |
33 | /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception.
34 | /// The error message that explains the reason for the exception.
35 | /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.
36 | public CommonMarkException(string message, Exception innerException) : base(message, innerException) { }
37 |
38 | /// Initializes a new instance of the class with a specified error message, a reference to the element that caused it and a reference to the inner exception that is the cause of this exception.
39 | /// The error message that explains the reason for the exception.
40 | /// The inline element that is related to the exception cause.
41 | /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.
42 | public CommonMarkException(string message, Inline inline, Exception innerException = null)
43 | : base(message, innerException)
44 | {
45 | this.InlineElement = inline;
46 | }
47 |
48 | /// Initializes a new instance of the class with a specified error message, a reference to the element that caused it and a reference to the inner exception that is the cause of this exception.
49 | /// The error message that explains the reason for the exception.
50 | /// The block element that is related to the exception cause.
51 | /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.
52 | public CommonMarkException(string message, Block block, Exception innerException = null)
53 | : base(message, innerException)
54 | {
55 | this.BlockElement = block;
56 | }
57 |
58 | #if v2_0 || v3_5 || v4_0 || v4_5
59 | /// Initializes a new instance of the class from the specified instances of the and classes.
60 | /// A instance that contains the information required to deserialize the new instance.
61 | /// A instance.
62 | [System.Security.SecuritySafeCritical]
63 | protected CommonMarkException(
64 | System.Runtime.Serialization.SerializationInfo serializationInfo,
65 | System.Runtime.Serialization.StreamingContext streamingContext)
66 | : base(serializationInfo, streamingContext)
67 | {
68 | // Block and Inline classes are not marked [Serializable] and thus cannot be used here.
69 | // Currently there also aren't any good use cases where this would provide added value.
70 | ////this.BlockElement = (Block)info.GetValue("BlockElement", typeof(Block));
71 | ////this.InlineElement = (Inline)info.GetValue("InlineElement", typeof(Inline));
72 | }
73 |
74 | /// Sets the with information about the exception.
75 | /// The that holds the serialized object data about the exception being thrown.
76 | /// The that contains contextual information about the source or destination.
77 | /// The parameter is a null reference (Nothing in Visual Basic).
78 | [System.Security.SecurityCritical]
79 | public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
80 | {
81 | base.GetObjectData(info, context);
82 | ////info.AddValue("BlockElement", this.BlockElement);
83 | ////info.AddValue("InlineElement", this.InlineElement);
84 | }
85 | #endif
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/CommonMark/Formatters/HtmlTextWriter.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using CommonMark.Syntax;
3 |
4 | namespace CommonMark.Formatters
5 | {
6 | ///
7 | /// A wrapper for that keeps track if the last symbol has been a newline.
8 | ///
9 | internal sealed class HtmlTextWriter
10 | {
11 | private readonly TextWriter _inner;
12 | private char _last = '\n';
13 | private readonly bool _windowsNewLine;
14 | private readonly char[] _newline;
15 |
16 | ///
17 | /// A reusable char buffer. This is used internally in (and thus will modify the buffer)
18 | /// but can also be used from class.
19 | ///
20 | internal char[] Buffer = new char[256];
21 |
22 | public HtmlTextWriter(TextWriter inner)
23 | {
24 | this._inner = inner;
25 |
26 | var nl = inner.NewLine;
27 | this._newline = nl.ToCharArray();
28 | this._windowsNewLine = nl == "\r\n";
29 | }
30 |
31 | public void WriteLine()
32 | {
33 | this._inner.Write(this._newline);
34 | this._last = '\n';
35 | }
36 |
37 | public void WriteLine(char data)
38 | {
39 | if (data == '\n' && this._windowsNewLine && this._last != '\r')
40 | this._inner.Write('\r');
41 |
42 | this._inner.Write(data);
43 | this._inner.Write(this._newline);
44 | this._last = '\n';
45 | }
46 |
47 | public void Write(StringPart value)
48 | {
49 | if (value.Length == 0)
50 | return;
51 |
52 | if (this.Buffer.Length < value.Length)
53 | this.Buffer = new char[value.Length];
54 |
55 | value.Source.CopyTo(value.StartIndex, this.Buffer, 0, value.Length);
56 |
57 | if (this._windowsNewLine)
58 | {
59 | var lastPos = value.StartIndex;
60 | var pos = lastPos;
61 |
62 | while (-1 != (pos = value.Source.IndexOf('\n', pos, value.Length - pos + value.StartIndex)))
63 | {
64 | var lastC = pos == 0 ? this._last : value.Source[pos - 1];
65 |
66 | if (lastC != '\r')
67 | {
68 | this._inner.Write(this.Buffer, lastPos - value.StartIndex, pos - lastPos);
69 | this._inner.Write('\r');
70 | lastPos = pos;
71 | }
72 |
73 | pos++;
74 | }
75 |
76 | this._inner.Write(this.Buffer, lastPos - value.StartIndex, value.Length - lastPos + value.StartIndex);
77 | }
78 | else
79 | {
80 | this._inner.Write(this.Buffer, 0, value.Length);
81 | }
82 |
83 | this._last = this.Buffer[value.Length - 1];
84 | }
85 |
86 | ///
87 | /// Writes a value that is known not to contain any newlines.
88 | ///
89 | public void WriteConstant(char[] value)
90 | {
91 | this._last = 'c';
92 | this._inner.Write(value, 0, value.Length);
93 | }
94 |
95 | ///
96 | /// Writes a value that is known not to contain any newlines.
97 | ///
98 | public void WriteConstant(char[] value, int startIndex, int length)
99 | {
100 | this._last = 'c';
101 | this._inner.Write(value, startIndex, length);
102 | }
103 |
104 | ///
105 | /// Writes a value that is known not to contain any newlines.
106 | ///
107 | public void WriteConstant(string value)
108 | {
109 | this._last = 'c';
110 | this._inner.Write(value);
111 | }
112 |
113 | ///
114 | /// Writes a value that is known not to contain any newlines.
115 | ///
116 | public void WriteLineConstant(string value)
117 | {
118 | this._last = '\n';
119 | this._inner.Write(value);
120 | this._inner.Write(this._newline);
121 | }
122 |
123 | public void Write(char[] value, int index, int count)
124 | {
125 | if (value == null || count == 0)
126 | return;
127 |
128 | if (this._windowsNewLine)
129 | {
130 | var lastPos = index;
131 | var pos = index;
132 |
133 | while (pos < index + count)
134 | {
135 | if (value[pos] != '\n')
136 | {
137 | pos++;
138 | continue;
139 | }
140 |
141 | var lastC = pos == index ? this._last : value[pos - 1];
142 |
143 | if (lastC != '\r')
144 | {
145 | this._inner.Write(value, lastPos, pos - lastPos);
146 | this._inner.Write('\r');
147 | lastPos = pos;
148 | }
149 |
150 | pos++;
151 | }
152 |
153 | this._inner.Write(value, lastPos, index + count - lastPos);
154 | }
155 | else
156 | {
157 | this._inner.Write(value, index, count);
158 | }
159 |
160 | this._last = value[index + count - 1];
161 | }
162 |
163 | public void Write(char value)
164 | {
165 | if (value == '\n' && this._windowsNewLine && this._last != '\r')
166 | this._inner.Write('\r');
167 |
168 | this._last = value;
169 | this._inner.Write(value);
170 | }
171 |
172 | ///
173 | /// Adds a newline if the writer does not currently end with a newline.
174 | ///
175 | public void EnsureLine()
176 | {
177 | if (this._last != '\n')
178 | this.WriteLine();
179 | }
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/Enumerable.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace CommonMark.Syntax
5 | {
6 | internal class Enumerable : IEnumerable
7 | {
8 | private Block _root;
9 |
10 | public Enumerable(Block root)
11 | {
12 | if (root == null)
13 | throw new ArgumentNullException(nameof(root));
14 |
15 | this._root = root;
16 | }
17 |
18 | public IEnumerator GetEnumerator()
19 | {
20 | return new Enumerator(this._root);
21 | }
22 |
23 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
24 | {
25 | return this.GetEnumerator();
26 | }
27 |
28 | private sealed class Enumerator : IEnumerator
29 | {
30 | private Block _root;
31 | private EnumeratorEntry _current;
32 | private Stack _blockStack = new Stack();
33 | private Stack _inlineStack = new Stack();
34 |
35 | public Enumerator(Block root)
36 | {
37 | this._root = root;
38 | this._blockStack.Push(new BlockStackEntry(root, null));
39 | }
40 |
41 | public EnumeratorEntry Current
42 | {
43 | get { return this._current; }
44 | }
45 |
46 | object System.Collections.IEnumerator.Current
47 | {
48 | get { return this._current; }
49 | }
50 |
51 | #if OptimizeFor45
52 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
53 | #endif
54 | private bool ShouldSkip(Inline inline)
55 | {
56 | if (inline.Tag == InlineTag.String
57 | && inline.FirstChild == null
58 | && inline.LiteralContentValue.Length == 0)
59 | return true;
60 |
61 | return false;
62 | }
63 |
64 | public bool MoveNext()
65 | {
66 | repeatMoveNext:
67 |
68 | Inline inline;
69 | if (this._inlineStack.Count > 0)
70 | {
71 | var entry = this._inlineStack.Pop();
72 | if (entry.NeedsClose != null)
73 | {
74 | inline = entry.NeedsClose;
75 | this._current = new EnumeratorEntry(false, true, inline);
76 |
77 | if (entry.Target != null)
78 | {
79 | entry.NeedsClose = null;
80 | this._inlineStack.Push(entry);
81 | }
82 |
83 | return true;
84 | }
85 |
86 | if (entry.Target != null)
87 | {
88 | inline = entry.Target;
89 | this._current = new EnumeratorEntry(true, inline.FirstChild == null, inline);
90 |
91 | if (inline.FirstChild != null)
92 | {
93 | this._inlineStack.Push(new InlineStackEntry(inline.NextSibling, inline));
94 | this._inlineStack.Push(new InlineStackEntry(inline.FirstChild, null));
95 | }
96 | else if (inline.NextSibling != null)
97 | {
98 | this._inlineStack.Push(new InlineStackEntry(inline.NextSibling, null));
99 | }
100 |
101 | if (this.ShouldSkip(this._current.Inline))
102 | {
103 | goto repeatMoveNext;
104 | }
105 | }
106 |
107 | return true;
108 | }
109 |
110 | Block block;
111 | if (this._blockStack.Count > 0)
112 | {
113 | var entry = this._blockStack.Pop();
114 | if (entry.NeedsClose != null)
115 | {
116 | block = entry.NeedsClose;
117 | this._current = new EnumeratorEntry(false, true, block);
118 |
119 | if (entry.Target != null)
120 | {
121 | entry.NeedsClose = null;
122 | this._blockStack.Push(entry);
123 | }
124 |
125 | return true;
126 | }
127 |
128 | if (entry.Target != null)
129 | {
130 | block = entry.Target;
131 | this._current = new EnumeratorEntry(true, block.FirstChild == null && block.InlineContent == null, block);
132 |
133 | if (block.FirstChild != null)
134 | {
135 | this._blockStack.Push(new BlockStackEntry(block.NextSibling, block));
136 | this._blockStack.Push(new BlockStackEntry(block.FirstChild, null));
137 | }
138 | else if (block.NextSibling != null && block != this._root)
139 | {
140 | this._blockStack.Push(new BlockStackEntry(block.NextSibling, block.InlineContent == null ? null : block));
141 | }
142 | else if (block.InlineContent != null)
143 | {
144 | this._blockStack.Push(new BlockStackEntry(null, block));
145 | }
146 |
147 | if (block.InlineContent != null)
148 | {
149 | this._inlineStack.Push(new InlineStackEntry(block.InlineContent, null));
150 | }
151 | }
152 |
153 | return true;
154 | }
155 |
156 | return false;
157 | }
158 |
159 | public void Reset()
160 | {
161 | this._current = null;
162 | this._blockStack.Clear();
163 | this._inlineStack.Clear();
164 | }
165 |
166 | void IDisposable.Dispose()
167 | {
168 | }
169 |
170 | private struct BlockStackEntry
171 | {
172 | public readonly Block Target;
173 | public Block NeedsClose;
174 | public BlockStackEntry(Block target, Block needsClose)
175 | {
176 | this.Target = target;
177 | this.NeedsClose = needsClose;
178 | }
179 | }
180 | private struct InlineStackEntry
181 | {
182 | public readonly Inline Target;
183 | public Inline NeedsClose;
184 | public InlineStackEntry(Inline target, Inline needsClose)
185 | {
186 | this.Target = target;
187 | this.NeedsClose = needsClose;
188 | }
189 | }
190 | }
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/CommonMark/CommonMarkSettings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace CommonMark
6 | {
7 | ///
8 | /// Class used to configure the behavior of .
9 | ///
10 | /// This class is not thread-safe so any changes to a instance that is reused (for example, the
11 | /// ) has to be updated while it is not in use otherwise the
12 | /// behaviour is undefined.
13 | public sealed class CommonMarkSettings
14 | {
15 | /// Initializes a new instance of the class.
16 | [Obsolete("Use CommonMarkSettings.Default.Clone() instead", false)]
17 | [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
18 | public CommonMarkSettings()
19 | { }
20 |
21 | ///
22 | /// Gets or sets the output format used by the last stage of conversion.
23 | ///
24 | public OutputFormat OutputFormat { get; set; }
25 |
26 | private Action _outputDelegate;
27 | ///
28 | /// Gets or sets the custom output delegate function used for formatting CommonMark output.
29 | /// Setting this to a non-null value will also set to .
30 | ///
31 | public Action OutputDelegate
32 | {
33 | get { return this._outputDelegate; }
34 | set
35 | {
36 | if (this._outputDelegate != value)
37 | {
38 | this._outputDelegate = value;
39 | this.OutputFormat = value == null ? default(OutputFormat) : OutputFormat.CustomDelegate;
40 | }
41 | }
42 | }
43 |
44 | ///
45 | /// Gets or sets a value indicating whether soft line breaks should be rendered as hard line breaks.
46 | ///
47 | public bool RenderSoftLineBreaksAsLineBreaks { get; set; }
48 |
49 | ///
50 | /// Gets or sets a value indicating whether the parser tracks precise positions in the source data for
51 | /// block and inline elements. This is disabled by default because it incurs an additional performance cost to
52 | /// keep track of the original position.
53 | /// Setting this to true will populate ,
54 | /// , and
55 | /// properties with correct information, otherwise the values
56 | /// of these properties are undefined.
57 | /// This also controls if these values will be written to the output.
58 | ///
59 | public bool TrackSourcePosition { get; set; }
60 |
61 | private CommonMarkAdditionalFeatures _additionalFeatures;
62 |
63 | ///
64 | /// Gets or sets any additional features (that are not present in the current CommonMark specification) that
65 | /// the parser and/or formatter will recognize.
66 | ///
67 | public CommonMarkAdditionalFeatures AdditionalFeatures
68 | {
69 | get { return this._additionalFeatures; }
70 | set { this._additionalFeatures = value; this._inlineParsers = null; this._inlineParserSpecialCharacters = null; }
71 | }
72 |
73 | private Func _uriResolver;
74 | ///
75 | /// Gets or sets the delegate that is used to resolve addresses during rendering process. Can be used to process application relative URLs (~/foo/bar ).
76 | ///
77 | /// CommonMarkSettings.Default.UriResolver = VirtualPathUtility.ToAbsolute;
78 | public Func UriResolver
79 | {
80 | get { return this._uriResolver; }
81 | set
82 | {
83 | if (value != null)
84 | {
85 | var orig = value;
86 | value = x =>
87 | {
88 | try
89 | {
90 | return orig(x);
91 | }
92 | catch (Exception ex)
93 | {
94 | throw new CommonMarkException("An error occurred while executing the CommonMarkSettings.UriResolver delegate. View inner exception for details.", ex);
95 | }
96 | };
97 | }
98 |
99 | this._uriResolver = value;
100 | }
101 | }
102 |
103 | #pragma warning disable 0618
104 | private static readonly CommonMarkSettings _default = new CommonMarkSettings();
105 | #pragma warning restore 0618
106 |
107 | ///
108 | /// The default settings for the converter. If the properties of this instance are modified, the changes will be applied to all
109 | /// future conversions that do not specify their own settings.
110 | ///
111 | public static CommonMarkSettings Default { get { return _default; } }
112 |
113 | ///
114 | /// Creates a copy of this configuration object.
115 | ///
116 | public CommonMarkSettings Clone()
117 | {
118 | return (CommonMarkSettings)this.MemberwiseClone();
119 | }
120 |
121 | #region [ Properties that cache structures used in the parsers ]
122 |
123 | private Func[] _inlineParsers;
124 |
125 | ///
126 | /// Gets the delegates that parse inline elements according to these settings.
127 | ///
128 | internal Func[] InlineParsers
129 | {
130 | get
131 | {
132 | var p = this._inlineParsers;
133 | if (p == null)
134 | {
135 | p = Parser.InlineMethods.InitializeParsers(this);
136 | this._inlineParsers = p;
137 | }
138 |
139 | return p;
140 | }
141 | }
142 |
143 | private char[] _inlineParserSpecialCharacters;
144 |
145 | ///
146 | /// Gets the characters that have special meaning for inline element parsers according to these settings.
147 | ///
148 | internal char[] InlineParserSpecialCharacters
149 | {
150 | get
151 | {
152 | var v = this._inlineParserSpecialCharacters;
153 | if (v == null)
154 | {
155 | var p = this.InlineParsers;
156 | var vs = new List(20);
157 | for (var i = 0; i < p.Length; i++)
158 | if (p[i] != null)
159 | vs.Add((char)i);
160 |
161 | v = this._inlineParserSpecialCharacters = vs.ToArray();
162 | }
163 |
164 | return v;
165 | }
166 | }
167 |
168 | #endregion
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/CommonMark/Parser/ScannerCharacterMatcher.cs:
--------------------------------------------------------------------------------
1 | namespace CommonMark.Parser
2 | {
3 | ///
4 | /// Class containing methods for fast forward matching of string contents
5 | ///
6 | internal static class ScannerCharacterMatcher
7 | {
8 | ///
9 | /// Moves along the given string as long as the current character is a whitespace.
10 | ///
11 | #if OptimizeFor45
12 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
13 | #endif
14 | internal static bool MatchWhitespaces(string data, ref char currentCharacter, ref int currentPosition, int lastPosition)
15 | {
16 | var matched = false;
17 | while (Utilities.IsWhitespace(currentCharacter) && currentPosition < lastPosition)
18 | {
19 | currentCharacter = data[++currentPosition];
20 | matched = true;
21 | }
22 | return matched;
23 | }
24 |
25 | ///
26 | /// Moves along the given string as long as the current character is a ASCII letter.
27 | ///
28 | #if OptimizeFor45
29 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
30 | #endif
31 | internal static bool MatchAsciiLetter(string data, ref char currentCharacter, ref int currentPosition, int lastPosition)
32 | {
33 | var matched = false;
34 | while (((currentCharacter >= 'a' && currentCharacter <= 'z')
35 | || (currentCharacter >= 'A' && currentCharacter <= 'Z'))
36 | && currentPosition < lastPosition)
37 | {
38 | currentCharacter = data[++currentPosition];
39 | matched = true;
40 | }
41 | return matched;
42 | }
43 |
44 | ///
45 | /// Moves along the given string as long as the current character is a valid HTML tag character
46 | /// (ASCII letter or digit or dash).
47 | ///
48 | #if OptimizeFor45
49 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
50 | #endif
51 | internal static bool MatchHtmlTagNameCharacter(string data, ref char currentCharacter, ref int currentPosition, int lastPosition)
52 | {
53 | var matched = false;
54 | while (( (currentCharacter >= 'a' && currentCharacter <= 'z')
55 | || (currentCharacter >= 'A' && currentCharacter <= 'Z')
56 | || (currentCharacter >= '0' && currentCharacter <= '9')
57 | || (currentCharacter == '-'))
58 | && currentPosition < lastPosition)
59 | {
60 | currentCharacter = data[++currentPosition];
61 | matched = true;
62 | }
63 | return matched;
64 | }
65 |
66 | ///
67 | /// Moves along the given string as long as the current character is a ASCII letter or digit or one of the given additional characters.
68 | ///
69 | #if OptimizeFor45
70 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
71 | #endif
72 | internal static bool MatchAsciiLetterOrDigit(string data, ref char currentCharacter, ref int currentPosition, int lastPosition, char valid1, char valid2, char valid3, char valid4)
73 | {
74 | var matched = false;
75 | while (( currentCharacter == valid1
76 | || currentCharacter == valid2
77 | || currentCharacter == valid3
78 | || currentCharacter == valid4
79 | || (currentCharacter >= 'a' && currentCharacter <= 'z')
80 | || (currentCharacter >= 'A' && currentCharacter <= 'Z')
81 | || (currentCharacter >= '0' && currentCharacter <= '9'))
82 | && currentPosition < lastPosition)
83 | {
84 | currentCharacter = data[++currentPosition];
85 | matched = true;
86 | }
87 | return matched;
88 | }
89 |
90 | ///
91 | /// Moves along the given string as long as the current character is a ASCII letter or digit or one of the given additional characters.
92 | ///
93 | #if OptimizeFor45
94 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
95 | #endif
96 | internal static bool MatchAsciiLetterOrDigit(string data, ref char currentCharacter, ref int currentPosition, int lastPosition, char valid1)
97 | {
98 | var matched = false;
99 | while ((currentCharacter == valid1
100 | || (currentCharacter >= 'a' && currentCharacter <= 'z')
101 | || (currentCharacter >= 'A' && currentCharacter <= 'Z')
102 | || (currentCharacter >= '0' && currentCharacter <= '9'))
103 | && currentPosition < lastPosition)
104 | {
105 | currentCharacter = data[++currentPosition];
106 | matched = true;
107 | }
108 | return matched;
109 | }
110 |
111 | ///
112 | /// Moves along the given string as long as the current character is a ASCII letter or one of the given additional characters.
113 | ///
114 | #if OptimizeFor45
115 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
116 | #endif
117 | internal static bool MatchAsciiLetter(string data, ref char currentCharacter, ref int currentPosition, int lastPosition, char valid1, char valid2)
118 | {
119 | var matched = false;
120 | while (( currentCharacter == valid1
121 | || currentCharacter == valid2
122 | || (currentCharacter >= 'a' && currentCharacter <= 'z')
123 | || (currentCharacter >= 'A' && currentCharacter <= 'Z')
124 | || (currentCharacter >= '0' && currentCharacter <= '9'))
125 | && currentPosition < lastPosition)
126 | {
127 | currentCharacter = data[++currentPosition];
128 | matched = true;
129 | }
130 | return matched;
131 | }
132 |
133 | #if OptimizeFor45
134 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
135 | #endif
136 | internal static bool MatchAnythingExcept(string data, ref char currentCharacter, ref int currentPosition, int lastPosition, char invalid1)
137 | {
138 | var matched = false;
139 | while (currentCharacter != invalid1 && currentPosition < lastPosition)
140 | {
141 | currentCharacter = data[++currentPosition];
142 | matched = true;
143 | }
144 | return matched;
145 | }
146 |
147 | #if OptimizeFor45
148 | [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
149 | #endif
150 | internal static bool MatchAnythingExceptWhitespaces(string data, ref char currentCharacter, ref int currentPosition, int lastPosition,
151 | char invalid1, char invalid2, char invalid3, char invalid4, char invalid5, char invalid6)
152 | {
153 | var matched = false;
154 | while (currentCharacter != invalid1
155 | && currentCharacter != invalid2
156 | && currentCharacter != invalid3
157 | && currentCharacter != invalid4
158 | && currentCharacter != invalid5
159 | && currentCharacter != invalid6
160 | && (currentCharacter != ' ' && currentCharacter != '\n')
161 | && currentPosition < lastPosition)
162 | {
163 | currentCharacter = data[++currentPosition];
164 | matched = true;
165 | }
166 | return matched;
167 | }
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/CommonMark/Syntax/Inline.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace CommonMark.Syntax
4 | {
5 | ///
6 | /// Represents a parsed inline element in the document.
7 | ///
8 | public sealed class Inline
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | public Inline()
14 | {
15 | }
16 |
17 | ///
18 | /// Initializes a new instance of the class.
19 | ///
20 | /// The type of inline element.
21 | public Inline(InlineTag tag)
22 | {
23 | this.Tag = tag;
24 | }
25 |
26 | ///
27 | /// Initializes a new instance of the class.
28 | ///
29 | /// The type of inline element. Should be one of the types that require literal content, for example, .
30 | /// The literal contents of the inline element.
31 | public Inline(InlineTag tag, string content)
32 | {
33 | this.Tag = tag;
34 | this.LiteralContent = content;
35 | }
36 |
37 | ///
38 | /// Initializes a new instance of the class.
39 | ///
40 | internal Inline(InlineTag tag, string content, int startIndex, int length)
41 | {
42 | this.Tag = tag;
43 | this.LiteralContentValue.Source = content;
44 | this.LiteralContentValue.StartIndex = startIndex;
45 | this.LiteralContentValue.Length = length;
46 | }
47 |
48 | ///
49 | /// Initializes a new instance of the class. The element type is set to
50 | ///
51 | /// The literal string contents of the inline element.
52 | public Inline(string content)
53 | {
54 | // this is not assigned because it is the default value.
55 | ////this.Tag = InlineTag.String;
56 |
57 | this.LiteralContent = content;
58 | }
59 |
60 | ///
61 | /// Initializes a new instance of the class. The element type is set to
62 | ///
63 | internal Inline(string content, int sourcePosition, int sourceLastPosition)
64 | {
65 | this.LiteralContent = content;
66 | this.SourcePosition = sourcePosition;
67 | this.SourceLastPosition = sourceLastPosition;
68 | }
69 |
70 | ///
71 | /// Initializes a new instance of the class. The element type is set to
72 | ///
73 | internal Inline(string content, int startIndex, int length, int sourcePosition, int sourceLastPosition, char delimiterCharacter)
74 | {
75 | this.LiteralContentValue.Source = content;
76 | this.LiteralContentValue.StartIndex = startIndex;
77 | this.LiteralContentValue.Length = length;
78 | this.SourcePosition = sourcePosition;
79 | this.SourceLastPosition = sourceLastPosition;
80 | this.Emphasis = new EmphasisData(delimiterCharacter);
81 | }
82 |
83 | ///
84 | /// Initializes a new instance of the class.
85 | ///
86 | /// The type of inline element. Should be one of the types that contain child elements, for example, .
87 | /// The first descendant element of the inline that is being created.
88 | public Inline(InlineTag tag, Inline content)
89 | {
90 | this.Tag = tag;
91 | this.FirstChild = content;
92 | }
93 |
94 | internal static Inline CreateLink(Inline label, string url, string title)
95 | {
96 | return new Inline()
97 | {
98 | Tag = InlineTag.Link,
99 | FirstChild = label,
100 | TargetUrl = url,
101 | LiteralContent = title
102 | };
103 | }
104 |
105 | ///
106 | /// Gets of sets the type of the inline element this instance represents.
107 | ///
108 | public InlineTag Tag { get; set; }
109 |
110 | ///
111 | /// Gets or sets the literal content of this element. This is only used if the property specifies
112 | /// a type that can have literal content.
113 | ///
114 | /// Note that for this property contains the title of the link.
115 | ///
116 | public string LiteralContent
117 | {
118 | get
119 | {
120 | // since the .ToString() has been called once, cache the value.
121 | return this.LiteralContent = this.LiteralContentValue.ToString();
122 | }
123 |
124 | set
125 | {
126 | this.LiteralContentValue.Source = value;
127 | this.LiteralContentValue.StartIndex = 0;
128 | this.LiteralContentValue.Length = value == null ? 0 : value.Length;
129 | }
130 | }
131 |
132 | internal StringPart LiteralContentValue;
133 |
134 | ///
135 | /// Gets or sets the target URL for this element. Only used for and
136 | /// .
137 | ///
138 | public string TargetUrl { get; set; }
139 |
140 | ///
141 | /// Gets or sets the first descendant of this element. This is only used if the property specifies
142 | /// a type that can have nested elements.
143 | ///
144 | public Inline FirstChild { get; set; }
145 |
146 | ///
147 | /// Gets or sets the position of the element within the source data.
148 | /// Note that if is not enabled, this property will contain
149 | /// the position relative to the containing block and not the whole document (not accounting for processing done
150 | /// in earlier parser stage, such as converting tabs to spaces).
151 | ///
152 | ///
153 | public int SourcePosition { get; set; }
154 |
155 | internal int SourceLastPosition { get; set; }
156 |
157 | ///
158 | /// Gets or sets the length of the element within the source data.
159 | /// Note that if is not enabled, this property will contain
160 | /// the length within the containing block (not accounting for processing done in earlier parser stage, such as
161 | /// converting tabs to spaces).
162 | ///
163 | ///
164 | public int SourceLength
165 | {
166 | get { return this.SourceLastPosition - this.SourcePosition; }
167 | set { this.SourceLastPosition = this.SourcePosition + value; }
168 | }
169 |
170 | ///
171 | /// Gets the link details. This is now obsolete in favor of and
172 | /// properties and this property will be removed in future.
173 | ///
174 | [Obsolete("The link properties have been moved to TargetUrl and LiteralContent (previously Title) properties to reduce number of objects created. This property will be removed in future versions.", false)]
175 | [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
176 | [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
177 | public InlineContentLinkable Linkable { get { return new InlineContentLinkable() { Url = this.TargetUrl, Title = this.LiteralContent }; } }
178 |
179 | private Inline _next;
180 |
181 | ///
182 | /// Gets the next sibling inline element. Returns null if this is the last element.
183 | ///
184 | public Inline NextSibling
185 | {
186 | get { return this._next; }
187 | set { this._next = value; }
188 | }
189 |
190 | ///
191 | /// Gets the last sibling of this inline. If no siblings are defined, returns self.
192 | ///
193 | public Inline LastSibling
194 | {
195 | get
196 | {
197 | var x = this._next;
198 | if (x == null)
199 | return this;
200 |
201 | while (x._next != null)
202 | x = x._next;
203 |
204 | return x;
205 | }
206 | }
207 |
208 | ///
209 | /// Gets the additional properties that apply to emphasis elements.
210 | ///
211 | public EmphasisData Emphasis { get; }
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/CommonMark/Parser/InlineStack.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using CommonMark.Syntax;
3 |
4 | namespace CommonMark.Parser
5 | {
6 | ///
7 | /// Describes an element in a stack of possible inline openers.
8 | ///
9 | internal sealed class InlineStack
10 | {
11 | ///
12 | /// The parser priority if this stack entry.
13 | ///
14 | public InlineStackPriority Priority;
15 |
16 | ///
17 | /// Previous entry in the stack. null if this is the last one.
18 | ///
19 | public InlineStack Previous;
20 |
21 | ///
22 | /// Next entry in the stack. null if this is the last one.
23 | ///
24 | public InlineStack Next;
25 |
26 | ///
27 | /// The at-the-moment text inline that could be transformed into the opener.
28 | ///
29 | public Inline StartingInline;
30 |
31 | ///
32 | /// The number of delimiter characters found for this opener.
33 | ///
34 | public int DelimiterCount;
35 |
36 | ///
37 | /// The character that was used in the opener.
38 | ///
39 | public char Delimiter;
40 |
41 | ///
42 | /// The position in the where this inline element was found.
43 | /// Used only if the specific parser requires this information.
44 | ///
45 | public int StartPosition;
46 |
47 | ///
48 | /// The flags set for this stack entry.
49 | ///
50 | public InlineStackFlags Flags;
51 |
52 | [Flags]
53 | public enum InlineStackFlags : byte
54 | {
55 | None = 0,
56 | Opener = 1,
57 | Closer = 2,
58 | ImageLink = 4
59 | }
60 |
61 | public enum InlineStackPriority : byte
62 | {
63 | Emphasis = 0,
64 | Links = 1,
65 | Maximum = Links
66 | }
67 |
68 | public static InlineStack FindMatchingOpener(InlineStack seachBackwardsFrom, InlineStackPriority priority, char delimiter, out bool canClose)
69 | {
70 | canClose = true;
71 | var istack = seachBackwardsFrom;
72 | while (true)
73 | {
74 | if (istack == null)
75 | {
76 | // this cannot be a closer since there is no opener available.
77 | canClose = false;
78 | return null;
79 | }
80 |
81 | if (istack.Priority > priority ||
82 | (istack.Delimiter == delimiter && 0 != (istack.Flags & InlineStackFlags.Closer)))
83 | {
84 | // there might be a closer further back but we cannot go there yet because a higher priority element is blocking
85 | // the other option is that the stack entry could be a closer for the same char - this means
86 | // that any opener we might find would first have to be matched against this closer.
87 | return null;
88 | }
89 |
90 | if (istack.Delimiter == delimiter)
91 | return istack;
92 |
93 | istack = istack.Previous;
94 | }
95 | }
96 |
97 | public static void AppendStackEntry(InlineStack entry, Subject subj)
98 | {
99 | if (subj.LastPendingInline != null)
100 | {
101 | entry.Previous = subj.LastPendingInline;
102 | subj.LastPendingInline.Next = entry;
103 | }
104 |
105 | if (subj.FirstPendingInline == null)
106 | subj.FirstPendingInline = entry;
107 |
108 | subj.LastPendingInline = entry;
109 | }
110 |
111 | ///
112 | /// Removes a subset of the stack.
113 | ///
114 | /// The first entry to be removed.
115 | /// The subject associated with this stack. Can be null if the pointers in the subject should not be updated.
116 | /// The last entry to be removed. Can be null if everything starting from has to be removed.
117 | public static void RemoveStackEntry(InlineStack first, Subject subj, InlineStack last)
118 | {
119 | var curPriority = first.Priority;
120 |
121 | if (last == null)
122 | {
123 | if (first.Previous != null)
124 | first.Previous.Next = null;
125 | else if (subj != null)
126 | subj.FirstPendingInline = null;
127 |
128 | if (subj != null)
129 | {
130 | last = subj.LastPendingInline;
131 | subj.LastPendingInline = first.Previous;
132 | }
133 |
134 | first = first.Next;
135 | }
136 | else
137 | {
138 | if (first.Previous != null)
139 | first.Previous.Next = last.Next;
140 | else if (subj != null)
141 | subj.FirstPendingInline = last.Next;
142 |
143 | if (last.Next != null)
144 | last.Next.Previous = first.Previous;
145 | else if (subj != null)
146 | subj.LastPendingInline = first.Previous;
147 |
148 | if (first == last)
149 | return;
150 |
151 | first = first.Next;
152 | last = last.Previous;
153 | }
154 |
155 | if (last == null || first == null)
156 | return;
157 |
158 | first.Previous = null;
159 | last.Next = null;
160 |
161 | // handle case like [*b*] (the whole [..] is being removed but the inner *..* must still be matched).
162 | // this is not done automatically because the initial * is recognized as a potential closer (assuming
163 | // potential scenario '*[*' ).
164 | if (curPriority > 0)
165 | PostProcessInlineStack(null, first, last, curPriority);
166 | }
167 |
168 | public static void PostProcessInlineStack(Subject subj, InlineStack first, InlineStack last, InlineStackPriority ignorePriority)
169 | {
170 | while (ignorePriority > 0)
171 | {
172 | var istack = first;
173 | while (istack != null)
174 | {
175 | if (istack.Priority >= ignorePriority)
176 | {
177 | RemoveStackEntry(istack, subj, istack);
178 | }
179 | else if (0 != (istack.Flags & InlineStackFlags.Closer))
180 | {
181 | bool canClose;
182 | var iopener = FindMatchingOpener(istack.Previous, istack.Priority, istack.Delimiter, out canClose);
183 | if (iopener != null)
184 | {
185 | bool retry = false;
186 | if (iopener.Delimiter == '~')
187 | {
188 | InlineMethods.MatchInlineStack(iopener, subj, istack.DelimiterCount, istack, (InlineTag)0, InlineTag.Strikethrough);
189 | if (istack.DelimiterCount > 1)
190 | retry = true;
191 | }
192 | else
193 | {
194 | var useDelims = InlineMethods.MatchInlineStack(iopener, subj, istack.DelimiterCount, istack, InlineTag.Emphasis, InlineTag.Strong);
195 | if (istack.DelimiterCount > 0)
196 | retry = true;
197 | }
198 |
199 | if (retry)
200 | {
201 | // remove everything between opened and closer (not inclusive).
202 | if (istack.Previous != null && iopener.Next != istack.Previous)
203 | RemoveStackEntry(iopener.Next, subj, istack.Previous);
204 |
205 | continue;
206 | }
207 | else
208 | {
209 | // remove opener, everything in between, and the closer
210 | RemoveStackEntry(iopener, subj, istack);
211 | }
212 | }
213 | else if (!canClose)
214 | {
215 | // this case means that a matching opener does not exist
216 | // remove the Closer flag so that a future Opener can be matched against it.
217 | istack.Flags &= ~InlineStackFlags.Closer;
218 | }
219 | }
220 |
221 | if (istack == last)
222 | break;
223 |
224 | istack = istack.Next;
225 | }
226 |
227 | ignorePriority--;
228 | }
229 | }
230 |
231 |
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/CommonMark.Console/Program.cs:
--------------------------------------------------------------------------------
1 | using CommonMark.Syntax;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 |
7 | namespace CommonMark
8 | {
9 | class Program
10 | {
11 | static void PrintUsage(string invalidArg = null)
12 | {
13 | if (invalidArg != null)
14 | {
15 | Console.WriteLine("Invalid argument: " + invalidArg);
16 | Console.WriteLine();
17 | }
18 |
19 | var fname = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
20 | Console.WriteLine("Usage: " + fname + " [FILE*] [--out FILE]");
21 | Console.WriteLine("Options: --help, -h Print usage information");
22 | Console.WriteLine(" --ast Print AST instead of HTML");
23 | Console.WriteLine(" --sourcepos Enable source position tracking and output");
24 | Console.WriteLine(" --bench 20 Execute a benchmark on the given input, optionally");
25 | Console.WriteLine(" specify the number of iterations, default is 20");
26 | Console.WriteLine(" --perltest Changes console input handling for runtests.pl");
27 | Console.WriteLine(" --version Print version");
28 | Console.WriteLine(" --out The output file; if not specified, stdout is used");
29 | }
30 |
31 | static int Main(string[] args)
32 | {
33 | var sources = new List();
34 | var benchmark = false;
35 | var benchmarkIterations = 20;
36 | var target = Console.Out;
37 | var runPerlTests = false;
38 | var settings = CommonMarkSettings.Default.Clone();
39 |
40 | try
41 | {
42 | for (var i = 0; i < args.Length; i++)
43 | {
44 | if (string.Equals(args[i], "--version", StringComparison.OrdinalIgnoreCase))
45 | {
46 | Console.WriteLine("CommonMark.NET {0}", CommonMarkConverter.Version);
47 | Console.WriteLine(" - (c) 2014-2015 Kārlis Gaņģis");
48 | return 0;
49 | }
50 | else if ((string.Equals(args[i], "--help", StringComparison.OrdinalIgnoreCase)) ||
51 | (string.Equals(args[i], "-h", StringComparison.OrdinalIgnoreCase)))
52 | {
53 | PrintUsage();
54 | return 0;
55 | }
56 | else if (string.Equals(args[i], "--perltest", StringComparison.OrdinalIgnoreCase))
57 | {
58 | runPerlTests = true;
59 | }
60 | else if (string.Equals(args[i], "--ast", StringComparison.OrdinalIgnoreCase))
61 | {
62 | settings.OutputFormat = OutputFormat.SyntaxTree;
63 | }
64 | else if (string.Equals(args[i], "--sourcepos", StringComparison.OrdinalIgnoreCase))
65 | {
66 | settings.TrackSourcePosition = true;
67 | }
68 | else if (string.Equals(args[i], "--bench", StringComparison.OrdinalIgnoreCase))
69 | {
70 | benchmark = true;
71 | if (i != args.Length - 1)
72 | {
73 | if (!int.TryParse(args[i + 1], System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out benchmarkIterations))
74 | benchmarkIterations = 20;
75 | else
76 | i++;
77 | }
78 | }
79 | else if (string.Equals(args[i], "--out", StringComparison.OrdinalIgnoreCase))
80 | {
81 | if (i == args.Length - 1 || args[i + 1].StartsWith("-"))
82 | {
83 | PrintUsage();
84 | return 1;
85 | }
86 |
87 | i++;
88 | target = new System.IO.StreamWriter(args[i]);
89 | }
90 | else if (args[i].StartsWith("-"))
91 | {
92 | PrintUsage(args[i]);
93 | return 1;
94 | }
95 | else
96 | {
97 | // treat as file argument
98 | sources.Add(new System.IO.StreamReader(args[i]));
99 | }
100 | }
101 |
102 | if (sources.Count == 0)
103 | {
104 | if (runPerlTests)
105 | {
106 | Console.InputEncoding = Encoding.UTF8;
107 | Console.OutputEncoding = Encoding.UTF8;
108 |
109 | // runtests.pl writes directly to STDIN but will not send Ctrl+C and also
110 | // will get confused by the additional information written to STDOUT
111 | var sb = new StringBuilder();
112 | while (Console.In.Peek() != -1)
113 | sb.AppendLine(Console.In.ReadLine());
114 |
115 | sources.Add(new System.IO.StringReader(sb.ToString()));
116 | }
117 | else if (!Console.IsInputRedirected)
118 | {
119 | Console.InputEncoding = Encoding.Unicode;
120 | Console.OutputEncoding = Encoding.Unicode;
121 |
122 | Console.WriteLine("Enter the source. Press Enter after the last line and" + Environment.NewLine + "then press Ctrl+C to run parser.");
123 | Console.WriteLine();
124 |
125 | Console.CancelKeyPress += (s, a) =>
126 | {
127 | a.Cancel = true;
128 | Console.WriteLine("Output:");
129 | Console.WriteLine();
130 | };
131 |
132 | sources.Add(Console.In);
133 | }
134 | else
135 | {
136 | Console.InputEncoding = Encoding.UTF8;
137 | Console.OutputEncoding = Encoding.UTF8;
138 |
139 | sources.Add(Console.In);
140 | }
141 | }
142 |
143 | if (benchmark)
144 | {
145 | Console.WriteLine("Running the benchmark...");
146 | foreach (var source in sources)
147 | {
148 | // by using a in-memory source, the disparity of results is reduced.
149 | var data = source.ReadToEnd();
150 |
151 | // in-memory source that gets reused further reduces the disparity.
152 | var builder = new StringBuilder(2 * 1024 * 1024);
153 |
154 | var sw = new System.Diagnostics.Stopwatch();
155 | var mem = GC.GetTotalMemory(true);
156 | long mem2 = 0;
157 |
158 | for (var x = -1 - benchmarkIterations / 10; x < benchmarkIterations; x++)
159 | {
160 | if (x == 0)
161 | sw.Start();
162 |
163 | builder.Length = 0;
164 | using (var reader = new System.IO.StringReader(data))
165 | using (var twriter = new System.IO.StringWriter(builder))
166 | CommonMarkConverter.Convert(reader, twriter, settings);
167 |
168 | if (mem2 == 0)
169 | mem2 = GC.GetTotalMemory(false);
170 |
171 | GC.Collect();
172 | }
173 |
174 | sw.Stop();
175 | target.WriteLine("Time spent: {0:0.#}ms Approximate memory usage: {1:0.000}MB",
176 | (decimal)sw.ElapsedMilliseconds / benchmarkIterations,
177 | (mem2 - mem) / 1024M / 1024M);
178 | }
179 | }
180 | else
181 | {
182 | foreach (var source in sources)
183 | CommonMarkConverter.Convert(source, target, settings);
184 | }
185 |
186 | if (System.Diagnostics.Debugger.IsAttached)
187 | {
188 | Console.WriteLine("Press any key to continue...");
189 | Console.ReadKey(true);
190 | }
191 |
192 | return 0;
193 | }
194 | catch (Exception ex)
195 | {
196 | Console.Error.WriteLine(ex.ToString());
197 | return -1;
198 | }
199 | finally
200 | {
201 | foreach (var s in sources)
202 | if (s != Console.In)
203 | s.Close();
204 |
205 | if (target != Console.Out)
206 | target.Close();
207 | }
208 | }
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/CommonMark.Tests/Specification/Specs.tt:
--------------------------------------------------------------------------------
1 | <#/*
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2014 Morten Houston Ludvigsen
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 | */#>
24 | <#@ template debug="false" hostspecific="true" language="C#" #>
25 | <#@ assembly name="System.Core" #>
26 | <#@ import namespace="System.Globalization" #>
27 | <#@ import namespace="System.IO" #>
28 | <#@ import namespace="System.Linq" #>
29 | <#@ import namespace="System.Text" #>
30 | <#@ import namespace="System.Text.RegularExpressions" #>
31 | <#@ import namespace="System.Collections.Generic" #>
32 | <#@ import namespace="System.CodeDom" #>
33 | <#@ import namespace="System.CodeDom.Compiler" #>
34 | <#@ output extension=".cs" #><#
35 | var specFile = "https://raw.githubusercontent.com/jgm/CommonMark/master/spec.txt";
36 | var emptyLines = false;
37 | var displayEmptyLines = false;
38 | #>
39 | using System;
40 | using System.Collections.Generic;
41 | using System.Linq;
42 | using System.Text;
43 | using System.Text.RegularExpressions;
44 | using System.Threading.Tasks;
45 | using Microsoft.VisualStudio.TestTools.UnitTesting;
46 |
47 | namespace CommonMark.Tests.Specification
48 | {
49 | [TestClass]
50 | public class Specs
51 | {
52 | <#
53 | foreach (var item in ReadSpecs(specFile))
54 | {
55 | if (item is string)
56 | {
57 | var line = (string)item;
58 | if (line == "")
59 | {
60 | emptyLines = true;
61 | }
62 | else
63 | {
64 | if (emptyLines && displayEmptyLines)
65 | {
66 | #>
67 | //
68 | <#
69 | }
70 | #>
71 | // <#= line #>
72 | <#
73 | displayEmptyLines = true;
74 | emptyLines = false;
75 | }
76 | }
77 | if (item is Spec)
78 | {
79 | var spec = (Spec)item;
80 | emptyLines = false;
81 | displayEmptyLines = false;
82 | #>
83 | [TestMethod]
84 | [TestCategory("<#= spec.SecHeading #>")]
85 | //[Timeout(1000)]
86 | public void <#= spec.Name #>()
87 | {
88 | // Example <#= spec.Example #>
89 | // Section: <#= spec.SecHeading #>
90 | //
91 | // The following CommonMark:
92 | <#= string.Join(Environment.NewLine, spec.CommonMark.Split('\n').Select(s => string.Format(" // {0}", s))) #>
93 | //
94 | // Should be rendered as:
95 | <#= string.Join(Environment.NewLine, spec.Html.Split('\n').Select(s => string.Format(" // {0}", s))) #>
96 |
97 | Helpers.Log("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, <#= spec.Example #>, "<#= Escape(spec.SecHeading) #>");
98 | Helpers.ExecuteTest("<#= Escape(spec.CommonMark) #>", "<#= Escape(spec.Html) #>");
99 | }
100 |
101 | <#
102 | }
103 | }
104 | #>
105 | }
106 | }
107 | <#+
108 | private class Item
109 | {
110 | }
111 |
112 | private class Spec
113 | {
114 | public int Example { get; set; }
115 | public int Number { get; set; }
116 | public string SecHeading { get; set; }
117 | public string SectionId { get; set; }
118 | public string CommonMark { get; set; }
119 | public string Html { get; set; }
120 |
121 | public string Name
122 | {
123 | get { return string.Format("Example{0:000}", Example); }
124 | }
125 | }
126 |
127 | private bool Match(string value, string pattern, out string[] groups)
128 | {
129 | var match = Regex.Match(value, pattern);
130 | if (match.Success)
131 | {
132 | groups = match.Groups.Cast().Select(g => g.Value).ToArray();
133 | return true;
134 | }
135 | groups = new string[] { };
136 | return false;
137 | }
138 |
139 | private string GetSectionId(IEnumerable sectionHeadings)
140 | {
141 | var name = string.Join("", sectionHeadings.Select(s => CultureInfo.InvariantCulture.TextInfo.ToTitleCase(s)));
142 | name = Regex.Replace(name, @"\s+", "");
143 | return name;
144 | }
145 |
146 | private IEnumerable ReadSpecs(string fileName)
147 | {
148 | var stage = 0;
149 | var commonMark = "";
150 | var html = "";
151 | var example = 0;
152 | var lineNum = 0;
153 | var exampleLine = 0;
154 | var sectionNums = new List();
155 | var sectionHeadings = new Stack();
156 | var sectionHeading = "";
157 | var groups = new string[] { };
158 | var line = "";
159 | var sectionSpecNumbers = new Dictionary();
160 | var endOfTests = false;
161 |
162 | using (var client = new System.Net.WebClient() { Encoding = Encoding.UTF8 })
163 | using (var reader = new StringReader(client.DownloadString(fileName)))
164 | {
165 | while (!endOfTests && (line = reader.ReadLine()) != null)
166 | {
167 | lineNum += 1;
168 | if (line.StartsWith("````````````````````````````````") || line == ".")
169 | {
170 | stage = (stage + 1) % 3;
171 | if (stage == 1)
172 | {
173 | exampleLine = lineNum;
174 | }
175 | if (stage == 0)
176 | {
177 | var sectionId = GetSectionId(sectionHeadings.Reverse());
178 | if (sectionSpecNumbers.ContainsKey(sectionId))
179 | {
180 | sectionSpecNumbers[sectionId] += 1;
181 | }
182 | else
183 | {
184 | sectionSpecNumbers[sectionId] = 1;
185 | }
186 |
187 | example++;
188 | yield return new Spec
189 | {
190 | Example = example,
191 | Number = sectionSpecNumbers[sectionId],
192 | SecHeading = string.Join(" - ", sectionHeadings.Reverse()),
193 | SectionId = sectionId,
194 | CommonMark = commonMark,
195 | Html = html
196 | };
197 | commonMark = "";
198 | html = "";
199 | }
200 | }
201 | else if (stage == 0 && line.StartsWith(""))
202 | {
203 | endOfTests = true;
204 | }
205 | else if (stage == 0 && Match(line, "^(#+) +(.*)", out groups))
206 | {
207 | var sectionLevel = groups[1].Length;
208 | sectionHeading = groups[2];
209 |
210 | while (sectionHeadings.Count() < sectionLevel)
211 | {
212 | sectionHeadings.Push("");
213 | }
214 | while (sectionHeadings.Count() >= sectionLevel)
215 | {
216 | sectionHeadings.Pop();
217 | }
218 | sectionHeadings.Push(sectionHeading);
219 |
220 | if (sectionNums.Count > 0 && sectionNums.Count == sectionLevel - 1)
221 | {
222 | sectionNums[sectionNums.Count - 1]++;
223 | }
224 | else if (sectionNums.Count > sectionLevel - 1)
225 | {
226 | sectionNums = Enumerable.Range(0, sectionLevel).ToList();
227 | sectionNums[sectionNums.Count - 1]++;
228 | }
229 | else
230 | {
231 | while (sectionNums.Count < sectionLevel - 1)
232 | {
233 | sectionNums.Add(1);
234 | }
235 | }
236 |
237 | yield return line.Trim();
238 | }
239 | else if (stage == 1)
240 | {
241 | if (commonMark.Length > 0)
242 | {
243 | commonMark += '\n';
244 | }
245 | commonMark += line;
246 | }
247 | else if (stage == 2)
248 | {
249 | if (html.Length > 0)
250 | {
251 | html += '\n';
252 | }
253 | html += line;
254 | }
255 | else
256 | {
257 | yield return line.Trim();
258 | }
259 | }
260 | while ((line = reader.ReadLine()) != null)
261 | {
262 | yield return line.Trim();
263 | }
264 | }
265 | }
266 |
267 | private static string Escape(string input)
268 | {
269 | return input
270 | .Replace("\\", "\\\\")
271 | .Replace("\"", "\\\"")
272 | .Replace("\0", "\\0")
273 | .Replace("\a", "\\a")
274 | .Replace("\b", "\\b")
275 | .Replace("\f", "\\f")
276 | .Replace("\n", "\\n")
277 | .Replace("\r", "\\r")
278 | .Replace("\t", "\\t")
279 | .Replace("\v", "\\v")
280 | ;
281 | }
282 |
283 | private static string ToLiteral(string input)
284 | {
285 | using (var writer = new StringWriter())
286 | {
287 | using (var provider = CodeDomProvider.CreateProvider("CSharp"))
288 | {
289 | provider.GenerateCodeFromExpression(new CodePrimitiveExpression(input), writer, null);
290 | return writer.ToString();
291 | }
292 | }
293 | }
294 | #>
--------------------------------------------------------------------------------
/CommonMark/Syntax/Block.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace CommonMark.Syntax
5 | {
6 | ///
7 | /// Represents a block-level element of the parsed document.
8 | ///
9 | public sealed class Block
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | /// The type of the element this instance represents.
15 | /// The position of the first character of this block in the source text.
16 | public Block(BlockTag tag, int sourcePosition)
17 | {
18 | this.Tag = tag;
19 | this.SourcePosition = sourcePosition;
20 | this.IsOpen = true;
21 | }
22 |
23 | ///
24 | /// Initializes a new instance of the class.
25 | ///
26 | /// The type of the element this instance represents.
27 | /// The number of the first line in the source text that contains this element.
28 | /// The number of the first column (within the first line) in the source text that contains this element.
29 | /// The position of the first character of this block in the source text.
30 | [Obsolete("StartLine/StartColumn are deprecated in favor of SourcePosition/SourceLength and will be removed in future. If you have a use case where this property cannot be replaced with the new ones, please log an issue at https://github.com/Knagis/CommonMark.NET", false)]
31 | public Block(BlockTag tag, int startLine, int startColumn, int sourcePosition)
32 | {
33 | this.Tag = tag;
34 | this.StartLine = startLine;
35 | this.EndLine = startLine;
36 | this.StartColumn = startColumn;
37 | this.SourcePosition = sourcePosition;
38 | this.IsOpen = true;
39 | }
40 |
41 | ///
42 | /// Returns an enumerable that allows the iteration over all block and inline elements within this
43 | /// instance. Note that the enumerator should not be used if optimal performance is desired and instead
44 | /// a custom implementation should be written.
45 | ///
46 | public IEnumerable AsEnumerable()
47 | {
48 | return new Enumerable(this);
49 | }
50 |
51 | ///
52 | /// Creates a new top-level document block.
53 | ///
54 | internal static Block CreateDocument()
55 | {
56 | #pragma warning disable 0618
57 | Block e = new Block(BlockTag.Document, 1, 1, 0);
58 | #pragma warning restore 0618
59 | e.Document = new DocumentData();
60 | e.Document.ReferenceMap = new Dictionary();
61 | e.Top = e;
62 | return e;
63 | }
64 |
65 | ///
66 | /// Gets or sets the type of the element this instance represents.
67 | ///
68 | public BlockTag Tag { get; set; }
69 |
70 | ///
71 | /// Gets or sets the type of the HTML block. Only applies when equals .
72 | ///
73 | public HtmlBlockType HtmlBlockType { get; set; }
74 |
75 | ///
76 | /// Gets or sets the number of the first line in the source text that contains this element.
77 | ///
78 | [Obsolete("This is deprecated in favor of SourcePosition/SourceLength and will be removed in future. If you have a use case where this property cannot be replaced with the new ones, please log an issue at https://github.com/Knagis/CommonMark.NET", false)]
79 | [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
80 | [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
81 | public int StartLine { get; set; }
82 |
83 | ///
84 | /// Gets or sets the number of the first column (within the first line) in the source text that contains this element.
85 | ///
86 | [Obsolete("This is deprecated in favor of SourcePosition/SourceLength and will be removed in future. If you have a use case where this property cannot be replaced with the new ones, please log an issue at https://github.com/Knagis/CommonMark.NET", false)]
87 | [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
88 | [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
89 | public int StartColumn { get; set; }
90 |
91 | ///
92 | /// Gets or sets the number of the last line in the source text that contains this element.
93 | ///
94 | [Obsolete("This is deprecated in favor of SourcePosition/SourceLength and will be removed in future. If you have a use case where this property cannot be replaced with the new ones, please log an issue at https://github.com/Knagis/CommonMark.NET", false)]
95 | [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
96 | [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
97 | public int EndLine { get; set; }
98 |
99 | ///
100 | /// Gets or sets the position of the block element within the source data. This position is before
101 | /// any opening characters. must be enabled
102 | /// for this value to be defined.
103 | ///
104 | ///
105 | public int SourcePosition { get; set; }
106 |
107 | internal int SourceLastPosition { get; set; }
108 |
109 | ///
110 | /// Gets or sets the length of the block element within the source data. This includes also characters that
111 | /// close the block element and in most cases the newline characters right after the block element.
112 | /// must be enabled for this value to be defined.
113 | ///
114 | ///
115 | public int SourceLength
116 | {
117 | get { return this.SourceLastPosition - this.SourcePosition; }
118 | set { this.SourceLastPosition = this.SourcePosition + value; }
119 | }
120 |
121 | ///
122 | /// Gets or sets a value indicating whether this block element has been completed (and thus new lines cannot be added
123 | /// to it) or is still open. By default all elements are created as open and are closed when the parser detects it.
124 | ///
125 | public bool IsOpen { get; set; }
126 |
127 | ///
128 | /// Gets or sets a value indicating whether the last line parsed for this block element was blank (containing only
129 | /// whitespaces).
130 | ///
131 | public bool IsLastLineBlank { get; set; }
132 |
133 | ///
134 | /// Gets or sets the first child element of this instance. null if there are no children.
135 | ///
136 | public Block FirstChild { get; set; }
137 |
138 | ///
139 | /// Gets or sets the last child element (the last sibling of ) of this instance.
140 | /// null if there are no children.
141 | ///
142 | public Block LastChild { get; set; }
143 |
144 | ///
145 | /// Gets or sets the parent element of this block.
146 | ///
147 | public Block Parent { get; set; }
148 |
149 | ///
150 | /// Gets or sets the root element (that represents the document itself).
151 | ///
152 | public Block Top { get; set; }
153 |
154 | ///
155 | /// Gets or sets the string content of this block. The content consists of multiple string parts to avoid string concatenation.
156 | /// Note that some parts of the parser (for example, ) might assume that
157 | /// the parts are not split within certain objects, so it is advised that the parts are split on newline.
158 | ///
159 | public StringContent StringContent { get; set; }
160 |
161 | ///
162 | /// Gets or sets the first inline element that was parsed from property.
163 | /// Note that once the inlines are parsed, will be set to null .
164 | ///
165 | public Inline InlineContent { get; set; }
166 |
167 | ///
168 | /// Gets or sets the additional properties that apply to list elements.
169 | ///
170 | public ListData ListData { get; set; }
171 |
172 | ///
173 | /// Gets or sets the additional properties that apply to fenced code blocks.
174 | ///
175 | public FencedCodeData FencedCodeData { get; set; }
176 |
177 | ///
178 | /// Gets or sets the additional properties that apply to heading elements.
179 | ///
180 | public HeadingData Heading { get; set; }
181 |
182 | ///
183 | /// Gets or sets the heading level (as in <h1> or <h2> ).
184 | ///
185 | [Obsolete("Use " + nameof(Heading) + " instead.")]
186 | public int HeaderLevel
187 | {
188 | get { return Heading.Level; }
189 | set { Heading = new HeadingData(value); }
190 | }
191 |
192 | ///
193 | /// Gets or sets the additional properties that apply to document nodes.
194 | ///
195 | public DocumentData Document { get; set; }
196 |
197 | ///
198 | /// Obsolete. Use instead.
199 | ///
200 | [Obsolete("Use " + nameof(Document) + " instead.")]
201 | public Dictionary ReferenceMap
202 | {
203 | get { return Document?.ReferenceMap; }
204 | set
205 | {
206 | if (Document == null)
207 | {
208 | if (value == null)
209 | {
210 | return;
211 | }
212 | Document = new DocumentData();
213 | }
214 | Document.ReferenceMap = value;
215 | }
216 | }
217 |
218 | ///
219 | /// Gets or sets the next sibling of this block element. null if this is the last element.
220 | ///
221 | public Block NextSibling { get; set; }
222 |
223 | ///
224 | /// Gets or sets the previous sibling of this block element. null if this is the first element.
225 | ///
226 | [Obsolete("This property will be removed in future. If you have a use case where this property is required, please log an issue at https://github.com/Knagis/CommonMark.NET", false)]
227 | [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
228 | [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
229 | public Block Previous { get; set; }
230 | }
231 | }
232 |
--------------------------------------------------------------------------------