public static Paragraph Parse(string text, Style?style = null) { if (text is null) { throw new ArgumentNullException(nameof(text)); } style ??= Style.Plain; var result = new Paragraph(); using var tokenizer = new MarkupTokenizer(text); var stack = new Stack <Style>(); while (tokenizer.MoveNext()) { var token = tokenizer.Current; if (token == null) { break; } if (token.Kind == MarkupTokenKind.Open) { var parsedStyle = StyleParser.Parse(token.Value); stack.Push(parsedStyle); } else if (token.Kind == MarkupTokenKind.Close) { if (stack.Count == 0) { throw new InvalidOperationException($"Encountered closing tag when none was expected near position {token.Position}."); } stack.Pop(); } else if (token.Kind == MarkupTokenKind.Text) { // Get the effective style. var effectiveStyle = style.Combine(stack.Reverse()); result.Append(Emoji.Replace(token.Value), effectiveStyle); } else { throw new InvalidOperationException("Encountered unknown markup token."); } } if (stack.Count > 0) { throw new InvalidOperationException("Unbalanced markup stack. Did you forget to close a tag?"); } return(result); }
/// <summary> /// Removes markup from the specified string. /// </summary> /// <param name="text">The text to remove markup from.</param> /// <returns>A string that does not have any markup.</returns> public static string RemoveMarkup(this string?text) { if (string.IsNullOrWhiteSpace(text)) { return(string.Empty); } var result = new StringBuilder(); var tokenizer = new MarkupTokenizer(text); while (tokenizer.MoveNext() && tokenizer.Current != null) { if (tokenizer.Current.Kind == MarkupTokenKind.Text) { result.Append(tokenizer.Current.Value); } } return(result.ToString()); }
/// <summary> /// Parse the markup to RichTextParts and add them to the markuptextcontrol. /// </summary> /// <param name="markup"></param> public void ParseMarkup(string markup) { MarkupTokenizer tokonizer = new MarkupTokenizer(markup); tokonizer.FindTokens(); MarkupToken[] markuptokens = tokonizer.GetTokens(); SolidBrush solidbrushTextcolorDefault = new SolidBrush(Color.Black); RichTextPart richtextpart = null; bool previous_markuptokon_hyperlinktext = false; for (int i = 0; i < markuptokens.Length; ++i) { if (markuptokens[i].IsLine) { richtextpart = new RichTextPart(Pens.Black); this.markuptextcontrol.Append(richtextpart); } else { switch (markuptokens[i].CharLeft) { case '#': // head int headlevel = markuptokens[i].Level; richtextpart = new RichTextPart(markuptokens[i].Text, this.fontfamily, this.headsfontsizes[headlevel], solidbrushTextcolorDefault, FontStyle.Bold); richtextpart.AppendNewLine = true; this.markuptextcontrol.Append(richtextpart); previous_markuptokon_hyperlinktext = false; break; case '-': // list richtextpart = new RichTextPart(markuptokens[i].Text, this.fontfamily, this.fontsizedefault, solidbrushTextcolorDefault, FontStyle.Regular); richtextpart.PrependBullit = true; richtextpart.AppendNewLine = true; this.markuptextcontrol.Append(richtextpart); previous_markuptokon_hyperlinktext = false; break; case '*': case '_': // emphasis int emphasislevel = markuptokens[i].Level; int emphasisfontstyle = (int)FontStyle.Italic; if (emphasislevel >= 1) { emphasisfontstyle = (int)FontStyle.Bold; } if (emphasislevel >= 2) { emphasisfontstyle += (int)FontStyle.Italic; } richtextpart = new RichTextPart(markuptokens[i].Text, this.fontfamily, this.fontsizedefault, solidbrushTextcolorDefault, (FontStyle)emphasisfontstyle); this.markuptextcontrol.Append(richtextpart); previous_markuptokon_hyperlinktext = false; break; case '~': // strikethrough richtextpart = new RichTextPart(markuptokens[i].Text, this.fontfamily, this.fontsizedefault, solidbrushTextcolorDefault, FontStyle.Strikeout); this.markuptextcontrol.Append(richtextpart); previous_markuptokon_hyperlinktext = false; break; case '[': // hyperlink text previous_markuptokon_hyperlinktext = true; break; case '(': // hyperlink url if (previous_markuptokon_hyperlinktext && i > 0) { FontStyle fontsylehyperlink = FontStyle.Underline; SolidBrush solidbrushHyperlink = new SolidBrush(Color.Blue); richtextpart = new RichTextPart(markuptokens[i - 1].Text, this.fontfamily, this.fontsizedefault, solidbrushHyperlink, fontsylehyperlink); richtextpart.Href = markuptokens[i].Text; this.markuptextcontrol.Append(richtextpart); } previous_markuptokon_hyperlinktext = false; break; case '`': // TODO: literally and code highlighting previous_markuptokon_hyperlinktext = false; break; default: if (markuptokens[i].Nomarkup) { richtextpart = new RichTextPart(markuptokens[i].Text, this.fontfamily, this.fontsizedefault, solidbrushTextcolorDefault, FontStyle.Regular); richtextpart.AppendNewLine = markuptokens[i].TextAppendNewLine; this.markuptextcontrol.Append(richtextpart); } break; } } } this.markuptextcontrol.Refresh(); }