/// <summary> /// Convert a Markdown element to a Forkdown one. /// </summary> /// <param name="mdo">Markdown object to convert.</param> public static Element ToForkdown(IMarkdownObject mdo) { Element result = mdo switch { MarkdownDocument _ => new Document(), HeadingBlock h => new Heading(h), ListBlock l => new Listing(l), ListItemBlock li => new ListItem(li), ParagraphBlock p => new Paragraph(p), CustomContainer c => new Section(c), CustomContainerInline c => new ExplicitInlineContainer(c), CodeInline c => new Code(c), FencedCodeBlock c => new CodeBlock(c), LinkInline l => new Link(l), LiteralInline t => new Text(t), LineBreakInline _ => new LineBreak(), ThematicBreakBlock _ => new Separator(), Tables.Table t => new Table(t), Tables.TableRow tr => new TableRow(tr), Tables.TableCell tc => new TableCell(tc), EmphasisInline e => new Emphasis(e), HtmlBlock b => new Html(b), _ => new Placeholder(mdo), }; var subs = mdo switch { LeafBlock b => b.Inline, IEnumerable <MarkdownObject> e => e, _ => null } ?? Nil.E <MarkdownObject>();
public void Heading_should_construct_from_HeadingBlock_correctly() { var headingBlock = new HeadingBlock(new ParagraphBlockParser()); headingBlock.Inline = new ContainerInline(); var firstContent = new LiteralInline("myheading"); headingBlock.Inline.AppendChild(firstContent); var firstHeading = new Heading(headingBlock); Assert.AreEqual("myheading", firstHeading.Title); var secondContent = new LiteralInline("-myotherheading"); headingBlock.Inline.AppendChild(secondContent); var secondHeading = new Heading(headingBlock); Assert.AreEqual("myheading-myotherheading", secondHeading.Title); var codeContent = new CodeInline(); codeContent.Content = "awesomeclassname"; headingBlock.Inline.AppendChild(codeContent); var codeHeading = new Heading(headingBlock); Assert.AreEqual("myheading-myotherheadingawesomeclassname", codeHeading.Title); }
/// <summary> /// Renders a code element /// </summary> /// <param name="inlineCollection"> The list to add to. </param> /// <param name="element"> The parsed inline element to render. </param> /// <param name="parent"> The container element. </param> /// <param name="context"> Persistent state. </param> private void RenderCodeRun(InlineCollection inlineCollection, CodeInline element, TextElement parent, RenderContext context) { var run = new Run(); run.FontFamily = CodeFontFamily ?? FontFamily; run.Text = CollapseWhitespace(context, element.Text); // Add it to the current inlines inlineCollection.Add(run); }
public void Setup() { document = new Document(); // Workaround for a quirk in the migradoc API. _ = document.AddSection().Elements; pdfBuilder = new PdfBuilder(document, PdfOptions.Default); renderer = new CodeInlineRenderer(); inline = new CodeInline(); inline.Content = sampleCode = "sample code"; }
/// <summary> /// Renders a code element /// </summary> /// <param name="element"> The parsed inline element to render. </param> /// <param name="context"> Persistent state. </param> protected override void RenderCodeRun(CodeInline element, IRenderContext context) { var localContext = context as InlineRenderContext; if (localContext == null) { throw new RenderContextIncorrectException(); } var text = CreateTextBlock(localContext); text.Text = CollapseWhitespace(context, element.Text); text.FontFamily = InlineCodeFontFamily ?? FontFamily; if (localContext.WithinItalics) { text.FontStyle = FontStyle.Italic; } if (localContext.WithinBold) { text.FontWeight = FontWeights.Bold; } var borderthickness = InlineCodeBorderThickness; var padding = InlineCodePadding; var spacingoffset = -(borderthickness.Bottom + padding.Bottom); var margin = new Thickness(0, spacingoffset, 0, spacingoffset); var border = new Border { BorderThickness = borderthickness, BorderBrush = InlineCodeBorderBrush, Background = InlineCodeBackground, Child = text, Padding = padding, Margin = margin }; // Aligns content in InlineUI, see https://social.msdn.microsoft.com/Forums/silverlight/en-US/48b5e91e-efc5-4768-8eaf-f897849fcf0b/richtextbox-inlineuicontainer-vertical-alignment-issue?forum=silverlightarchieve border.RenderTransform = new TranslateTransform { Y = 4 }; var inlineUIContainer = new InlineUIContainer { Child = border, }; // Add it to the current inlines localContext.InlineCollection.Add(inlineUIContainer); }
static Common() { BoldTextInline.AddTripChars(_triggerList); ItalicTextInline.AddTripChars(_triggerList); MarkdownLinkInline.AddTripChars(_triggerList); HyperlinkInline.AddTripChars(_triggerList); StrikethroughTextInline.AddTripChars(_triggerList); SuperscriptTextInline.AddTripChars(_triggerList); CodeInline.AddTripChars(_triggerList); // Create an array of characters to search against using IndexOfAny. _tripCharacters = _triggerList.Select(trigger => trigger.FirstChar).Distinct().ToArray(); }
/// <summary> /// Renders a code element. /// </summary> /// <param name="inlineCollection"> The list to add to. </param> /// <param name="element"> The parsed inline element to render. </param> /// <param name="context"> Persistent state. </param> private void RenderCodeRun(InlineCollection inlineCollection, CodeInline element, RenderContext context) { var color = ((SolidColorBrush)Foreground).Color; color.A = 160; var run = new Run { FontFamily = CodeFontFamily ?? FontFamily, Text = CollapseWhitespace(context, element.Text), Foreground = new SolidColorBrush(color), }; // Add it to the current inlines inlineCollection.Add(run); }
/// <summary> /// Renders a code element /// </summary> /// <param name="element"> The parsed inline element to render. </param> /// <param name="context"> Persistent state. </param> protected override void RenderCodeRun(CodeInline element, IRenderContext context) { if (!(context is InlineRenderContext localContext)) { throw new RenderContextIncorrectException(); } var text = CreateTextBlock(localContext); text.Text = CollapseWhitespace(context, element.Text); text.FontFamily = InlineCodeFontFamily ?? FontFamily; text.Foreground = InlineCodeForeground ?? Foreground; if (localContext.WithinItalics) { text.FontStyle = FontStyles.Italic; } if (localContext.WithinBold) { text.FontWeight = FontWeights.Bold; } var border = new Border { BorderThickness = InlineCodeBorderThickness, BorderBrush = InlineCodeBorderBrush, Background = InlineCodeBackground, Child = text, Padding = InlineCodePadding, Margin = InlineCodeMargin }; // Aligns content in InlineUI, see https://social.msdn.microsoft.com/Forums/silverlight/en-US/48b5e91e-efc5-4768-8eaf-f897849fcf0b/richtextbox-inlineuicontainer-vertical-alignment-issue?forum=silverlightarchieve border.RenderTransform = new TranslateTransform { Y = 4 }; var codeInline = new Inlines.CodeUIElementInline { Child = border, Text = text.Text }; localContext.InlineCollection.Add(codeInline); }
static Common() { BoldItalicTextInline.AddTripChars(_triggerList); UnderlineTextInline.AddTripChars(_triggerList); BoldTextInline.AddTripChars(_triggerList); ItalicTextInline.AddTripChars(_triggerList); MarkdownLinkInline.AddTripChars(_triggerList); HyperlinkInline.AddTripChars(_triggerList); StrikethroughTextInline.AddTripChars(_triggerList); CodeInline.AddTripChars(_triggerList); ImageInline.AddTripChars(_triggerList); EmojiInline.AddTripChars(_triggerList); LinkAnchorInline.AddTripChars(_triggerList); DiscordInline.AddTripChars(_triggerList); SpoilerTextInline.AddTripChars(_triggerList); // Create an array of characters to search against using IndexOfAny. _tripCharacters = _triggerList.Select(trigger => trigger.FirstChar).Distinct().ToArray(); }
/// <summary> /// Renders a code element /// </summary> /// <param name="element"> The parsed inline element to render. </param> /// <param name="context"> Persistent state. </param> protected override void RenderCodeRun(CodeInline element, IRenderContext context) { var localContext = context as InlineRenderContext; if (localContext == null) { throw new RenderContextIncorrectException(); } var text = CollapseWhitespace(context, element.Text); // Avoid a crash if the current inline is inside an hyperline. // This happens when using inline code blocks like [`SomeCode`](https://www.foo.bar). if (localContext.Parent is Hyperlink) { // Fallback span Run run = new Run { Text = text, FontFamily = InlineCodeFontFamily ?? FontFamily, Foreground = InlineCodeForeground ?? Foreground }; // Additional formatting if (localContext.WithinItalics) { run.FontStyle = FontStyle.Italic; } if (localContext.WithinBold) { run.FontWeight = FontWeights.Bold; } // Add the fallback block localContext.InlineCollection.Add(run); } else { var textBlock = CreateTextBlock(localContext); textBlock.Text = text; textBlock.FontFamily = InlineCodeFontFamily ?? FontFamily; textBlock.Foreground = InlineCodeForeground ?? Foreground; if (localContext.WithinItalics) { textBlock.FontStyle = FontStyle.Italic; } if (localContext.WithinBold) { textBlock.FontWeight = FontWeights.Bold; } var inlineUIContainer = new InlineUIContainer { Child = new Border { BorderThickness = InlineCodeBorderThickness, BorderBrush = InlineCodeBorderBrush, Background = InlineCodeBackground, Child = textBlock, Padding = InlineCodePadding, Margin = InlineCodeMargin, // Aligns content in InlineUI, see https://social.msdn.microsoft.com/Forums/silverlight/en-US/48b5e91e-efc5-4768-8eaf-f897849fcf0b/richtextbox-inlineuicontainer-vertical-alignment-issue?forum=silverlightarchieve RenderTransform = new TranslateTransform { Y = 4 } } }; // Add it to the current inlines localContext.InlineCollection.Add(inlineUIContainer); } }
/// <summary> /// Finds the next inline element by matching trip chars and verifying the match. /// </summary> /// <param name="markdown"> The markdown text to parse. </param> /// <param name="start"> The position to start parsing. </param> /// <param name="end"> The position to stop parsing. </param> /// <param name="ignoreLinks"> Indicates whether to parse links. </param> /// <returns>Returns the next element</returns> private static InlineParseResult FindNextInlineElement(string markdown, int start, int end, bool ignoreLinks) { // Search for the next inline sequence. for (int pos = start; pos < end; pos++) { // IndexOfAny should be the fastest way to skip characters we don't care about. pos = markdown.IndexOfAny(_tripCharacters, pos, end - pos); if (pos < 0) { break; } // Find the trigger(s) that matched. char currentChar = markdown[pos]; foreach (InlineTripCharHelper currentTripChar in _triggerList) { // Check if our current char matches the suffix char. if (currentChar == currentTripChar.FirstChar) { // Don't match if the previous character was a backslash. if (pos > start && markdown[pos - 1] == '\\') { continue; } // If we are here we have a possible match. Call into the inline class to verify. 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.LinkReference: parseResult = LinkAnchorInline.Parse(markdown, pos, end); break; case InlineParseMethod.Bold: parseResult = BoldTextInline.Parse(markdown, pos, end); break; case InlineParseMethod.Italic: parseResult = ItalicTextInline.Parse(markdown, pos, end); break; case InlineParseMethod.MarkdownLink: if (!ignoreLinks) { parseResult = MarkdownLinkInline.Parse(markdown, pos, end); } break; case InlineParseMethod.AngleBracketLink: if (!ignoreLinks) { parseResult = HyperlinkInline.ParseAngleBracketLink(markdown, pos, end); } break; case InlineParseMethod.Url: if (!ignoreLinks) { parseResult = HyperlinkInline.ParseUrl(markdown, pos, end); } break; case InlineParseMethod.RedditLink: if (!ignoreLinks) { parseResult = HyperlinkInline.ParseRedditLink(markdown, pos, end); } break; case InlineParseMethod.PartialLink: if (!ignoreLinks) { parseResult = HyperlinkInline.ParsePartialLink(markdown, pos, end); } break; case InlineParseMethod.Email: if (!ignoreLinks) { parseResult = HyperlinkInline.ParseEmailAddress(markdown, start, pos, end); } break; case InlineParseMethod.Strikethrough: parseResult = StrikethroughTextInline.Parse(markdown, pos, end); break; case InlineParseMethod.Superscript: parseResult = SuperscriptTextInline.Parse(markdown, pos, end); break; case InlineParseMethod.Subscript: parseResult = SubscriptTextInline.Parse(markdown, pos, end); break; case InlineParseMethod.Code: parseResult = CodeInline.Parse(markdown, pos, end); break; case InlineParseMethod.Image: parseResult = ImageInline.Parse(markdown, pos, end); break; case InlineParseMethod.Emoji: parseResult = EmojiInline.Parse(markdown, pos, end); break; } if (parseResult != null) { return(parseResult); } } } } // If we didn't find any elements we have a normal text block. // Let us consume the entire range. return(new InlineParseResult(TextRunInline.Parse(markdown, start, end), start, end)); }
/// <summary> /// Renders a code element /// </summary> /// <param name="element"> The parsed inline element to render. </param> /// <param name="context"> Persistent state. </param> protected abstract void RenderCodeRun(CodeInline element, IRenderContext context);
protected virtual void AddCodeInLine(CodeInline codeInline) => AddText(codeInline.Content, t => { t.Colour = Color4.Orange; });
protected override void AddCodeInLine(CodeInline codeInline) => AddDrawable(new OsuMarkdownInlineCode { Text = codeInline.Content });
// TODO : Add background (colour B6) and change font to monospace protected override void AddCodeInLine(CodeInline codeInline) => AddText(codeInline.Content, t => { t.Colour = colourProvider.Light1; });
protected override void RenderCodeRun(CodeInline element, IRenderContext context) { //throw new NotImplementedException(); }
public override bool Match(InlineProcessor processor, ref StringSlice slice) { var match = slice.CurrentChar; if (slice.PeekCharExtra(-1) == match) { return(false); } var startPosition = slice.Start; // Match the opened sticks int openSticks = slice.CountAndSkipChar(match); int contentStart = slice.Start; int closeSticks = 0; char c = slice.CurrentChar; var builder = new ValueStringBuilder(stackalloc char[ValueStringBuilder.StackallocThreshold]); // A backtick string is a string of one or more backtick characters (`) that is neither preceded nor followed by a backtick. // A code span begins with a backtick string and ends with a backtick string of equal length. // The contents of the code span are the characters between the two backtick strings, normalized in the following ways: // 1. line endings are converted to spaces. // 2. If the resulting string both begins AND ends with a space character, but does not consist entirely // of space characters, a single space character is removed from the front and back. // This allows you to include code that begins or ends with backtick characters, which must be separated by // whitespace from the opening or closing backtick strings. bool allSpace = true; bool containsNewLine = false; var contentEnd = -1; while (c != '\0') { // Transform '\n' into a single space if (c == '\n') { containsNewLine = true; c = ' '; } else if (c == '\r') { containsNewLine = true; slice.SkipChar(); c = slice.CurrentChar; continue; } if (c == match) { contentEnd = slice.Start; closeSticks = slice.CountAndSkipChar(match); if (openSticks == closeSticks) { break; } allSpace = false; builder.Append(match, closeSticks); c = slice.CurrentChar; } else { builder.Append(c); if (c != ' ') { allSpace = false; } c = slice.NextChar(); } } bool isMatching = false; if (closeSticks == openSticks) { ReadOnlySpan <char> contentSpan = builder.AsSpan(); var content = containsNewLine ? new LazySubstring(contentSpan.ToString()) : new LazySubstring(slice.Text, contentStart, contentSpan.Length); Debug.Assert(contentSpan.SequenceEqual(content.AsSpan())); // Remove one space from front and back if the string is not all spaces if (!allSpace && contentSpan.Length > 2 && contentSpan[0] == ' ' && contentSpan[contentSpan.Length - 1] == ' ') { content.Offset++; content.Length -= 2; } int delimiterCount = Math.Min(openSticks, closeSticks); var spanStart = processor.GetSourcePosition(startPosition, out int line, out int column); var spanEnd = processor.GetSourcePosition(slice.Start - 1); var codeInline = new CodeInline(content) { Delimiter = match, Span = new SourceSpan(spanStart, spanEnd), Line = line, Column = column, DelimiterCount = delimiterCount, }; if (processor.TrackTrivia) { codeInline.ContentWithTrivia = new StringSlice(slice.Text, contentStart, contentEnd - 1); } processor.Inline = codeInline; isMatching = true; } builder.Dispose(); return(isMatching); }
protected override void RenderCodeRun(CodeInline element, IRenderContext context) { }
public Code(CodeInline code) : base(code) => this.Content = code.Content;