示例#1
0
        public void Undo_ReplacesProxyTableBlockWithAParagraphBlock()
        {
            // Arrange
            const string dummyText            = "dummyText";
            var          dummyProxyTableBlock = new ProxyTableBlock(null);

            dummyProxyTableBlock.Lines = new StringLineGroup(dummyText);
            BlockParser    dummyBlockParser = _mockRepository.Create <BlockParser>().Object;
            ContainerBlock dummyParent      = _mockRepository.Create <ContainerBlock>(dummyBlockParser).Object; // Must specify block parser since we're calling ProcessLine later

            dummyParent.Add(dummyProxyTableBlock);                                                              // Assigns dummyParent to dummyProxyTableBlock.Parent
            BlockProcessor dummyBlockProcessor = MarkdigTypesFactory.CreateBlockProcessor();

            dummyBlockProcessor.Open(dummyParent);
            dummyBlockProcessor.Open(dummyProxyTableBlock);
            ExposedFlexiTableBlockParser testSubject = CreateExposedFlexiTableBlockParser();

            // Act
            testSubject.ExposedUndo(dummyBlockProcessor, dummyProxyTableBlock);

            // Assert
            Assert.Single(dummyParent);
            var resultParagraphBlock = dummyParent[0] as ParagraphBlock;

            Assert.NotNull(resultParagraphBlock);
            Assert.Equal(dummyText, resultParagraphBlock.Lines.ToString());
            // Verify that ParagraphBlock remains open
            dummyBlockProcessor.ProcessLine(new StringSlice(dummyText));
            Assert.Equal($"{dummyText}\n{dummyText}", resultParagraphBlock.Lines.ToString());
        }
示例#2
0
        public void UpdateOpenFlexiSectionBlocks_DiscardsStacksForClosedTreesAndCreatesANewStackForANewTree()
        {
            // Arrange
            var dummyCurrentBranch = new Stack <FlexiSectionBlock>();

            dummyCurrentBranch.Push(CreateFlexiSectionBlock());
            FlexiSectionBlock dummyClosedFlexiSectionBlock = CreateFlexiSectionBlock();

            dummyClosedFlexiSectionBlock.IsOpen = false;
            var dummyClosedBranchStack = new Stack <FlexiSectionBlock>();

            dummyClosedBranchStack.Push(dummyClosedFlexiSectionBlock);
            var dummyOpenSectionBlocks = new Stack <Stack <FlexiSectionBlock> >();

            dummyOpenSectionBlocks.Push(dummyCurrentBranch);
            dummyOpenSectionBlocks.Push(dummyClosedBranchStack);
            BlockProcessor dummyBlockProcessor = MarkdigTypesFactory.CreateBlockProcessor();

            dummyBlockProcessor.ProcessLine(new StringSlice("")); // This line sets BlockProcessor.CurrentContainer to a MarkdownDocument
            FlexiSectionBlock dummyNewFlexiSectionBlock     = CreateFlexiSectionBlock();
            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.Equal(2, dummyOpenSectionBlocks.Count); // Closed tree removed, open tree remains, new tree added
            Assert.Same(dummyNewFlexiSectionBlock, dummyOpenSectionBlocks.Pop().Peek());
            Assert.Same(dummyCurrentBranch, dummyOpenSectionBlocks.Peek());
        }
示例#3
0
//Process block elements in lines
    private void ProcessLines()
    {
        while (true)
        {
            var textLine = lineReader.ReadLine();

            if (textLine == null)
            {
                break;
            }

            blockProcessor.ProcessLine(textLine);
        }
    }
