public MarkdownQuoteBlock(QuoteBlock quoteBlock) { this.quoteBlock = quoteBlock; AutoSizeAxes = Axes.Y; RelativeSizeAxes = Axes.X; }
public void QuoteBlockNoTitle() { var block = new QuoteBlock(); var title = block.GetTitle(); Assert.Equal("Empty", title); }
/// <summary> /// Renders a quote element. /// </summary> protected override void RenderQuote(QuoteBlock element, IRenderContext context) { if (!(context is BlockCollectionRenderContext localContext)) { throw new RenderContextIncorrectException(); } var blockCollection = localContext.BlockCollection; var section = new Section { Margin = QuoteMargin, Background = QuoteBackground, BorderThickness = QuoteBorderThickness, Padding = QuotePadding, }; var childContext = new BlockCollectionRenderContext(section.Blocks, context) { Parent = section }; if (QuoteForeground != null && !localContext.OverrideForeground) { childContext.Foreground = QuoteForeground; } RenderBlocks(element.Blocks, childContext); section.BorderBrush = childContext.OverrideForeground ? childContext.Foreground : QuoteBorderBrush ?? childContext.Foreground; blockCollection.Add(section); }
/// <summary> /// Renders a quote element. /// </summary> protected override void RenderQuote(QuoteBlock element, IRenderContext context) { if (!(context is UIElementCollectionRenderContext localContext)) { throw new RenderContextIncorrectException(); } var blockUIElementCollection = localContext.BlockUIElementCollection; var stackPanel = new StackPanel(); var childContext = new UIElementCollectionRenderContext(stackPanel.Children, context) { Parent = stackPanel }; if (QuoteForeground != null && !localContext.OverrideForeground) { childContext.Foreground = QuoteForeground; } RenderBlocks(element.Blocks, childContext); var border = new Border { Margin = QuoteMargin, Background = QuoteBackground, BorderBrush = childContext.OverrideForeground ? childContext.Foreground : QuoteBorderBrush ?? childContext.Foreground, BorderThickness = QuoteBorderThickness, Padding = QuotePadding, Child = stackPanel }; blockUIElementCollection.Add(border); }
public async Task <IActionResult> Quote(Enumeration.ContentItemType contentType, int contentId) { var block = new QuoteBlock(); SetTypeId(block, contentType, contentId); block.LastModified = block.Date = DateTime.UtcNow; await _blockRepository.Create(block); return(ViewComponent("Block", block)); }
public void QuoteBlockHasTitle() { var block = new QuoteBlock() { Body = new Extend.Fields.TextField { Value = "To be or not to be" } }; var title = block.GetTitle(); Assert.Equal("To be or not to be", title); }
public void SearchQuoteBlock() { var block = new QuoteBlock { Body = new TextField { Value = "Lorem ipsum" } }; var search = block.GetIndexedContent(); Assert.Equal(block.Body.Value, search); }
/// <summary> /// Renders a quote element. /// </summary> /// <param name="element"></param> /// <param name="currentBlocks"></param> private void RenderQuote(QuoteBlock element, BlockCollection currentBlocks) { // Make the new quote paragraph Paragraph quotePara = new Paragraph(); quotePara.Margin = new Thickness(element.QuoteIndent * 12, 12, 12, 12); quotePara.Foreground = new SolidColorBrush(Color.FromArgb(180, 255, 255, 255)); // Add it to the blocks currentBlocks.Add(quotePara); // Render the children into the para inline. bool trimTextStart = true; RenderInlineChildren(element, quotePara.Inlines, ref trimTextStart); }
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 QuoteBlockRenderer(); // Setup the sample quote block. quote = new QuoteBlock(new QuoteBlockParser()); quoteText = "some text"; ParagraphBlock paragraph = new ParagraphBlock(new ParagraphBlockParser()); paragraph.Inline = new ContainerInline().AppendChild(new LiteralInline(quoteText)); quote.Add(paragraph); }
private View Render(QuoteBlock block) { var blockQuote = BlockQuoteTemplate.CreateContent() as View; var inner = new StackLayout(); foreach (var view in RenderBlocks(block.AsEnumerable())) { inner.Children.Add(view); } blockQuote.BindingContext = new Templates.BlockQuoteAstNode { View = inner }; return(blockQuote); }
private IEnumerable <View> Render(QuoteBlock block) { var initialIsQuoted = this.isQuoted; //var initialStack = this.stack; var views = new List <View>(); this.isQuoted = true; var stack = new StackLayout() { Spacing = this.Theme.Margin, }; var style = this.Theme.Quote; if (style.BorderSize > 0) { var horizontalStack = new StackLayout() { Orientation = StackOrientation.Horizontal, BackgroundColor = this.Theme.Quote.BackgroundColor, }; horizontalStack.Children.Add(new BoxView() { WidthRequest = style.BorderSize, BackgroundColor = style.BorderColor, }); horizontalStack.Children.Add(stack); views.Add(horizontalStack); } else { stack.BackgroundColor = this.Theme.Quote.BackgroundColor; views.Add(stack); } var subviews = this.Render(block.AsEnumerable()); this.isQuoted = initialIsQuoted; //this.stack = initialStack; return(subviews); }
private OpenXmlElement[] ConvertQuoteBlock(QuoteBlock quoteBlock, int numberingId, int nestLevel) { var styleId = UserSettingStyleMap.GetStyleId(UserSettingStyleMap.StyleMapKeyType.Quote, null); var oxmlElements = new List <OpenXmlElement>(); foreach (var block in quoteBlock) { switch (block) { case ParagraphBlock paragraphBlock: var oxmlParagraph = ConvertParagraphBlock(paragraphBlock, WordDocumentNumberingManager.OutsideOfListNumberingId, 0); var oxmlQuoteParagraph = Manipulator.ElementCreator.CreateQuoteElement(oxmlParagraph, styleId, numberingId, nestLevel); Manipulator.AdjustImageDimension(oxmlQuoteParagraph); oxmlElements.Add(oxmlQuoteParagraph); break; case ListBlock listBlock: var oxmlListItems = ConvertListBlock(listBlock, WordDocumentNumberingManager.OutsideOfListNumberingId, 0); foreach (var oxmlListItem in oxmlListItems) { if (oxmlListItem.GetType() == typeof(Paragraph)) { var oxmlListItemParagraph = (Paragraph)oxmlListItem; var oxmlListItemQuoteParagraph = Manipulator.ElementCreator.CreateQuoteElement(oxmlListItemParagraph, styleId, numberingId, nestLevel); Manipulator.AdjustImageDimension(oxmlListItemQuoteParagraph); oxmlElements.Add(oxmlListItemQuoteParagraph); } } break; default: throw new NotImplementedException(string.Format("Unknown block type within the quote block: {0}", block.GetType().FullName)); } } return(oxmlElements.ToArray()); }
/// <summary> /// Called by all elements to find the next element to parse out of the markdown given a startingPos and an ending Pos /// </summary> /// <param name="markdown"></param> /// <param name="startingPos"></param> /// <param name="endingPost"></param> /// <returns></returns> public static MarkdownBlock FindNextBlock(ref string markdown, ref int startingPos, int endingPos) { // We need to look at the start of this current block and figure out what type it is. // Find the next char that isn't a \n, \r, or ' ', keep track of white space int spaceCount = 0; while (markdown.Length > startingPos && endingPos > startingPos && (markdown[startingPos] == '\r' || markdown[startingPos] == '\n' || Char.IsWhiteSpace(markdown[startingPos]))) { // If we find a space count it for the indent rules. If not reset the count. spaceCount = markdown[startingPos] == ' ' ? spaceCount + 1 : 0; startingPos++; } if (CodeBlock.CanHandleBlock(ref markdown, startingPos, endingPos, spaceCount)) { return(new CodeBlock()); } if (QuoteBlock.CanHandleBlock(ref markdown, startingPos, endingPos)) { return(new QuoteBlock()); } if (HeaderBlock.CanHandleBlock(ref markdown, startingPos, endingPos)) { return(new HeaderBlock()); } if (ListElementBlock.CanHandleBlock(ref markdown, startingPos, endingPos)) { return(new ListElementBlock()); } if (HorizontalRuleBlock.CanHandleBlock(ref markdown, startingPos, endingPos)) { return(new HorizontalRuleBlock()); } if (LineBreakBlock.CanHandleBlock(ref markdown, startingPos, endingPos)) { return(new LineBreakBlock()); } // If we can't match any of these just make a new paragraph. return(new ParagraphBlock()); }
private void Render(QuoteBlock block) { var initialIsQuoted = this.isQuoted; var initialStack = this.stack; this.isQuoted = true; this.stack = new StackLayout() { Spacing = this.Theme.Margin, }; var style = this.Theme.Quote; if (style.BorderSize > 0) { var horizontalStack = new StackLayout() { Orientation = StackOrientation.Horizontal, BackgroundColor = this.Theme.Quote.BackgroundColor, }; horizontalStack.Children.Add(new BoxView() { WidthRequest = style.BorderSize, BackgroundColor = style.BorderColor, }); horizontalStack.Children.Add(this.stack); initialStack.Children.Add(horizontalStack); } else { stack.BackgroundColor = this.Theme.Quote.BackgroundColor; initialStack.Children.Add(this.stack); } this.Render(block.AsEnumerable()); this.isQuoted = initialIsQuoted; this.stack = initialStack; }
void Render(QuoteBlock block) { var initialIsQuoted = isQuoted; var initialStack = stack; isQuoted = true; stack = new StackLayout() { Spacing = Theme.VerticalSpacing, }; var style = Theme.Quote; if (style.BorderSize > 0) { var horizontalStack = new StackLayout() { Orientation = StackOrientation.Horizontal, BackgroundColor = Theme.Quote.BackgroundColor, }; horizontalStack.Children.Add(new BoxView() { WidthRequest = style.BorderSize, BackgroundColor = style.BorderColor, }); horizontalStack.Children.Add(stack); initialStack.Children.Add(horizontalStack); } else { stack.BackgroundColor = Theme.Quote.BackgroundColor; initialStack.Children.Add(stack); } Render(block.AsEnumerable()); isQuoted = initialIsQuoted; stack = initialStack; }
/// <summary> /// Renders a quote element. /// </summary> /// <param name="element"></param> /// <param name="blockUIElementCollection"></param> private void RenderQuote(QuoteBlock element, UIElementCollection blockUIElementCollection, RenderContext context) { if (QuoteForeground != null) { context = context.Clone(); context.Foreground = QuoteForeground; } var stackPanel = new StackPanel(); RenderBlocks(element.Blocks, stackPanel.Children, context); var border = new Border(); border.Margin = QuoteMargin; border.Background = QuoteBackground; border.BorderBrush = QuoteBorderBrush ?? context.Foreground; border.BorderThickness = QuoteBorderThickness; border.Padding = QuotePadding; border.Child = stackPanel; blockUIElementCollection.Add(border); }
/// <summary> /// Parses a markdown document. /// </summary> /// <param name="markdown"> The markdown text. </param> /// <param name="start"> The position to start parsing. </param> /// <param name="end"> The position to stop parsing. </param> /// <param name="quoteDepth"> The current nesting level for block quoting. </param> /// <param name="actualEnd"> Set to the position at which parsing ended. This can be /// different from <paramref name="end"/> when the parser is being called recursively. /// </param> /// <returns> A list of parsed blocks. </returns> internal static List <MarkdownBlock> Parse(string markdown, bool inlineOnly, int start, int end, int quoteDepth, out int actualEnd) { // We need to parse out the list of blocks. // Some blocks need to start on a new paragraph (code, lists and tables) while other // blocks can start on any line (headers, horizontal rules and quotes). // Text that is outside of any other block becomes a paragraph. var blocks = new List <MarkdownBlock>(); var startOfLine = start; var paragraphText = new StringBuilder(); // These are needed to parse underline-style header blocks. var previousRealtStartOfLine = start; var previousStartOfLine = start; var previousEndOfLine = start; var skip = false; // Go line by line. while (startOfLine < end) { // Find the first non-whitespace character. var nonSpacePos = startOfLine; var nonSpaceChar = '\0'; var realStartOfLine = startOfLine; // i.e. including quotes. var expectedQuotesRemaining = quoteDepth; while (true) { while (nonSpacePos < end) { var c = markdown[nonSpacePos]; if (c == '\r' || c == '\n') { // The line is either entirely whitespace, or is empty. break; } if (c != ' ' && c != '\t') { // The line has content. nonSpaceChar = c; break; } nonSpacePos++; } // When parsing blocks in a blockquote context, we need to count the number of // quote characters ('>'). If there are less than expected AND this is the // start of a new paragraph, then stop parsing. if (expectedQuotesRemaining == 0) { break; } if (nonSpaceChar == '>') { // Expected block quote characters should be ignored. expectedQuotesRemaining--; nonSpacePos++; nonSpaceChar = '\0'; startOfLine = nonSpacePos; // Ignore the first space after the quote character, if there is one. if (!(startOfLine < end && markdown[startOfLine] == ' ')) { skip = true; } } else { var lastIndentation = 0; string lastline = null; // Determines how many Quote levels were in the last line. if (realStartOfLine > 0) { lastline = markdown.Substring(previousRealtStartOfLine, previousEndOfLine - previousRealtStartOfLine); lastIndentation = lastline.Count(c => c == '>'); } var currentEndOfLine = Common.FindNextSingleNewLine(markdown, nonSpacePos, end, out _); var currentline = markdown.Substring(realStartOfLine, currentEndOfLine - realStartOfLine); var currentIndentation = currentline.Count(c => c == '>'); var firstChar = markdown[realStartOfLine]; // This is a quote that doesn't start with a Quote marker, but carries on from the last line. if (lastIndentation == 1) { if (nonSpaceChar != '\0' && firstChar != '>') { break; } } // Collapse down a level of quotes if the current indentation is greater than the last indentation. // Only if the last indentation is greater than 1, and the current indentation is greater than 0 if (lastIndentation > 1 && currentIndentation > 0 && currentIndentation < lastIndentation) { break; } // This must be the end of the blockquote. End the current paragraph, if any. actualEnd = realStartOfLine; if (paragraphText.Length > 0) { blocks.Add(ParagraphBlock.Parse(paragraphText.ToString())); } return(blocks); } } // Find the end of the current line. var endOfLine = Common.FindNextSingleNewLine(markdown, nonSpacePos, end, out var startOfNextLine); if (nonSpaceChar == '\0') { // The line is empty or nothing but whitespace. // End the current paragraph. if (paragraphText.Length > 0) { blocks.Add(ParagraphBlock.Parse(paragraphText.ToString())); paragraphText.Clear(); } } else { // This is a header if the line starts with a hash character, // or if the line starts with '-' or a '=' character and has no other characters. // Or a quote if the line starts with a greater than character (optionally preceded by whitespace). // Or a horizontal rule if the line contains nothing but 3 '*', '-' or '_' characters (with optional whitespace). MarkdownBlock newBlockElement = null; if (newBlockElement == null) { // Some block elements must start on a new paragraph (tables, lists and code). var endOfBlock = startOfNextLine; if (newBlockElement == null && nonSpaceChar == '`' && !inlineOnly) { newBlockElement = CodeBlock.Parse(markdown, realStartOfLine, end, quoteDepth, out endOfBlock); } // This check needs to go after the code block check. if (newBlockElement == null && nonSpaceChar == '>' && !inlineOnly && nonSpacePos + 1 < end && markdown[nonSpacePos + 1] == ' ') { newBlockElement = QuoteBlock.Parse(markdown, realStartOfLine, startOfNextLine, quoteDepth, out endOfBlock); } if (newBlockElement != null) { startOfNextLine = endOfBlock; } } // Block elements start new paragraphs. if (newBlockElement == null) { // The line contains paragraph text. if (paragraphText.Length > 0) { paragraphText.Append("\r\n"); } // Add the last paragraph if we are at the end of the input text. if (startOfNextLine >= end) { if (paragraphText.Length == 0) { // Optimize for single line paragraphs. blocks.Add(ParagraphBlock.Parse(markdown.Substring(startOfLine, endOfLine - startOfLine))); } else { // Slow path. paragraphText.Append(markdown.Substring(startOfLine, endOfLine - startOfLine)); blocks.Add(ParagraphBlock.Parse(paragraphText.ToString())); } } else { paragraphText.Append(markdown.Substring(startOfLine, endOfLine - startOfLine)); } } else { // The line contained a block. End the current paragraph, if any. if (paragraphText.Length > 0) { blocks.Add(ParagraphBlock.Parse(paragraphText.ToString())); paragraphText.Clear(); } blocks.Add(newBlockElement); } } // Repeat. previousRealtStartOfLine = realStartOfLine; previousStartOfLine = startOfLine; previousEndOfLine = endOfLine; startOfLine = startOfNextLine; } actualEnd = startOfLine; return(blocks); }
public override BlockState TryOpen(BlockProcessor processor) { if (processor.IsCodeIndent) { return(BlockState.None); } var column = processor.Column; var sourcePosition = processor.Start; // 5.1 Block quotes // A block quote marker consists of 0-3 spaces of initial indent, plus // (a) the character > together with a following space, or // (b) a single character > not followed by a space. // (c) a single character > followed by a space and explanation mark // denoting the css class to apply to the block quote // i.e.... // > !danger This is a dangerous quote // > !success This is a success quote var currentChar = processor.CurrentChar; var c = processor.NextChar(); if (c.IsSpaceOrTab()) { c = processor.NextChar(); } var cssClass = ""; if (c == '!') { // Parse until we reach white space while (true) { if (c.IsWhitespace()) { break; } c = processor.NextChar(); if (!c.IsWhitespace()) { cssClass += c; } } } if (c.IsSpaceOrTab()) { processor.NextColumn(); } // Build quote var quoteBlock = new QuoteBlock(this) { QuoteChar = currentChar, Column = column, Span = new SourceSpan(sourcePosition, processor.Line.End), }; // If we found a css class ensure it's allowed if (!string.IsNullOrEmpty(cssClass)) { var validCss = cssClass.ToLower(); if (_validCssClasses.Contains(validCss)) { quoteBlock.GetAttributes().AddClass(validCss); } } processor.NewBlocks.Push(quoteBlock); return(BlockState.Continue); }
/// <summary> /// Parses a markdown document. /// </summary> /// <param name="markdown"> The markdown text. </param> /// <param name="start"> The position to start parsing. </param> /// <param name="end"> The position to stop parsing. </param> /// <param name="quoteDepth"> The current nesting level for block quoting. </param> /// <param name="actualEnd"> Set to the position at which parsing ended. This can be /// different from <paramref name="end"/> when the parser is being called recursively. /// </param> /// <returns> A list of parsed blocks. </returns> internal static List <MarkdownBlock> Parse(string markdown, int start, int end, int quoteDepth, out int actualEnd) { // We need to parse out the list of blocks. // Some blocks need to start on a new paragraph (code, lists and tables) while other // blocks can start on any line (headers, horizontal rules and quotes). // Text that is outside of any other block becomes a paragraph. var blocks = new List <MarkdownBlock>(); int startOfLine = start; bool lineStartsNewParagraph = true; var paragraphText = new StringBuilder(); // These are needed to parse underline-style header blocks. int previousRealtStartOfLine = start; int previousStartOfLine = start; int previousEndOfLine = start; // Go line by line. while (startOfLine < end) { // Find the first non-whitespace character. int nonSpacePos = startOfLine; char nonSpaceChar = '\0'; int realStartOfLine = startOfLine; // i.e. including quotes. int expectedQuotesRemaining = quoteDepth; while (true) { while (nonSpacePos < end) { char c = markdown[nonSpacePos]; if (c == '\r' || c == '\n') { // The line is either entirely whitespace, or is empty. break; } if (c != ' ' && c != '\t') { // The line has content. nonSpaceChar = c; break; } nonSpacePos++; } // When parsing blocks in a blockquote context, we need to count the number of // quote characters ('>'). If there are less than expected AND this is the // start of a new paragraph, then stop parsing. if (expectedQuotesRemaining == 0) { break; } if (nonSpaceChar == '>') { // Expected block quote characters should be ignored. expectedQuotesRemaining--; nonSpacePos++; nonSpaceChar = '\0'; startOfLine = nonSpacePos; // Ignore the first space after the quote character, if there is one. if (startOfLine < end && markdown[startOfLine] == ' ') { startOfLine++; nonSpacePos++; } } else { int lastIndentation = 0; string lastline = null; // Determines how many Quote levels were in the last line. if (realStartOfLine > 0) { lastline = markdown.Substring(previousRealtStartOfLine, previousEndOfLine - previousRealtStartOfLine); lastIndentation = lastline.Count(c => c == '>'); } var currentEndOfLine = Common.FindNextSingleNewLine(markdown, nonSpacePos, end, out _); var currentline = markdown.Substring(realStartOfLine, currentEndOfLine - realStartOfLine); var currentIndentation = currentline.Count(c => c == '>'); var firstChar = markdown[realStartOfLine]; // This is a quote that doesn't start with a Quote marker, but carries on from the last line. if (lastIndentation == 1) { if (nonSpaceChar != '\0' && firstChar != '>') { break; } } // Collapse down a level of quotes if the current indentation is greater than the last indentation. // Only if the last indentation is greater than 1, and the current indentation is greater than 0 if (lastIndentation > 1 && currentIndentation > 0 && currentIndentation < lastIndentation) { break; } // This must be the end of the blockquote. End the current paragraph, if any. actualEnd = realStartOfLine; if (paragraphText.Length > 0) { blocks.Add(ParagraphBlock.Parse(paragraphText.ToString())); } return(blocks); } } // Find the end of the current line. int startOfNextLine; int endOfLine = Common.FindNextSingleNewLine(markdown, nonSpacePos, end, out startOfNextLine); if (nonSpaceChar == '\0') { // The line is empty or nothing but whitespace. lineStartsNewParagraph = true; // End the current paragraph. if (paragraphText.Length > 0) { blocks.Add(ParagraphBlock.Parse(paragraphText.ToString())); paragraphText.Clear(); } } else { // This is a header if the line starts with a hash character, // or if the line starts with '-' or a '=' character and has no other characters. // Or a quote if the line starts with a greater than character (optionally preceded by whitespace). // Or a horizontal rule if the line contains nothing but 3 '*', '-' or '_' characters (with optional whitespace). MarkdownBlock newBlockElement = null; if (nonSpaceChar == '#' && nonSpacePos == startOfLine) { // Hash-prefixed header. newBlockElement = HeaderBlock.ParseHashPrefixedHeader(markdown, startOfLine, endOfLine); } else if ((nonSpaceChar == '-' || nonSpaceChar == '=') && nonSpacePos == startOfLine && paragraphText.Length > 0) { // Underline style header. These are weird because you don't know you've // got one until you've gone past it. // Note: we intentionally deviate from reddit here in that we only // recognize this type of header if the previous line is part of a // paragraph. For example if you have this, the header at the bottom is // ignored: // a|b // -|- // 1|2 // === newBlockElement = HeaderBlock.ParseUnderlineStyleHeader(markdown, previousStartOfLine, previousEndOfLine, startOfLine, endOfLine); if (newBlockElement != null) { // We're going to have to remove the header text from the pending // paragraph by prematurely ending the current paragraph. // We already made sure that there is a paragraph in progress. paragraphText.Length = paragraphText.Length - (previousEndOfLine - previousStartOfLine); } } // These characters overlap with the underline-style header - this check should go after that one. if (newBlockElement == null && (nonSpaceChar == '*' || nonSpaceChar == '-' || nonSpaceChar == '_')) { newBlockElement = HorizontalRuleBlock.Parse(markdown, startOfLine, endOfLine); } if (newBlockElement == null && lineStartsNewParagraph) { // Some block elements must start on a new paragraph (tables, lists and code). int endOfBlock = startOfNextLine; if (nonSpaceChar == '*' || nonSpaceChar == '+' || nonSpaceChar == '-' || (nonSpaceChar >= '0' && nonSpaceChar <= '9')) { newBlockElement = ListBlock.Parse(markdown, realStartOfLine, end, quoteDepth, out endOfBlock); } if (newBlockElement == null && (nonSpacePos > startOfLine || nonSpaceChar == '`')) { newBlockElement = CodeBlock.Parse(markdown, realStartOfLine, end, quoteDepth, out endOfBlock); } if (newBlockElement == null) { newBlockElement = TableBlock.Parse(markdown, realStartOfLine, endOfLine, end, quoteDepth, out endOfBlock); } if (newBlockElement != null) { startOfNextLine = endOfBlock; } } // This check needs to go after the code block check. if (newBlockElement == null && nonSpaceChar == '>') { newBlockElement = QuoteBlock.Parse(markdown, realStartOfLine, end, quoteDepth, out startOfNextLine); } // This check needs to go after the code block check. if (newBlockElement == null && nonSpaceChar == '[') { newBlockElement = LinkReferenceBlock.Parse(markdown, startOfLine, endOfLine); } // Block elements start new paragraphs. lineStartsNewParagraph = newBlockElement != null; if (newBlockElement == null) { // The line contains paragraph text. if (paragraphText.Length > 0) { // If the previous two characters were both spaces, then append a line break. if (paragraphText.Length > 2 && paragraphText[paragraphText.Length - 1] == ' ' && paragraphText[paragraphText.Length - 2] == ' ') { // Replace the two spaces with a line break. paragraphText[paragraphText.Length - 2] = '\r'; paragraphText[paragraphText.Length - 1] = '\n'; } else { paragraphText.Append(" "); } } // Add the last paragraph if we are at the end of the input text. if (startOfNextLine >= end) { if (paragraphText.Length == 0) { // Optimize for single line paragraphs. blocks.Add(ParagraphBlock.Parse(markdown.Substring(startOfLine, endOfLine - startOfLine))); } else { // Slow path. paragraphText.Append(markdown.Substring(startOfLine, endOfLine - startOfLine)); blocks.Add(ParagraphBlock.Parse(paragraphText.ToString())); } } else { paragraphText.Append(markdown.Substring(startOfLine, endOfLine - startOfLine)); } } else { // The line contained a block. End the current paragraph, if any. if (paragraphText.Length > 0) { blocks.Add(ParagraphBlock.Parse(paragraphText.ToString())); paragraphText.Clear(); } blocks.Add(newBlockElement); } } // Repeat. previousRealtStartOfLine = realStartOfLine; previousStartOfLine = startOfLine; previousEndOfLine = endOfLine; startOfLine = startOfNextLine; } actualEnd = startOfLine; return(blocks); }
/// <summary> /// Renders a quote element. /// </summary> /// <param name="element"> The parsed block element to render. </param> /// <param name="context"> Persistent state. </param> protected abstract void RenderQuote(QuoteBlock element, IRenderContext context);
public override BlockState TryOpen(BlockProcessor processor) { if (processor.IsCodeIndent) { return(BlockState.None); } var sourcePosition = processor.Start; // 5.1 Block quotes // A block quote marker consists of 0-3 spaces of initial indent, plus (a) the character > together with a following space, or (b) a single character > not followed by a space. var quoteChar = processor.CurrentChar; var column = processor.Column; var c = processor.NextChar(); var quoteBlock = new QuoteBlock(this) { QuoteChar = quoteChar, Column = column, Span = new SourceSpan(sourcePosition, processor.Line.End), LinesBefore = processor.UseLinesBefore() }; bool hasSpaceAfterQuoteChar = false; if (c == ' ') { processor.NextColumn(); hasSpaceAfterQuoteChar = true; processor.SkipFirstUnwindSpace = true; } else if (c == '\t') { processor.NextColumn(); } var triviaBefore = processor.UseTrivia(sourcePosition - 1); StringSlice triviaAfter = StringSlice.Empty; bool wasEmptyLine = false; if (processor.Line.IsEmptyOrWhitespace()) { processor.TriviaStart = processor.Start; triviaAfter = processor.UseTrivia(processor.Line.End); wasEmptyLine = true; } quoteBlock.QuoteLines.Add(new QuoteBlockLine { TriviaBefore = triviaBefore, TriviaAfter = triviaAfter, QuoteChar = true, HasSpaceAfterQuoteChar = hasSpaceAfterQuoteChar, NewLine = processor.Line.NewLine, }); processor.NewBlocks.Push(quoteBlock); if (!wasEmptyLine) { processor.TriviaStart = processor.Start; } return(BlockState.Continue); }
/// <summary> /// Adds DocFX Note Support on top of Quote Formatting. /// </summary> /// <param name="element">QuoteBlock Element</param> /// <param name="context">Render Context</param> protected override void RenderQuote(QuoteBlock element, IRenderContext context) { // Grab the Local context and cast it. var localContext = context as UIElementCollectionRenderContext; var collection = localContext?.BlockUIElementCollection; // Store these, they will be changed temporarily. var originalQuoteForeground = QuoteForeground; var originalLinkForeground = LinkForeground; DocFXNote noteType = null; string header = null; SolidColorBrush localforeground = null; SolidColorBrush localbackground = null; string symbolglyph = string.Empty; // Check the required structure of the Quote is correct. Determine if it is a DocFX Note. if (element.Blocks.First() is ParagraphBlock para) { if (para.Inlines.First() is TextRunInline textinline) { // Find the matching DocFX note style and header. foreach (var style in styles) { // Search between stylisticly matching notes with different headers. foreach (var identifier in style.Identifiers) { // Match the identifier with the start of the Quote to match. if (textinline.Text.StartsWith(identifier.Key)) { noteType = style; header = identifier.Value; symbolglyph = style.Glyph; // Removes the identifier from the text textinline.Text = textinline.Text.Replace(identifier.Key, string.Empty); localforeground = style.LightForeground; localbackground = style.LightBackground; } } } // Apply special formatting context. if (noteType != null) { if (localContext?.Clone() is UIElementCollectionRenderContext newcontext) { localContext = newcontext; localContext.TrimLeadingWhitespace = true; QuoteForeground = Foreground; LinkForeground = localforeground; } } } } // Begins the standard rendering. base.RenderQuote(element, localContext); // Add styling to render if DocFX note. if (noteType != null) { // Restore original formatting properties. QuoteForeground = originalQuoteForeground; LinkForeground = originalLinkForeground; if (localContext == null || collection?.Any() != true) { return; } // Gets the current Quote Block UI from the UI Collection, and then styles it. Adds a header. if (collection.Last() is Border border) { border.CornerRadius = new CornerRadius(6); border.BorderThickness = new Thickness(0); border.Padding = new Thickness(20); border.Margin = new Thickness(0, 5, 0, 5); border.Background = localbackground; var headerPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 0, 0, 10) }; headerPanel.Children.Add(new TextBlock { FontSize = 18, Foreground = localforeground, Text = symbolglyph, FontFamily = new FontFamily("Segoe MDL2 Assets"), }); headerPanel.Children.Add(new TextBlock { FontSize = 16, Foreground = localforeground, Margin = new Thickness(5, 0, 0, 0), Text = header, VerticalAlignment = VerticalAlignment.Center, TextLineBounds = TextLineBounds.Tight, FontWeight = FontWeights.SemiBold }); if (border.Child is StackPanel panel) { panel.Children.Insert(0, headerPanel); } } } }
protected override void RenderQuote(QuoteBlock element, IRenderContext context) { }
protected override MarkdownQuoteBlock CreateQuoteBlock(QuoteBlock quoteBlock) => new OsuMarkdownQuoteBlock(quoteBlock);
public OsuMarkdownQuoteBlock(QuoteBlock quoteBlock) : base(quoteBlock) { }
public MarkdownQuoteBlock(QuoteBlock quoteBlock) : this() { Style = quoteBlock; }
protected override void RenderQuote(QuoteBlock element, IRenderContext context) { throw new NotImplementedException(); }