Skip to content
This repository was archived by the owner on Feb 25, 2026. It is now read-only.
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
This control was originally written by [Quinn Damerell](https://github.com/QuinnDamerell) and [Paul Bartrum](https://github.com/paulbartrum) for [Baconit](https://github.com/QuinnDamerell/Baconit), a popular open source reddit UWP. The control *almost* supports the full markdown syntax, with a focus on super-efficient parsing and rendering. The control is efficient enough to be used in virtualizing lists.

 

*Note:* For a full list of markdown syntax, see the [official syntax guide](http://daringfireball.net/projects/markdown/syntax).

 
Expand All @@ -10,6 +8,19 @@

 

# COMMENTS

Comments can be added in Markdown, and they won't be rendered to the screen.

To create a comment, enclose in XML style comment tags:

><\!-- Comments are now Implemented -->

There is a Comment below this line.
<!-- Comments are now Implemented -->

&nbsp;

# PARAGRAPHS

Paragraphs are delimited by a blank line. Simply starting text on a new line won't create a new paragraph; It will remain on the same line in the final, rendered version as the previous line. You need an extra, blank line to start a new paragraph. This is especially important when dealing with quotes and, to a lesser degree, lists.
Expand Down Expand Up @@ -49,6 +60,18 @@ is displayed as:

>This sentence includes **bold text**.

### Bold & Italics

Text can be displayed in a bold font by surrounding a word or words with either triple asterisks (\*) or triple underscores (\_).

For example:

>This sentence includes \*\*\*bold & italic text\*\*\*.

is displayed as:

>This sentence includes ***bold & italic text***.

### Strikethrough

Text can be displayed in a strikethrough font by surrounding a word or words with double tildes (~~). For example:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,10 @@ private void RenderInlineChildren(InlineCollection inlineCollection, IList<Markd
{
foreach (MarkdownInline element in inlineElements)
{
RenderInline(inlineCollection, element, parent, context);
if (element.Type != MarkdownInlineType.Comment)
{
RenderInline(inlineCollection, element, parent, context);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ internal class Common
{
internal enum InlineParseMethod
{
/// <summary>
/// A Comment text
/// </summary>
Comment,

/// <summary>
/// A bold element
/// </summary>
Expand All @@ -35,6 +40,11 @@ internal enum InlineParseMethod
/// </summary>
Italic,

/// <summary>
/// An bold and italic block
/// </summary>
BoldItalic,

/// <summary>
/// A link block
/// </summary>
Expand Down Expand Up @@ -106,10 +116,12 @@ internal class InlineTripCharHelper

static Common()
{
BoldItalicTextInline.AddTripChars(_triggerList);
BoldTextInline.AddTripChars(_triggerList);
ItalicTextInline.AddTripChars(_triggerList);
MarkdownLinkInline.AddTripChars(_triggerList);
HyperlinkInline.AddTripChars(_triggerList);
CommentInline.AddTripChars(_triggerList);
StrikethroughTextInline.AddTripChars(_triggerList);
SuperscriptTextInline.AddTripChars(_triggerList);
CodeInline.AddTripChars(_triggerList);
Expand Down Expand Up @@ -219,6 +231,12 @@ private static InlineParseResult FindNextInlineElement(string markdown, int star
InlineParseResult parseResult = null;
switch (currentTripChar.Method)
{
case InlineParseMethod.BoldItalic:
parseResult = BoldItalicTextInline.Parse(markdown, pos, end);
break;
case InlineParseMethod.Comment:
parseResult = CommentInline.Parse(markdown, pos, end);
break;
case InlineParseMethod.Bold:
parseResult = BoldTextInline.Parse(markdown, pos, end);
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// ******************************************************************
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
// ******************************************************************

using System.Collections.Generic;
using Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Helpers;

namespace Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Parse
{
/// <summary>
/// Represents a span containing bold italic text.
/// </summary>
internal class BoldItalicTextInline : MarkdownInline, IInlineContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="BoldItalicTextInline"/> class.
/// </summary>
public BoldItalicTextInline()
: base(MarkdownInlineType.Bold)
{
}

/// <summary>
/// Gets or sets the contents of the inline.
/// </summary>
public IList<MarkdownInline> Inlines { get; set; }

/// <summary>
/// Returns the chars that if found means we might have a match.
/// </summary>
internal static void AddTripChars(List<Common.InlineTripCharHelper> tripCharHelpers)
{
tripCharHelpers.Add(new Common.InlineTripCharHelper() { FirstChar = '*', Method = Common.InlineParseMethod.BoldItalic });
tripCharHelpers.Add(new Common.InlineTripCharHelper() { FirstChar = '_', Method = Common.InlineParseMethod.BoldItalic });
}

/// <summary>
/// Attempts to parse a bold text span.
/// </summary>
/// <param name="markdown"> The markdown text. </param>
/// <param name="start"> The location to start parsing. </param>
/// <param name="maxEnd"> The location to stop parsing. </param>
/// <returns> A parsed bold text span, or <c>null</c> if this is not a bold text span. </returns>
internal static Common.InlineParseResult Parse(string markdown, int start, int maxEnd)
{
if (start >= maxEnd - 1)
{
return null;
}

if (markdown == null || markdown.Length < 6)
{
return null;
}

// Check the start sequence.
string startSequence = markdown.Substring(start, 3);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes an Exception when at the start of the document, and writing asterisks. Ensure markdown is at least length 6.

if (startSequence != "***" && startSequence != "___")
{
return null;
}

// Find the end of the span. The end sequence (either '***' or '___') must be the same
// as the start sequence.
var innerStart = start + 3;
int innerEnd = Common.IndexOf(markdown, startSequence, innerStart, maxEnd);
if (innerEnd == -1)
{
return null;
}

// The span must contain at least one character.
if (innerStart == innerEnd)
{
return null;
}

// The first character inside the span must NOT be a space.
if (Common.IsWhiteSpace(markdown[innerStart]))
{
return null;
}

// The last character inside the span must NOT be a space.
if (Common.IsWhiteSpace(markdown[innerEnd - 1]))
{
return null;
}

// We found something!
var bold = new BoldTextInline
{
Inlines = new List<MarkdownInline>
{
new ItalicTextInline
{
Inlines = Common.ParseInlineChildren(markdown, innerStart, innerEnd)
}
}
};
return new Common.InlineParseResult(bold, start, innerEnd + 3);
}

/// <summary>
/// Converts the object into it's textual representation.
/// </summary>
/// <returns> The textual representation of this object. </returns>
public override string ToString()
{
if (Inlines == null)
{
return base.ToString();
}

return "***" + string.Join(string.Empty, Inlines) + "***";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// ******************************************************************
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THE CODE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE CODE OR THE USE OR OTHER DEALINGS IN THE CODE.
// ******************************************************************

using System.Collections.Generic;
using Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Helpers;

namespace Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Parse
{
/// <summary>
/// Represents a span that contains comment.
/// </summary>
internal class CommentInline : MarkdownInline, IInlineContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="CommentInline"/> class.
/// </summary>
public CommentInline()
: base(MarkdownInlineType.Comment)
{
}

/// <summary>
/// Gets or sets the contents of the inline.
/// </summary>
public IList<MarkdownInline> Inlines { get; set; }

/// <summary>
/// Returns the chars that if found means we might have a match.
/// </summary>
internal static void AddTripChars(List<Common.InlineTripCharHelper> tripCharHelpers)
{
tripCharHelpers.Add(new Common.InlineTripCharHelper() { FirstChar = '<', Method = Common.InlineParseMethod.Comment });
}

/// <summary>
/// Attempts to parse a comment span.
/// </summary>
/// <param name="markdown"> The markdown text. </param>
/// <param name="start"> The location to start parsing. </param>
/// <param name="maxEnd"> The location to stop parsing. </param>
/// <returns> A parsed bold text span, or <c>null</c> if this is not a bold text span. </returns>
internal static Common.InlineParseResult Parse(string markdown, int start, int maxEnd)
{
if (start >= maxEnd - 1)
{
return null;
}

// Check the start sequence.
string startSequence = markdown.Substring(start, 4);
if (startSequence != "<!--")
{
return null;
}

// Find the end of the span. The end sequence ('-->')
var innerStart = start + 4;
int innerEnd = Common.IndexOf(markdown, "-->", innerStart, maxEnd);
if (innerEnd == -1)
{
return null;
}

// We found something!
var result = new CommentInline();
result.Inlines = Common.ParseInlineChildren(string.Empty, 0, 0);
return new Common.InlineParseResult(result, start, innerEnd + 3);
}

/// <summary>
/// Converts the object into it's textual representation.
/// </summary>
/// <returns> The textual representation of this object. </returns>
public override string ToString()
{
if (Inlines == null)
{
return base.ToString();
}

return "<!--" + string.Join(string.Empty, Inlines) + "-->";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls.Markdown.Parse
{
internal enum MarkdownInlineType
{
/// <summary>
/// A comment
/// </summary>
Comment,

/// <summary>
/// A text run
/// </summary>
Expand Down