示例#4
0
        public void Create_CreatesFlexiCardBlock()
        {
            // Arrange
            const int                      dummyColumn                = 2;
            var                            dummyLine                  = new StringSlice("dummyText", 3, 8);
            const string                   dummyUrl                   = "dummyUrl";
            const string                   dummyBackgroundIcon        = "dummyBackgroundIcon";
            IFlexiCardBlockOptions         dummyDefaultCardOptions    = _mockRepository.Create <IFlexiCardBlockOptions>().Object;
            Mock <IFlexiCardsBlockOptions> mockFlexiCardsBlockOptions = _mockRepository.Create <IFlexiCardsBlockOptions>();

            mockFlexiCardsBlockOptions.Setup(f => f.DefaultCardOptions).Returns(dummyDefaultCardOptions);
            Mock <BlockParser>   dummyBlockParser          = _mockRepository.Create <BlockParser>();
            ProxyFlexiCardsBlock dummyProxyFlexiCardsBlock = CreateProxyFlexiCardsBlock(mockFlexiCardsBlockOptions.Object, blockParser: dummyBlockParser.Object);
            BlockProcessor       dummyBlockProcessor       = MarkdigTypesFactory.CreateBlockProcessor();

            // Following 3 lines set dummyBlockProcessor.CurrentContainer
            dummyBlockParser.Setup(b => b.TryContinue(dummyBlockProcessor, dummyProxyFlexiCardsBlock)).Returns(BlockState.ContinueDiscard);
            dummyBlockProcessor.Open(dummyProxyFlexiCardsBlock);
            dummyBlockProcessor.ProcessLine(new StringSlice(string.Empty));
            dummyBlockProcessor.Column = dummyColumn;
            dummyBlockProcessor.Line   = dummyLine;
            var dummyAttributes = new ReadOnlyDictionary <string, string>(new Dictionary <string, string>());
            Mock <IFlexiCardBlockOptions> mockFlexiCardBlockOptions = _mockRepository.Create <IFlexiCardBlockOptions>();

            mockFlexiCardBlockOptions.Setup(f => f.Url).Returns(dummyUrl);
            mockFlexiCardBlockOptions.Setup(f => f.BackgroundIcon).Returns(dummyBackgroundIcon);
            mockFlexiCardBlockOptions.Setup(f => f.Attributes).Returns(dummyAttributes);
            Mock <IBlockOptionsFactory <IFlexiCardBlockOptions> > mockFlexiCardBlockOptionsFactory = _mockRepository.
                                                                                                     Create <IBlockOptionsFactory <IFlexiCardBlockOptions> >();

            mockFlexiCardBlockOptionsFactory.Setup(f => f.Create(dummyDefaultCardOptions, dummyBlockProcessor)).Returns(mockFlexiCardBlockOptions.Object);
            FlexiCardBlockFactory testSubject = CreateFlexiCardBlockFactory(mockFlexiCardBlockOptionsFactory.Object);

            // Act
            FlexiCardBlock result = testSubject.Create(dummyBlockProcessor, dummyBlockParser.Object);

            // Assert
            _mockRepository.VerifyAll();
            Assert.Equal(dummyUrl, result.Url);
            Assert.Equal(dummyBackgroundIcon, result.BackgroundIcon);
            Assert.Same(dummyAttributes, result.Attributes);
            Assert.Same(dummyBlockParser.Object, result.Parser);
            Assert.Equal(dummyColumn, result.Column);
            Assert.Equal(dummyLine.Start, result.Span.Start);
            Assert.Equal(0, result.Span.End);
        }
示例#5
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());
        }
示例#6
0
        public void UpdateOpenFlexiSectionBlocks_ClosesFlexiSectionBlocksInCurrentBranchWithTheSameOrHigherLevels()
        {
            // Arrange
            BlockProcessor     dummyBlockProcessor = MarkdigTypesFactory.CreateBlockProcessor();
            Mock <BlockParser> mockBlockParser     = _mockRepository.Create <BlockParser>();

            mockBlockParser.Setup(b => b.TryContinue(dummyBlockProcessor, It.IsAny <Block>())).Returns(BlockState.Continue);
            FlexiSectionBlock dummyLevel1FlexiSectionBlock = CreateFlexiSectionBlock(level: 1, blockParser: mockBlockParser.Object);
            FlexiSectionBlock dummyLevel2FlexiSectionBlock = CreateFlexiSectionBlock(level: 2, blockParser: mockBlockParser.Object);
            FlexiSectionBlock dummyLevel3FlexiSectionBlock = CreateFlexiSectionBlock(level: 3, blockParser: mockBlockParser.Object);

            dummyBlockProcessor.Open(dummyLevel1FlexiSectionBlock);
            dummyBlockProcessor.Open(dummyLevel2FlexiSectionBlock);
            dummyBlockProcessor.Open(dummyLevel3FlexiSectionBlock);
            dummyBlockProcessor.ProcessLine(new StringSlice("")); // Sets BlockProcessor.CurrentContainer to a FlexiSectionBlock
            var dummyCurrentBranch = new Stack <FlexiSectionBlock>();

            dummyCurrentBranch.Push(dummyLevel1FlexiSectionBlock);
            dummyCurrentBranch.Push(dummyLevel2FlexiSectionBlock);
            dummyCurrentBranch.Push(dummyLevel3FlexiSectionBlock);
            var dummyOpenSectionBlocks = new Stack <Stack <FlexiSectionBlock> >();

            dummyOpenSectionBlocks.Push(dummyCurrentBranch);
            FlexiSectionBlock dummyNewFlexiSectionBlock     = CreateFlexiSectionBlock(level: 2);
            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.Equal(2, dummyCurrentBranch.Count); // Level 2 and level 3 blocks get removed
            Assert.Same(dummyNewFlexiSectionBlock, dummyCurrentBranch.Pop());
            Assert.Same(dummyLevel1FlexiSectionBlock, dummyCurrentBranch.Peek());
            mockBlockParser.Verify(b => b.Close(dummyBlockProcessor, dummyLevel2FlexiSectionBlock)); // Gets closed
            mockBlockParser.Verify(b => b.Close(dummyBlockProcessor, dummyLevel3FlexiSectionBlock)); // Gets closed
        }
