Esempio n. 1
0
        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);
Esempio n. 2
0
        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)));
        }
Esempio n. 3
0
        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);
        }
Esempio n. 4
0
        /// <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>();
Esempio n. 5
0
 /// <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());
 }
Esempio n. 6
0
        /// <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);
        }
Esempio n. 9
0
        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);
        }
Esempio n. 10
0
        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);
        }
Esempio n. 11
0
        protected virtual OsuMarkdownListItem CreateListItem(ListItemBlock listItemBlock, int level, bool isOrdered)
        {
            if (isOrdered)
            {
                return(new OsuMarkdownOrderedListItem(listItemBlock.Order));
            }

            return(new OsuMarkdownUnorderedListItem(level));
        }
Esempio n. 12
0
        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);
        }
Esempio n. 13
0
        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);
         }
     }
 }
Esempio n. 15
0
        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;
        }
Esempio n. 16
0
        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);
        }
Esempio n. 17
0
        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;
        }
Esempio n. 18
0
        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);
        }
Esempio n. 19
0
        /// <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);
        }
Esempio n. 20
0
        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());
        }
Esempio n. 21
0
        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
            });
        }
Esempio n. 22
0
        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());
        }
Esempio n. 23
0
        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());
        }
Esempio n. 24
0
        /// <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);
        }
Esempio n. 25
0
        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);
        }
Esempio n. 26
0
        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);
        }
Esempio n. 27
0
        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));
 }