private NavigationItem ParseListItemBlock(ListItemBlock listItemBlock) { DisplayLink rootLink = default; List <NavigationItem> children = new List <NavigationItem>(); foreach (var listItemChild in listItemBlock) { switch (listItemChild) { case ParagraphBlock paragraphBlock: rootLink = ParseParagraph(paragraphBlock); break; case ListBlock listBlock: children.AddRange(ParseListBlock(listBlock)); break; default: throw new InvalidOperationException( $"Document contains invalid navigation document syntax at {listItemBlock.ToPositionText()}. Expected paragraph."); } } if (rootLink.Equals(default(DisplayLink))) { return(default);
private RepositoryEntry ImportIndexEntry(DirectoryInfo currentPath, ListItemBlock block) { if (block.LastChild == null) { return(null); } var paragraph = block.LastChild as ParagraphBlock; if (paragraph == null || paragraph.Inline == null) { return(null); } var link = paragraph.Inline as LinkInline; if (link == null) { var container = paragraph.Inline as ContainerInline; if (container == null) { return(null); } link = container.FirstChild as LinkInline; if (link == null) { return(null); } } return(ImportEntry(Path.Combine(currentPath.FullName, link.Url))); }
private void OnHeadingBlockParsed(BlockProcessor processor, Block block) { if (!(block is HeadingBlock headingBlock) || block is BlogMetadataBlock) { return; } if (headingBlock.Level < 2) { return; // Ignore h1 since there's no point including it. } var document = processor.Document; var toc = document.Where(b => b is TableOfContentsBlock).FirstOrDefault() as TableOfContentsBlock; if (toc == null) { return; } ContainerBlock parent = toc; for (int i = 0; i < headingBlock.Level - 2; i++) // 2 is the minimum level we support, hence -2 { if (!(parent.LastChild is ContainerBlock childContainer)) { childContainer = new ListItemBlock(block.Parser); parent.Add(childContainer); } parent = (ContainerBlock)parent.LastChild; } var headingCopy = new HeadingBlock(block.Parser) { Column = headingBlock.Column, HeaderChar = headingBlock.HeaderChar, Inline = headingBlock.Inline, IsBreakable = headingBlock.IsBreakable, IsOpen = headingBlock.IsOpen, Level = headingBlock.Level, Line = headingBlock.Line, ProcessInlines = headingBlock.ProcessInlines, RemoveAfterProcessInlines = headingBlock.RemoveAfterProcessInlines, Span = headingBlock.Span }; headingCopy.Lines = new StringLineGroup(headingBlock.Lines.Lines.Length); headingCopy.SetAttributes(headingBlock.GetAttributes()); foreach (var line in headingBlock.Lines.Lines) { if (line.Slice.Text == null) { continue; } var textCopy = new StringSlice(line.Slice.Text, line.Slice.Start, line.Slice.End); var reffableLine = new StringLine(ref textCopy); headingCopy.Lines.Add(ref reffableLine); } parent.Add(headingCopy); }
/// <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>();
/// <summary> /// Extracts USDM specification from given ListItemBlock. /// </summary> /// <param name="listBlock">Markdown list item block</param> /// <returns>USDM specification information</returns> static private Specification DecomposeListItemBlock(ListItemBlock item) { foreach (var block in item.Blocks) { var paragraph = block as ParagraphBlock; if (paragraph != null) { var link = paragraph.Inlines[0] as MarkdownLinkInline; if (link != null) { var isImplemented = link.Inlines[0].ToString()?.Equals("x"); var id = link.ReferenceId; var description = paragraph.Inlines[1].ToString(); if (isImplemented != null && id != null && description != null) { return(new Specification { IsImplemented = isImplemented ?? false, ID = id, Description = description }); } } } } return(new Specification()); }
/// <summary> /// Parsing helper method. /// </summary> private static void AppendTextToListItem(ListItemBlock listItem, string markdown, int start, int end) { ListItemBuilder listItemBuilder = null; if (listItem.Blocks.Count > 0) { listItemBuilder = listItem.Blocks[listItem.Blocks.Count - 1] as ListItemBuilder; } if (listItemBuilder == null) { // Add a new block. listItemBuilder = new ListItemBuilder(); listItem.Blocks.Add(listItemBuilder); } var builder = listItemBuilder.Builder; if (builder.Length >= 2 && Common.IsWhiteSpace(builder[builder.Length - 2]) && Common.IsWhiteSpace(builder[builder.Length - 1])) { builder.Length -= 2; builder.AppendLine(); } else if (builder.Length > 0) { builder.Append(' '); } builder.Append(markdown.Substring(start, end - start)); }
public ListItemBlockView(DocumentEditorContextView root, ListItemBlock listItemBlock) { _root = root; _listItemBlock = listItemBlock; _childContents = new StackPanel(); SetRow(_childContents, 0); SetColumn(_childContents, 1); _listSymbol = new TextBlock(); _listSymbol.Text = " - "; SetRow(_listSymbol, 0); SetColumn(_listSymbol, 0); Children.Add(_listSymbol); Children.Add(_childContents); ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto }); ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) }); foreach (var block in listItemBlock.Children) { var view = _root.CreateViewFor(block); Children.Add(view); } }
private View Render(ListBlock list, int index, ListItemBlock itemBlock, MarkdownTheme theme) { var stack = new StackLayout() { Spacing = theme.Margin }; var horizontalStack = new StackLayout { Orientation = StackOrientation.Horizontal, Margin = new Thickness(theme.Margin, 0, 0, 0) }; var bullet = new Label { Text = list.IsOrdered ? $"{index}." : "\u2981", FontSize = theme.Paragraph.FontSize, TextColor = theme.Paragraph.ForegroundColor, VerticalOptions = LayoutOptions.Start, HorizontalOptions = LayoutOptions.Center, HorizontalTextAlignment = TextAlignment.Center, VerticalTextAlignment = TextAlignment.Center, LineBreakMode = LineBreakMode.NoWrap, Margin = theme.ListItemThickness }; horizontalStack.Children.Add(bullet); horizontalStack.Children.Add(_render.Render(itemBlock.AsEnumerable(), theme)); stack.Children.Add(horizontalStack); return(stack); }
public List <Paragraph> ParseList(ListBlock block, int indent = 0, int numbering = -1) { List <Paragraph> result = new List <Paragraph>(); if (numbering == -1) { numbering = this.InsertNewNumbering(); } ListBlock b = block as ListBlock; foreach (var item in b) { ListItemBlock li = item as ListItemBlock; if (li.First() is ParagraphBlock) { Paragraph p = this.ParseParagaph(li.First() as ParagraphBlock); this.ChangeToListItem(p, numbering, indent); result.Add(p); if (li.Count > 1 && li[1] is ListBlock) { result.AddRange(this.ParseList(li[1] as ListBlock, indent + 1, numbering)); } } } return(result); }
private BlockState TryContinueListItem(BlockProcessor state, ListItemBlock listItem) { var list = (ListBlock)listItem.Parent; // Allow all blanks lines if the last block is a fenced code block // Allow 1 blank line inside a list // If > 1 blank line, terminate this list if (state.IsBlankLine) { if (state.CurrentBlock != null && state.CurrentBlock.IsBreakable) { if (!(state.NextContinue is ListBlock)) { list.CountAllBlankLines++; listItem.Add(new BlankLineBlock()); } list.CountBlankLinesReset++; } if (list.CountBlankLinesReset == 1 && listItem.ColumnWidth < 0) { state.Close(listItem); // Leave the list open list.IsOpen = true; return(BlockState.Continue); } // Update list-item source end position listItem.UpdateSpanEnd(state.Line.End); return(BlockState.Continue); } list.CountBlankLinesReset = 0; int columnWidth = listItem.ColumnWidth; if (columnWidth < 0) { columnWidth = -columnWidth; } if (state.Indent >= columnWidth) { if (state.Indent > columnWidth && state.IsCodeIndent) { state.GoToColumn(state.ColumnBeforeIndent + columnWidth); } // Update list-item source end position listItem.UpdateSpanEnd(state.Line.End); return(BlockState.Continue); } return(BlockState.None); }
protected virtual OsuMarkdownListItem CreateListItem(ListItemBlock listItemBlock, int level, bool isOrdered) { if (isOrdered) { return(new OsuMarkdownOrderedListItem(listItemBlock.Order)); } return(new OsuMarkdownUnorderedListItem(level)); }
private Phrase Render(ListBlock parent, int index, ListItemBlock block) { var stack = new Phrase(); var subv = this.Render(block.AsEnumerable()); subv.ToList().ForEach(v => stack.Add(v)); return(stack); }
private string Render(ListItemBlock block) { var stack = ""; var subv = this.Render(block.AsEnumerable()); subv.ToList().ForEach(v => stack += v); return(stack); }
private static void RenderNumberedList(VT100Renderer renderer, ListItemBlock block, int index) { // For a numbered list, we need to make sure the index is incremented. foreach (var line in block) { if (line is ParagraphBlock paragraphBlock) { renderer.Write(index.ToString()).Write(". ").Write(paragraphBlock.Inline); } } }
void Render(ListBlock parent, int index, ListItemBlock block) { var initialStack = stack; stack = new StackLayout() { Spacing = Theme.Margin, }; Render(block.AsEnumerable()); var horizontalStack = new StackLayout { Orientation = StackOrientation.Horizontal, Margin = new Thickness(listScope * Theme.Margin, 0, 0, 0), }; View bullet; if (parent.IsOrdered) { bullet = new Label { Text = $"{index}.", FontSize = Theme.Paragraph.FontSize, TextColor = Theme.Paragraph.ForegroundColor, VerticalOptions = LayoutOptions.Start, HorizontalOptions = LayoutOptions.End, LineHeight = Theme.Paragraph.LineHeight, }; } else { bullet = new BoxView { WidthRequest = 4, HeightRequest = 4, Margin = new Thickness(0, 6, 0, 0), BackgroundColor = Theme.Paragraph.ForegroundColor, VerticalOptions = LayoutOptions.Start, HorizontalOptions = LayoutOptions.Center, }; } horizontalStack.Children.Add(bullet); horizontalStack.Children.Add(stack); initialStack.Children.Add(horizontalStack); stack = initialStack; }
private View Render(ListBlock list, int index, ListItemBlock itemBlock, MarkdownTheme theme) { var stack = new StackLayout() { Spacing = theme.Margin }; var horizontalStack = new StackLayout { Orientation = StackOrientation.Horizontal, Margin = new Thickness(theme.Margin, 0, 0, 0), }; View bullet; if (list.IsOrdered) { bullet = new Label { Text = $"{index}.", FontSize = theme.Paragraph.FontSize, TextColor = theme.Paragraph.ForegroundColor, VerticalOptions = LayoutOptions.Start, HorizontalOptions = LayoutOptions.End, HorizontalTextAlignment = TextAlignment.Center, VerticalTextAlignment = TextAlignment.Center, LineBreakMode = LineBreakMode.NoWrap, Margin = new Thickness(0, 3, 0, 0) }; } else { bullet = new BoxView { WidthRequest = 4, HeightRequest = 4, Margin = new Thickness(0, 10, 0, 0), BackgroundColor = theme.Paragraph.ForegroundColor, VerticalOptions = LayoutOptions.Start, HorizontalOptions = LayoutOptions.Center }; } horizontalStack.Children.Add(bullet); horizontalStack.Children.Add(_render.Render(itemBlock.AsEnumerable(), theme)); stack.Children.Add(horizontalStack); return(stack); }
void Render(ListBlock parent, ListStyle listTheme, int index, ListItemBlock block) { var initialStack = stack; stack = new StackLayout() { Spacing = 0, VerticalOptions = listTheme.ItemVerticalOptions, }; Render(block.AsEnumerable()); Grid.SetColumn(stack, 1); var horizontalStack = new Grid { ColumnDefinitions = new ColumnDefinitionCollection { new ColumnDefinition() { Width = GridLength.Auto }, new ColumnDefinition() { Width = GridLength.Star }, }, ColumnSpacing = listTheme.Spacing ?? Theme.Margin, RowSpacing = 0, Margin = new Thickness(block.Column * listTheme.Indentation, 0, 0, 0), }; if (listTheme.BulletStyleType == ListStyleType.None) { horizontalStack.ColumnSpacing = 0; } var bullet = GetListBullet(listTheme, index, parent, block); if (bullet != null) { Grid.SetColumn(bullet, 0); horizontalStack.Children.Add(bullet); } horizontalStack.Children.Add(stack); initialStack.Children.Add(horizontalStack); stack = initialStack; }
View GetListBullet(ListStyle listTheme, int index, ListBlock parent, ListItemBlock block) { if (listTheme.BulletStyleType == ListStyleType.None) { return(null); } if (listTheme.BulletStyleType == ListStyleType.Custom) { return(listTheme.CustomCallback?.Invoke(index, parent, block)); } if (listTheme.BulletStyleType == ListStyleType.Decimal || listTheme.BulletStyleType == ListStyleType.Symbol) { return(new Label { Text = listTheme.BulletStyleType == ListStyleType.Symbol ? listTheme.Symbol : $"{index}.", FontSize = listTheme.BulletFontSize ?? Theme.Paragraph.FontSize, TextColor = listTheme.BulletColor ?? Theme.Paragraph.ForegroundColor, LineHeight = listTheme.BulletLineHeight ?? Theme.Paragraph.LineHeight, FontAttributes = listTheme.BulletFontAttributes, VerticalOptions = listTheme.BulletVerticalOptions, }); } else if (listTheme.BulletStyleType == ListStyleType.Square || listTheme.BulletStyleType == ListStyleType.Circle) { var bullet = new Frame { WidthRequest = listTheme.BulletSize, HeightRequest = listTheme.BulletSize, BackgroundColor = listTheme.BulletColor ?? Theme.Paragraph.ForegroundColor, Padding = 0, HasShadow = false, VerticalOptions = listTheme.BulletVerticalOptions, CornerRadius = 0, }; if (listTheme.BulletStyleType == ListStyleType.Circle) { bullet.CornerRadius = listTheme.BulletSize / 2; } return(bullet); } return(null); }
/// <summary> /// Create an ordered list block with the given elements. /// </summary> /// <param name="contents">Contents - each element will be a list item.</param> private ListBlock CreateOrderedListBlock(params string[] contents) { BlockParser parser = new ListBlockParser(); ListBlock block = new ListBlock(parser); block.IsOrdered = true; foreach (string item in contents) { ListItemBlock listItem = new ListItemBlock(parser); ParagraphBlock paragraph = new ParagraphBlock(new ParagraphBlockParser()); paragraph.Inline = new ContainerInline().AppendChild(new LiteralInline(item)); listItem.Add(paragraph); block.Add(listItem); } return(block); }
public void UpdateOpenFlexiSectionBlocks_IgnoresAncestorContainerBlocksThatAreClosed() { // Arrange BlockProcessor dummyBlockProcessor = MarkdigTypesFactory.CreateBlockProcessor(); // Create abitrary closed blocks (ListItemBlock and ListBlock) and set BlockProcessor.CurrentContainer to dummyListItemBlock. // Closed blocks should be ignored, so UpdateOpenFlexiSectionBlocks should not create a new stack. Mock <BlockParser> mockBlockParser = _mockRepository.Create <BlockParser>(); mockBlockParser.Setup(b => b.TryContinue(dummyBlockProcessor, It.IsAny <Block>())).Returns(BlockState.Continue); var dummyListItemBlock = new ListItemBlock(mockBlockParser.Object); var dummyListBlock = new ListBlock(null) { dummyListItemBlock }; FlexiSectionBlock dummyFlexiSectionBlock = CreateFlexiSectionBlock(); dummyFlexiSectionBlock.Add(dummyListBlock); dummyBlockProcessor.Open(dummyListItemBlock); dummyBlockProcessor.ProcessLine(new StringSlice("")); // This line sets BlockProcessor.CurrentContainer to dummyListItemBlock dummyListItemBlock.IsOpen = false; dummyListBlock.IsOpen = false; FlexiSectionBlock dummyNewFlexiSectionBlock = CreateFlexiSectionBlock(); var dummyCurrentBranch = new Stack <FlexiSectionBlock>(); dummyCurrentBranch.Push(dummyFlexiSectionBlock); var dummyOpenSectionBlocks = new Stack <Stack <FlexiSectionBlock> >(); dummyOpenSectionBlocks.Push(dummyCurrentBranch); Mock <FlexiSectionBlockFactory> mockTestSubject = CreateMockFlexiSectionBlockFactory(); mockTestSubject.CallBase = true; mockTestSubject.Setup(t => t.GetOrCreateOpenFlexiSectionBlocks(dummyBlockProcessor.Document)).Returns(dummyOpenSectionBlocks); // Act mockTestSubject.Object.UpdateOpenFlexiSectionBlocks(dummyBlockProcessor, dummyNewFlexiSectionBlock); // Assert _mockRepository.VerifyAll(); Assert.Single(dummyCurrentBranch); Assert.Equal(dummyNewFlexiSectionBlock, dummyCurrentBranch.Peek()); }
private Choice ParseChoice(ListItemBlock listItemBlock) { if (!(listItemBlock[0] is ParagraphBlock paragraphBlock)) { throw new Exception("Unable to parse the Choice. No text found."); } string text = CleanContent(GetBlockContent(paragraphBlock)); string sectionKey = paragraphBlock.Inline.Descendants <LinkInline>().FirstOrDefault().Url; var(conditions, effects) = ParseStatEffects(listItemBlock); return(new Choice { Conditions = conditions, Effects = effects, Text = text, SectionKey = sectionKey }); }
public void EnsureChildrenAreWritten() { // Create a list block with a single list item containing // an emphasis (italic) inline. BlockParser parser = new ListBlockParser(); ListBlock block = new ListBlock(parser); block.IsOrdered = true; string item = "item"; ListItemBlock listItem = new ListItemBlock(parser); ParagraphBlock paragraphBlock = new ParagraphBlock(new ParagraphBlockParser()); EmphasisInline emphasis = new EmphasisInline(); emphasis.DelimiterChar = '*'; emphasis.DelimiterCount = 1; emphasis.AppendChild(new LiteralInline(item)); paragraphBlock.Inline = new ContainerInline().AppendChild(emphasis); listItem.Add(paragraphBlock); block.Add(listItem); renderer.Write(pdfBuilder, block); Assert.AreEqual(1, document.LastSection.Elements.Count); Paragraph paragraph = (Paragraph)document.LastSection.Elements[0]; // Paragraph should have 3 elements: bullet, content, and linefeed. Assert.AreEqual(3, paragraph.Elements.Count); FormattedText text = paragraph.Elements[1] as FormattedText; Assert.NotNull(text); var textStyle = document.Styles[text.Style]; Assert.True(textStyle.Font.Italic); Assert.AreEqual($" 1. {item}\n", paragraph.GetRawText()); }
private static string GetText(ListItemBlock listItemBlock) { var text = new StringBuilder(); foreach (var block in listItemBlock.Cast <ParagraphBlock>()) { foreach (var part in block.Inline) { if (part is CodeInline codeInline) { text.Append(codeInline.Content); } else if (part is LiteralInline literalInline) { text.Append(literalInline.Content); } else if (part is LinkInline linkInline) { if (linkInline.Count() < 1 || linkInline.ElementAt(0) is not LiteralInline linkText) { throw new Exception("GitHub wiki has unexpected link format."); } text.Append(linkText.Content); } else { throw new Exception($"GitHub wiki argument description contains an inline type we haven't implemented support for: \"{part.GetType()}\""); } } text.Append(" "); } return(text.ToString().Trim()); }
/// <summary> /// Parses a list block. /// </summary> /// <param name="markdown"> The markdown text. </param> /// <param name="start"> The location of the first character in the block. </param> /// <param name="maxEnd"> The location to stop parsing. </param> /// <param name="quoteDepth"> The current nesting level for block quoting. </param> /// <param name="actualEnd"> Set to the end of the block when the return value is non-null. </param> /// <returns> A parsed list block, or <c>null</c> if this is not a list block. </returns> internal static ListBlock Parse(string markdown, int start, int maxEnd, int quoteDepth, out int actualEnd) { var russianDolls = new List <NestedListInfo>(); int russianDollIndex = -1; bool previousLineWasBlank = false; ListItemBlock currentListItem = null; actualEnd = start; foreach (var lineInfo in Common.ParseLines(markdown, start, maxEnd, quoteDepth)) { // Is this line blank? if (lineInfo.IsLineBlank) { // The line is blank, which means the next line which contains text may end the list (or it may not...). previousLineWasBlank = true; } else { // Does the line contain a list item? ListItemPreamble listItemPreamble = null; if (lineInfo.FirstNonWhitespaceChar - lineInfo.StartOfLine < (russianDollIndex + 2) * 4) { listItemPreamble = ParseItemPreamble(markdown, lineInfo.FirstNonWhitespaceChar, lineInfo.EndOfLine); } if (listItemPreamble != null) { // Yes, this line contains a list item. // Determining the nesting level is done as follows: // 1. If this is the first line, then the list is not nested. // 2. If the number of spaces at the start of the line is equal to that of // an existing list, then the nesting level is the same as that list. // 3. Otherwise, if the number of spaces is 0-4, then the nesting level // is one level deep. // 4. Otherwise, if the number of spaces is 5-8, then the nesting level // is two levels deep (but no deeper than one level more than the // previous list item). // 5. Etcetera. ListBlock listToAddTo = null; int spaceCount = lineInfo.FirstNonWhitespaceChar - lineInfo.StartOfLine; russianDollIndex = russianDolls.FindIndex(rd => rd.SpaceCount == spaceCount); if (russianDollIndex >= 0) { // Add the new list item to an existing list. listToAddTo = russianDolls[russianDollIndex].List; // Don't add new list items to items higher up in the list. russianDolls.RemoveRange(russianDollIndex + 1, russianDolls.Count - (russianDollIndex + 1)); } else { russianDollIndex = Math.Max(1, 1 + ((spaceCount - 1) / 4)); if (russianDollIndex < russianDolls.Count) { // Add the new list item to an existing list. listToAddTo = russianDolls[russianDollIndex].List; // Don't add new list items to items higher up in the list. russianDolls.RemoveRange(russianDollIndex + 1, russianDolls.Count - (russianDollIndex + 1)); } else { // Create a new list. listToAddTo = new ListBlock { Style = listItemPreamble.Style, Items = new List <ListItemBlock>() }; if (russianDolls.Count > 0) { currentListItem.Blocks.Add(listToAddTo); } russianDollIndex = russianDolls.Count; russianDolls.Add(new NestedListInfo { List = listToAddTo, SpaceCount = spaceCount }); } } // Add a new list item. currentListItem = new ListItemBlock() { Blocks = new List <MarkdownBlock>() }; listToAddTo.Items.Add(currentListItem); // Add the rest of the line to the builder. AppendTextToListItem(currentListItem, markdown, listItemPreamble.ContentStartPos, lineInfo.EndOfLine); } else { // No, this line contains text. // Is there even a list in progress? if (currentListItem == null) { actualEnd = start; return(null); } // 0 spaces = end of the list. // 1-4 spaces = first level. // 5-8 spaces = second level, etc. if (previousLineWasBlank) { // This is the start of a new paragraph. int spaceCount = lineInfo.FirstNonWhitespaceChar - lineInfo.StartOfLine; if (spaceCount == 0) { break; } russianDollIndex = Math.Min(russianDollIndex, (spaceCount - 1) / 4); ListBlock listToAddTo = russianDolls[russianDollIndex].List; currentListItem = listToAddTo.Items[listToAddTo.Items.Count - 1]; currentListItem.Blocks.Add(new ListItemBuilder()); AppendTextToListItem(currentListItem, markdown, Math.Min(lineInfo.FirstNonWhitespaceChar, lineInfo.StartOfLine + ((russianDollIndex + 1) * 4)), lineInfo.EndOfLine); } else { // Inline text. AppendTextToListItem(currentListItem, markdown, lineInfo.FirstNonWhitespaceChar, lineInfo.EndOfLine); } } // The line was not blank. previousLineWasBlank = false; } // Go to the next line. actualEnd = lineInfo.EndOfLine; } var result = russianDolls[0].List; ReplaceStringBuilders(result); return(result); }
private (List <StatEffect> conditions, List <StatEffect> effects) ParseStatEffects(ListItemBlock listItemBlock) { var conditions = new List <StatEffect>(); var effects = new List <StatEffect>(); // No effects or conditions. if (listItemBlock.Count < 2 || !(listItemBlock[1] is ListBlock listBlock)) { return(conditions, effects); } foreach (ParagraphBlock paragraphBlock in listBlock.Descendants <ParagraphBlock>()) { string content = GetBlockContent(paragraphBlock); (string key, dynamic value, EffectType effectType) = GetStat(content); if (paragraphBlock.Inline.FirstChild is EmphasisInline) // Is a Condition { conditions.Add(new StatEffect { EffectType = effectType, Key = key, Value = value }); } else { effects.Add(new StatEffect { EffectType = effectType, Key = key, Value = value }); } } return(conditions, effects); }
private BlockState TryParseListItem(BlockProcessor state, Block block) { // If we have a code indent and we are not in a ListItem, early exit if (!(block is ListItemBlock) && state.IsCodeIndent) { return(BlockState.None); } var currentListItem = block as ListItemBlock; var currentParent = block as ListBlock ?? (ListBlock)currentListItem?.Parent; var initColumnBeforeIndent = state.ColumnBeforeIndent; var initColumn = state.Column; var sourcePosition = state.Start; var sourceEndPosition = state.Line.End; var c = state.CurrentChar; var itemParser = mapItemParsers[c]; bool isOrdered = itemParser is OrderedListItemParser; if (itemParser == null) { return(BlockState.None); } // Try to parse the list item ListInfo listInfo; if (!itemParser.TryParse(state, currentParent?.BulletType ?? '\0', out listInfo)) { // Reset to an a start position state.GoToColumn(initColumn); return(BlockState.None); } // Gets the current character after a succesfull parsing of the list information c = state.CurrentChar; // Item starting with a blank line int columnWidth; // Do we have a blank line right after the bullet? if (c == '\0') { // Use a negative number to store the number of expected chars columnWidth = -(state.Column - initColumnBeforeIndent + 1); } else { if (!c.IsSpaceOrTab()) { state.GoToColumn(initColumn); return(BlockState.None); } // Parse the following indent state.RestartIndent(); var columnBeforeIndent = state.Column; state.ParseIndent(); // We expect at most 4 columns after // If we have more, we reset the position if (state.Indent > 4) { state.GoToColumn(columnBeforeIndent + 1); } // Number of spaces required for the following content to be part of this list item // If the list item starts with a blank line, the number of spaces // following the list marker doesn't change the required indentation columnWidth = (state.IsBlankLine ? columnBeforeIndent : state.Column) - initColumnBeforeIndent; } // Starts/continue the list unless: // - an empty list item follows a paragraph // - an ordered list is not starting by '1' var isPreviousParagraph = (block ?? state.LastBlock) is ParagraphBlock; if (isPreviousParagraph) { var isOpen = state.IsOpen(block ?? state.LastBlock); if (state.IsBlankLine || (isOpen && listInfo.BulletType == '1' && listInfo.OrderedStart != "1")) { state.GoToColumn(initColumn); return(BlockState.None); } } var newListItem = new ListItemBlock(this) { Column = initColumn, ColumnWidth = columnWidth, Span = new SourceSpan(sourcePosition, sourceEndPosition) }; state.NewBlocks.Push(newListItem); if (currentParent != null) { // If we have a new list item, close the previous one if (currentListItem != null) { state.Close(currentListItem); } // Reset the list if it is a new list or a new type of bullet if (currentParent.IsOrdered != isOrdered || currentParent.OrderedDelimiter != listInfo.OrderedDelimiter || currentParent.BulletType != listInfo.BulletType) { state.Close(currentParent); currentParent = null; } } if (currentParent == null) { var newList = new ListBlock(this) { Column = initColumn, Span = new SourceSpan(sourcePosition, sourceEndPosition), IsOrdered = isOrdered, BulletType = listInfo.BulletType, OrderedDelimiter = listInfo.OrderedDelimiter, DefaultOrderedStart = listInfo.DefaultOrderedStart, OrderedStart = listInfo.OrderedStart, }; state.NewBlocks.Push(newList); } return(BlockState.Continue); }
private void Render(ListBlock parent, int index, ListItemBlock block) { var initialStack = this.stack; this.stack = new StackLayout() { Spacing = this.Theme.Margin, }; this.Render(block.AsEnumerable()); var horizontalStack = new StackLayout { Orientation = StackOrientation.Horizontal, Margin = new Thickness(listScope * this.Theme.Margin, 0, 0, 0), }; if (parent.BulletType != '-') { View bullet; if (parent.IsOrdered) { bullet = new Label { Text = $"{index}.", FontSize = this.Theme.Paragraph.FontSize, TextColor = this.Theme.Paragraph.ForegroundColor, VerticalOptions = LayoutOptions.Start, HorizontalOptions = LayoutOptions.End, }; } else { bullet = new Label { Text = "●", FontSize = this.Theme.Paragraph.FontSize, TextColor = this.Theme.Paragraph.ForegroundColor, VerticalOptions = LayoutOptions.Start, HorizontalOptions = LayoutOptions.End, }; //bullet = new BoxView //{ // WidthRequest = 4, // HeightRequest = 4, // Margin = new Thickness(0, 6, 0, 0), // BackgroundColor = this.Theme.Paragraph.ForegroundColor, // VerticalOptions = LayoutOptions.Start, // HorizontalOptions = LayoutOptions.Center, //}; } horizontalStack.Children.Add(bullet); } horizontalStack.Children.Add(this.stack); initialStack.Children.Add(horizontalStack); this.stack = initialStack; }
private static string TranslateListItemBlock(ListItemBlock listItemBlock, int nestLevel) { return(TranslateParagraphBlock((ParagraphBlock)listItemBlock[0], nestLevel)); }