示例#7
0
        internal virtual void ProcessContent(BlockProcessor blockProcessor,
                                             FlexiIncludeBlock flexiIncludeBlock,
                                             ContainerBlock parentOfNewBlocks,
                                             ReadOnlyCollection <string> content)
        {
            // The method used here is also used for GridTables. Using a child processor avoids conflicts with existing
            // open blocks in the root processor.
            BlockProcessor childProcessor = blockProcessor.CreateChild();

            childProcessor.Open(parentOfNewBlocks);

            // If content is code, start with ```
            bool isCode = flexiIncludeBlock.Type == FlexiIncludeType.Code;

            if (isCode)
            {
                childProcessor.ProcessLine(_codeBlockFence);
            }

            // TODO this has the potential to be really slow if content has lots of lines and we've got lots of clippings using start/end strings
            // Clippings - need not be sequential, they can also overlap
            int contentNumLines = content.Count;

            foreach (Clipping clipping in flexiIncludeBlock.Clippings ?? _defaultClippings)
            {
                string before = clipping.Before;
                if (isCode && before != null)
                {
                    childProcessor.ProcessLine(new StringSlice(before)); // No issue even if Before is multiline since we're in a code block
                }

                int startLineNumber = -1;
                (string startString, string endString) = clipping.GetNormalizedStartAndEndStrings();
                bool startStringSpecified = startString != null;
                bool endStringSpecified   = endString != null;
                (int normalizedStartLine, int normalizedEndLine)normalizedStartAndEndLines = default;
                if (!startStringSpecified || !endStringSpecified)
                {
                    normalizedStartAndEndLines = clipping.GetNormalizedStartAndEndLines(contentNumLines);
                }
                if (startStringSpecified)
                {
                    int lastIndex = contentNumLines - 2; // Since demarcation lines are not included in the clipping, the last line cannot be a start demarcation line.
                    for (int i = 0; i <= lastIndex; i++)
                    {
                        if (content[i].Contains(startString))
                        {
                            startLineNumber = i + 2;
                            break;
                        }
                    }

                    if (startLineNumber == -1)
                    {
                        throw new OptionsException(nameof(Clipping.StartString),
                                                   string.Format(Strings.OptionsException_FlexiIncludeBlockFactory_NoLineContainsStartString, startString));
                    }
                }
                else
                {
                    startLineNumber = normalizedStartAndEndLines.normalizedStartLine;
                }

                // If we encounter an invalid block in the included content, this ensures the BlockException thrown has the right line number in the included content's source.
                childProcessor.LineIndex = startLineNumber - 1;

                for (int lineNumber = startLineNumber; lineNumber <= contentNumLines; lineNumber++)
                {
                    var stringSlice = new StringSlice(content[lineNumber - 1]);

                    if (!stringSlice.IsEmpty)
                    {
                        if (clipping.Indent > 0)
                        {
                            stringSlice = _leadingWhitespaceEditorService.Indent(stringSlice, clipping.Indent);
                        }

                        _leadingWhitespaceEditorService.Dedent(ref stringSlice, clipping.Dedent);
                        _leadingWhitespaceEditorService.Collapse(ref stringSlice, clipping.Collapse);
                    }

                    childProcessor.ProcessLine(stringSlice);

                    // Check whether we've reached the end of the clipping
                    if (endStringSpecified)
                    {
                        if (lineNumber == contentNumLines)
                        {
                            throw new OptionsException(nameof(Clipping.EndString),
                                                       string.Format(Strings.OptionsException_FlexiIncludeBlockFactory_NoLineContainsEndString, endString));
                        }

                        // Check if next line contains the end line substring
                        if (content[lineNumber].Contains(endString))
                        {
                            break;
                        }
                    }
                    else if (lineNumber == normalizedStartAndEndLines.normalizedEndLine)
                    {
                        break;
                    }
                }

                string after = clipping.After;
                if (isCode && after != null)
                {
                    childProcessor.ProcessLine(new StringSlice(after)); // No issue even if Before is multiline since we're in a code block
                }
            }

            if (isCode) // If content is code, end with ```
            {
                childProcessor.ProcessLine(_codeBlockFence);
            }

            // Ensure that the last replacement block has been closed. While the block never makes it to the OpenedBlocks collection in the root processor,
            // calling Close for it ensures that it and its children's Close methods and events get called.
            childProcessor.Close(parentOfNewBlocks.LastChild);

            // BlockProcessors are pooled. Once we're done with innerProcessor, we must release it. This also removes all references to
            // tempContainerBlock, which should allow it to be collected quickly.
            childProcessor.ReleaseChild();
        }