mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-04 21:35:43 +00:00
Add support for task lists (issue #7)
This commit is contained in:
@@ -83,6 +83,7 @@
|
||||
<None Include="Specs\GridTableSpecs.md" />
|
||||
<None Include="Specs\HardlineBreakSpecs.md" />
|
||||
<None Include="Specs\BootstrapSpecs.md" />
|
||||
<None Include="Specs\TaskListSpecs.md" />
|
||||
<None Include="Specs\SmartyPantsSpecs.md" />
|
||||
<None Include="Specs\MediaSpecs.md" />
|
||||
<None Include="Specs\MathSpecs.md" />
|
||||
|
||||
@@ -19190,4 +19190,58 @@ namespace Markdig.Tests
|
||||
TestParser.TestSpec("[With a new text][This is a heading]\n# This is a heading", "<p><a href=\"#this-is-a-heading\">With a new text</a></p>\n<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers");
|
||||
}
|
||||
}
|
||||
// # Extensions
|
||||
//
|
||||
// Adds support for task lists:
|
||||
//
|
||||
// ## TaskLists
|
||||
//
|
||||
// A task list item consist of `[ ]` or `[x]` or `[X]` inside a list item (ordered or unordered)
|
||||
[TestFixture]
|
||||
public partial class TestExtensionsTaskLists
|
||||
{
|
||||
[Test]
|
||||
public void Example001()
|
||||
{
|
||||
// Example 1
|
||||
// Section: Extensions TaskLists
|
||||
//
|
||||
// The following CommonMark:
|
||||
// - [ ] Item1
|
||||
// - [x] Item2
|
||||
// - [ ] Item3
|
||||
// - Item4
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <ul>
|
||||
// <li><input disabled="disabled" type="checkbox" /> Item1</li>
|
||||
// <li><input disabled="disabled" type="checkbox" checked="checked" /> Item2</li>
|
||||
// <li><input disabled="disabled" type="checkbox" /> Item3</li>
|
||||
// <li>Item4</li>
|
||||
// </ul>
|
||||
|
||||
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 1, "Extensions TaskLists");
|
||||
TestParser.TestSpec("- [ ] Item1\n- [x] Item2\n- [ ] Item3\n- Item4", "<ul>\n<li><input disabled=\"disabled\" type=\"checkbox\" /> Item1</li>\n<li><input disabled=\"disabled\" type=\"checkbox\" checked=\"checked\" /> Item2</li>\n<li><input disabled=\"disabled\" type=\"checkbox\" /> Item3</li>\n<li>Item4</li>\n</ul>", "tasklists");
|
||||
}
|
||||
}
|
||||
// A task is not recognized outside a list item:
|
||||
[TestFixture]
|
||||
public partial class TestExtensionsTaskLists
|
||||
{
|
||||
[Test]
|
||||
public void Example002()
|
||||
{
|
||||
// Example 2
|
||||
// Section: Extensions TaskLists
|
||||
//
|
||||
// The following CommonMark:
|
||||
// [ ] This is not a task list
|
||||
//
|
||||
// Should be rendered as:
|
||||
// <p>[ ] This is not a task list</p>
|
||||
|
||||
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 2, "Extensions TaskLists");
|
||||
TestParser.TestSpec("[ ] This is not a task list", "<p>[ ] This is not a task list</p>", "tasklists");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ SOFTWARE.
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("MediaSpecs.md"), "medialinks"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("SmartyPantsSpecs.md"), "smartypants"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("AutoIdentifierSpecs.md"), "autoidentifiers"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("TaskListSpecs.md"), "tasklists"),
|
||||
};
|
||||
var emptyLines = false;
|
||||
var displayEmptyLines = false;
|
||||
|
||||
29
src/Markdig.Tests/Specs/TaskListSpecs.md
Normal file
29
src/Markdig.Tests/Specs/TaskListSpecs.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Extensions
|
||||
|
||||
Adds support for task lists:
|
||||
|
||||
## TaskLists
|
||||
|
||||
A task list item consist of `[ ]` or `[x]` or `[X]` inside a list item (ordered or unordered)
|
||||
|
||||
```````````````````````````````` example
|
||||
- [ ] Item1
|
||||
- [x] Item2
|
||||
- [ ] Item3
|
||||
- Item4
|
||||
.
|
||||
<ul>
|
||||
<li><input disabled="disabled" type="checkbox" /> Item1</li>
|
||||
<li><input disabled="disabled" type="checkbox" checked="checked" /> Item2</li>
|
||||
<li><input disabled="disabled" type="checkbox" /> Item3</li>
|
||||
<li>Item4</li>
|
||||
</ul>
|
||||
````````````````````````````````
|
||||
|
||||
A task is not recognized outside a list item:
|
||||
|
||||
```````````````````````````````` example
|
||||
[ ] This is not a task list
|
||||
.
|
||||
<p>[ ] This is not a task list</p>
|
||||
````````````````````````````````
|
||||
35
src/Markdig/Extensions/TaskLists/HtmlTaskListRenderer.cs
Normal file
35
src/Markdig/Extensions/TaskLists/HtmlTaskListRenderer.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
|
||||
namespace Markdig.Extensions.TaskLists
|
||||
{
|
||||
/// <summary>
|
||||
/// A HTML renderer for a <see cref="TaskList"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.Renderers.Html.HtmlObjectRenderer{TaskList}" />
|
||||
public class HtmlTaskListRenderer : HtmlObjectRenderer<TaskList>
|
||||
{
|
||||
protected override void Write(HtmlRenderer renderer, TaskList obj)
|
||||
{
|
||||
if (renderer.EnableHtmlForInline)
|
||||
{
|
||||
renderer.Write("<input").WriteAttributes(obj).Write(" disabled=\"disabled\" type=\"checkbox\"");
|
||||
if (obj.Checked)
|
||||
{
|
||||
renderer.Write(" checked=\"checked\"");
|
||||
}
|
||||
renderer.Write(" />");
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer.Write('[');
|
||||
renderer.Write(obj.Checked ? "x" : " ");
|
||||
renderer.Write(']');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/Markdig/Extensions/TaskLists/TaskList.cs
Normal file
17
src/Markdig/Extensions/TaskLists/TaskList.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using System.Diagnostics;
|
||||
using Markdig.Syntax.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.TaskLists
|
||||
{
|
||||
/// <summary>
|
||||
/// An inline for TaskList.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("TaskList {Checked}")]
|
||||
public class TaskList : LeafInline
|
||||
{
|
||||
public bool Checked { get; set; }
|
||||
}
|
||||
}
|
||||
33
src/Markdig/Extensions/TaskLists/TaskListExtension.cs
Normal file
33
src/Markdig/Extensions/TaskLists/TaskListExtension.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using Markdig.Parsers.Inlines;
|
||||
using Markdig.Renderers;
|
||||
|
||||
namespace Markdig.Extensions.TaskLists
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension to enable TaskList.
|
||||
/// </summary>
|
||||
public class TaskListExtension : IMarkdownExtension
|
||||
{
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
if (!pipeline.InlineParsers.Contains<TaskListInlineParser>())
|
||||
{
|
||||
// Insert the parser after the code span parser
|
||||
pipeline.InlineParsers.InsertBefore<LinkInlineParser>(new TaskListInlineParser());
|
||||
}
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
{
|
||||
htmlRenderer.ObjectRenderers.AddIfNotAlready<HtmlTaskListRenderer>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/Markdig/Extensions/TaskLists/TaskListInlineParser.cs
Normal file
62
src/Markdig/Extensions/TaskLists/TaskListInlineParser.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Alexandre Mutel. All rights reserved.
|
||||
// This file is licensed under the BSD-Clause 2 license.
|
||||
// See the license.txt file in the project root for more information.
|
||||
using Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Extensions.TaskLists
|
||||
{
|
||||
/// <summary>
|
||||
/// The inline parser for SmartyPants.
|
||||
/// </summary>
|
||||
public class TaskListInlineParser : InlineParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TaskListInlineParser"/> class.
|
||||
/// </summary>
|
||||
public TaskListInlineParser()
|
||||
{
|
||||
OpeningCharacters = new[] {'['};
|
||||
}
|
||||
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
// A tasklist is either
|
||||
// [ ]
|
||||
// or [x] or [X]
|
||||
|
||||
if (!(processor.Block.Parent is ListItemBlock))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var startingPosition = slice.Start;
|
||||
var c = slice.NextChar();
|
||||
if (!c.IsSpace() && c != 'x' && c != 'X')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (slice.NextChar() != ']')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Skip last ]
|
||||
slice.NextChar();
|
||||
|
||||
// Create the TaskList
|
||||
int line;
|
||||
int column;
|
||||
var taskItem = new TaskList()
|
||||
{
|
||||
SourceStartPosition = processor.GetSourcePosition(startingPosition, out line, out column),
|
||||
Line = line,
|
||||
Column = column,
|
||||
Checked = !c.IsSpace()
|
||||
};
|
||||
taskItem.SourceEndPosition = taskItem.SourceStartPosition + 2;
|
||||
processor.Inline = taskItem;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ using Markdig.Extensions.Mathematics;
|
||||
using Markdig.Extensions.MediaLinks;
|
||||
using Markdig.Extensions.SmartyPants;
|
||||
using Markdig.Extensions.Tables;
|
||||
using Markdig.Extensions.TaskLists;
|
||||
using Markdig.Parsers;
|
||||
using Markdig.Parsers.Inlines;
|
||||
|
||||
@@ -53,6 +54,7 @@ namespace Markdig
|
||||
.UseMediaLinks()
|
||||
.UsePipeTables()
|
||||
.UseListExtras()
|
||||
.UseTaskLists()
|
||||
.UseGenericAttributes(); // Must be last as it is one parser that is modifying other parsers
|
||||
}
|
||||
|
||||
@@ -67,6 +69,17 @@ namespace Markdig
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the task list extension.
|
||||
/// </summary>
|
||||
/// <param name="pipeline">The pipeline.</param>
|
||||
/// <returns>The modified pipeline</returns>
|
||||
public static MarkdownPipelineBuilder UseTaskLists(this MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
pipeline.Extensions.AddIfNotAlready<TaskListExtension>();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the custom container extension.
|
||||
/// </summary>
|
||||
@@ -407,6 +420,9 @@ namespace Markdig
|
||||
case "autoidentifiers":
|
||||
pipeline.UseAutoIdentifiers();
|
||||
break;
|
||||
case "tasklists":
|
||||
pipeline.UseTaskLists();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"unknown extension {extension}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user