// Handles closing of FlexiSectionBlocks. A FlexiSectionBlock is closed when another FlexiSectionBlock in the same tree with the same or // lower level opens. internal virtual void UpdateOpenFlexiSectionBlocks(BlockProcessor processor, FlexiSectionBlock flexiSectionBlock) { // Since sectioning content roots like blockquotes have their own discrete section trees, // we maintain a stack of stacks. Each stack represents the open branch of a tree. Stack <Stack <FlexiSectionBlock> > openFlexiSectionBlocks = GetOrCreateOpenFlexiSectionBlocks(processor.Document); // Discard stacks for closed branches while (openFlexiSectionBlocks.Count > 0) { // When a sectioning content root is closed, all of its children are closed, so the last section in its branch // will be closed. Under no circumstance will the section at the tip of a branch be closed without its ancestors // being closed as well. if (openFlexiSectionBlocks.Peek().Peek().IsOpen) { break; } openFlexiSectionBlocks.Pop(); } // Find parent container block - processor.CurrentContainer may be closed. processor.CurrentContainer is only updated when // BlockProcessor.ProcessNewBlocks calls BlockProcessor.CloseAll, so at this point, processor.CurrentContainer may not be the eventual // parent of our new FlexiSectionBlock. ContainerBlock parentContainerBlock = processor.CurrentContainer; while (!parentContainerBlock.IsOpen) // We will eventually reach the root MarkdownDocument (gauranteed to be open) if all other containers aren't open { parentContainerBlock = parentContainerBlock.Parent; } if (!(parentContainerBlock is FlexiSectionBlock)) // parentContainerBlock is a sectioning content root, for example, a blockquote. Create a new stack for a new tree { var newStack = new Stack <FlexiSectionBlock>(); newStack.Push(flexiSectionBlock); openFlexiSectionBlocks.Push(newStack); } else { Stack <FlexiSectionBlock> currentBranch = openFlexiSectionBlocks.Peek(); // If parentContainerBlock is a FlexiSectionBlock, at least 1 branch of open FlexiSectionBlocks exists // Close open FlexiSectionBlocks that have the same or higher levels FlexiSectionBlock flexiSectionBlockToClose = null; while (currentBranch.Count > 0) { if (currentBranch.Peek().Level < flexiSectionBlock.Level) { break; } flexiSectionBlockToClose = currentBranch.Pop(); } if (flexiSectionBlockToClose != null) { processor.Close(flexiSectionBlockToClose); } // Add new FlexiSectionBlock to current stack currentBranch.Push(flexiSectionBlock); } }
/// <inheritdoc /> public FlexiSectionBlock Create(int level, BlockProcessor blockProcessor, BlockParser blockParser) { (IFlexiSectionBlockOptions flexiSectionBlockOptions, IFlexiSectionBlocksExtensionOptions _) = _optionsService.CreateOptions(blockProcessor); // Level ValidateLevel(level); // Block name string blockName = ResolveBlockName(flexiSectionBlockOptions.BlockName); // Element SectioningContentElement element = flexiSectionBlockOptions.Element; ValidateElement(element); // Rendering mode FlexiSectionBlockRenderingMode renderingMode = flexiSectionBlockOptions.RenderingMode; ValidateRenderingMode(renderingMode); // Create FlexiSectionBlock var flexiSectionBlock = new FlexiSectionBlock(blockName, element, flexiSectionBlockOptions.LinkIcon, renderingMode, level, flexiSectionBlockOptions.Attributes, blockParser) { Column = blockProcessor.Column, Span = new SourceSpan(blockProcessor.Start, blockProcessor.Line.End), // TODO span should include children // BlockProcessor will assign Line }; // Create FlexiSectionHeadingBlock FlexiSectionHeadingBlock flexiSectionHeadingBlock = _flexiSectionHeadingBlockFactory.Create(blockProcessor, flexiSectionBlockOptions, blockParser); flexiSectionBlock.Add(flexiSectionHeadingBlock); // Close FlexiSectionBlocks with same or lower levels UpdateOpenFlexiSectionBlocks(blockProcessor, flexiSectionBlock); return(flexiSectionBlock); }