private MarkdownNode Parse(string rawMarkdown) { MarkdownNode root = new MarkdownNode() { Type = MarkdownType.Root }; _current = root; string[] lines = rawMarkdown.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); foreach (var line in lines) { foreach (var handler in _handlers) { if (handler.HandleLine(_current, line)) { break; } } } return(root); }
public bool HandleLine(MarkdownNode context, string line) { line = line.Trim(); bool isBullet; int skip; if (line.StartsWith("-")) { isBullet = true; skip = 1; } else if (line.StartsWith("#.")) { isBullet = false; skip = 2; } else { return(false); } var bulletNode = new MarkdownNode() { Type = isBullet ? MarkdownType.Bulleted : MarkdownType.Numbered, }; context.AddChild(bulletNode); AppendParsedTextTo(bulletNode, line.Substring(skip).Trim()); return(true); }
public bool HandleLine(MarkdownNode context, string line) { line = line.Trim(); var headingTag = GetHeadingTag(line); if (headingTag == null) { return(false); } MarkdownType type; switch (headingTag) { case "#": type = MarkdownType.Heading1; break; case "##": type = MarkdownType.Heading2; break; case "###": type = MarkdownType.Heading3; break; default: return(false); } string headingText = line.Substring(headingTag.Length).Trim(); var node = new MarkdownNode() { Type = type, Text = headingText }; context.AddChild(node); return(true); }
private bool nextSatisfies(int i, MarkdownNode parent, Func <MarkdownNode, bool> predicate) { i += 1; if (i < 0 || i >= parent.Children.Count) { return(false); } var node = parent.Children[i]; return(predicate(node)); }
public void RemoveChild(MarkdownNode child) { if (child.Parent == this) { _children.Remove(child); child.Parent = null; if (_children.Count == 0) { _children = null; } } }
public void AddChild(MarkdownNode child) { if (child.Parent != null) { child.Parent.RemoveChild(child); } child.Parent = this; if (_children == null) { _children = new List <MarkdownNode>(); } _children.Add(child); }
public bool HandleLine(MarkdownNode context, string line) { line = line.Trim(); var para = new MarkdownNode() { Type = MarkdownType.Paragraph }; context.AddChild(para); AppendParsedTextTo(para, line); return(true); }
protected void AppendParsedTextTo(MarkdownNode parent, string line) { var ranges = Parse(line); Stack <MarkdownNode> stack = new Stack <MarkdownNode>(); foreach (var range in ranges) { if (range.IsEmpty) { throw new Exception("Unexpected empty markdown paragraph range."); } stack.Clear(); stack.Push(new MarkdownNode() { Type = MarkdownType.Text, Text = range.ExtractText(line) }); if (range.Underline) { stack.Push(new MarkdownNode() { Type = MarkdownType.Underline }); } if (range.Italic) { stack.Push(new MarkdownNode() { Type = MarkdownType.Italic }); } if (range.Bold) { stack.Push(new MarkdownNode() { Type = MarkdownType.Bold }); } var node = parent; while (stack.Count > 0) { var child = stack.Pop(); node.AddChild(child); node = child; } } }
private void RenderNode(MarkdownNode node, StringBuilder builder) { string openTag = null; string closeTag = null; switch (node.Type) { case MarkdownType.Heading1: openTag = "<span style=\"font-size: 140%; font-weight: bold\">"; closeTag = "</span>"; break; case MarkdownType.Heading2: openTag = "<span style=\"font-size: 120%; font-weight: bold; font-style=italic;\">"; closeTag = "</span>"; break; case MarkdownType.Heading3: openTag = "<span style=\"font-size: 105%; font-weight: bold\">"; closeTag = "</span>"; break; // case MarkdownType.Bulleted: openTag = "<ul>"; closeTag = "</ul>"; break; // case MarkdownType.Numbered: openTag = "<ol>"; closeTag = "</ol>"; break; case MarkdownType.Bold: openTag = "<b>"; closeTag = "</b>"; break; case MarkdownType.Italic: openTag = "<i>"; closeTag = "</i>"; break; case MarkdownType.Underline: openTag = "<u>"; closeTag = "</u>"; break; //case MarkdownType.BlankLine: openTag = null; closeTag = "<br>"; break; default: break; } if (openTag != null) { builder.Append(openTag); } if (node.Text != null) { builder.Append(WebUtility.HtmlEncode(node.Text)); } for (int i = 0; i < node.Children.Count; ++i) { var child = node.Children[i]; if (child.Type == MarkdownType.Bulleted) { if (!previousSatisfies(i, node, n => n.Type == MarkdownType.Bulleted)) { builder.AppendLine(); builder.AppendLine("<ul>"); } builder.Append("<li>"); RenderNode(child, builder); builder.AppendLine("</li>"); if (!nextSatisfies(i, node, n => n.Type == MarkdownType.Bulleted)) { builder.AppendLine(); builder.AppendLine("</ul>"); } } else if (child.Type == MarkdownType.Numbered) { if (!previousSatisfies(i, node, n => n.Type == MarkdownType.Numbered)) { builder.AppendLine(); builder.AppendLine("<ol>"); } builder.Append("<li>"); RenderNode(child, builder); builder.AppendLine("</li>"); if (!nextSatisfies(i, node, n => n.Type == MarkdownType.Numbered)) { builder.AppendLine(); builder.AppendLine("</ol>"); } } else if (child.Type == MarkdownType.Paragraph) { if (!previousSatisfies(i, node, n => n.Type == MarkdownType.Paragraph)) { builder.AppendLine(); builder.AppendLine("<p>"); } else { builder.AppendLine("<br>"); } RenderNode(child, builder); if (!nextSatisfies(i, node, n => n.Type == MarkdownType.Paragraph)) { builder.AppendLine(); builder.AppendLine("</p>"); } } else { RenderNode(child, builder); } } if (closeTag != null) { if (node.IsInlineModifier) { builder.Append(closeTag); } else { builder.AppendLine(closeTag); } } }
public Markdown(string rawMarkdown) { _root = Parse(rawMarkdown); }