public static void ProcessStage2(Syntax.Block document, CommonMarkSettings settings = null) { if (document == null) { throw new ArgumentNullException("document"); } if (document.Tag != Syntax.BlockTag.Document) { throw new ArgumentException("The block element passed to this method must represent a top level document.", "document"); } if (settings == null) { settings = CommonMarkSettings.Default; } try { BlockMethods.ProcessInlines(document, document.ReferenceMap, settings); } catch (CommonMarkException) { throw; } catch (Exception ex) { throw new CommonMarkException("An error occurred during inline parsing.", ex); } }
public static void ProcessStage2(Syntax.Block document, CommonMarkSettings settings = null) { if (document == null) { throw new ArgumentNullException(document.ToString()); } if (document.Tag != Syntax.BlockTag.Document) { throw new ArgumentException("Блочный элемент, предоставляемый методу, должен быть высокоуровневым документом.", document.ToString()); } if (settings == null) { settings = CommonMarkSettings.Default; } try { BlockMethods.ProcessInlines(document, document.ReferenceMap, settings); } catch (CommonMarkException) { throw; } catch (Exception ex) { throw new CommonMarkException("Ошибка при инлайн-парсинге.", ex); } }
public static bool PositionSafeForSmartLink(Block ast, int start, int length) { if (ast == null) return true; var end = start + length; var blockTags = new[] {BlockTag.FencedCode, BlockTag.HtmlBlock, BlockTag.IndentedCode, BlockTag.ReferenceDefinition}; var inlineTags = new[] {InlineTag.Code, InlineTag.Link, InlineTag.RawHtml, InlineTag.Image}; var lastBlockTag = BlockTag.Document; foreach (var block in EnumerateBlocks(ast.FirstChild)) { if (block.SourcePosition + block.SourceLength < start) { lastBlockTag = block.Tag; continue; } if (block.SourcePosition >= end) return !blockTags.Any(tag => tag == lastBlockTag); if (blockTags.Any(tag => tag == block.Tag)) return false; return !EnumerateInlines(block.InlineContent) .TakeWhile(il => il.SourcePosition < end) .Where(il => il.SourcePosition + il.SourceLength > start) .Any(il => inlineTags.Any(tag => tag == il.Tag)); } return true; }
public static void ProcessStage3(Syntax.Block document, TextWriter target, CommonMarkSettings settings = null) { if (document == null) { throw new ArgumentNullException(document.ToString()); } if (target == null) { throw new ArgumentNullException(target.ToString()); } if (document.Tag != Syntax.BlockTag.Document) { throw new ArgumentException("Блочный элемент, предоставляемый методу, болжен быть высокоуровневым документом.", document.ToString()); } if (settings == null) { settings = CommonMarkSettings.Default; } try { switch (settings.OutputFormat) { case OutputFormat.Html: HtmlFormatterSlim.BlocksToHtml(target, document, settings); break; case OutputFormat.SyntaxTree: Printer.PrintBlocks(target, document, settings); break; case OutputFormat.CustomDelegate: if (settings.OutputDelegate == null) { throw new CommonMarkException("Если `settings.OutputFormat` установлен в `CustomDelegate`, свойство `settings.OutputDelegate` должно быть заполнено."); } settings.OutputDelegate(document, target, settings); break; default: throw new CommonMarkException("Неподдерживаемое значение '" + settings.OutputFormat + "' в `settings.OutputFormat`."); } } catch (CommonMarkException) { throw; } catch (IOException) { throw; } catch (Exception ex) { throw new CommonMarkException("Ошибка при форматировании документа.", ex); } }
public static void ProcessStage3(Syntax.Block document, TextWriter target, CommonMarkSettings settings = null) { if (document == null) { throw new ArgumentNullException("document"); } if (target == null) { throw new ArgumentNullException("target"); } if (document.Tag != Syntax.BlockTag.Document) { throw new ArgumentException("The block element passed to this method must represent a top level document.", "document"); } if (settings == null) { settings = CommonMarkSettings.Default; } try { switch (settings.OutputFormat) { case OutputFormat.Html: HtmlFormatterSlim.BlocksToHtml(target, document, settings); break; case OutputFormat.SyntaxTree: Printer.PrintBlocks(target, document, settings); break; case OutputFormat.CustomDelegate: if (settings.OutputDelegate == null) { throw new CommonMarkException("If `settings.OutputFormat` is set to `CustomDelegate`, the `settings.OutputDelegate` property must be populated."); } settings.OutputDelegate(document, target, settings); break; default: throw new CommonMarkException("Unsupported value '" + settings.OutputFormat + "' in `settings.OutputFormat`."); } } catch (CommonMarkException) { throw; } catch (IOException) { throw; } catch (Exception ex) { throw new CommonMarkException("An error occurred during formatting of the document.", ex); } }
/// <summary> /// Initializes a new instance of the <see cref="TopicParserResult"/> class. /// </summary> /// <param name="doc">Document block.</param> internal TopicParserResult(Block doc) { if (doc == null) throw new ArgumentNullException("doc"); if (doc.Tag != BlockTag.Document) throw new InvalidOperationException("Unexpected block tag: " + doc.Tag); Document = doc; }
public async Task WriteAsync(Block block) { await WriteStartElementAsync("para"); await WriteStartElementAsync("command"); await WriteChildInlinesAsync(block); await WriteEndElementAsync(); //command await WriteEndElementAsync(); //para }
private static string GetTitle(TopicData topic, Block block) { if (block == null || block.HeaderLevel != 1) return topic.Name; var title = string.Empty; for (var inline = block.InlineContent; inline != null; inline = inline.NextSibling) title += inline.LiteralContent; return title; }
/// <summary> /// Creates a new top-level document block. /// </summary> internal static Block CreateDocument() { #pragma warning disable 0618 Block e = new Block(BlockTag.Document, 1, 1, 0); #pragma warning restore 0618 e.ReferenceMap = new Dictionary<string, Reference>(); e.Top = e; return e; }
internal override async Task<SectionState> DoWriteStartSectionAsync(Block block, string title) { await WriteStartElementAsync("glossaryEntry"); await WriteStartElementAsync("terms"); foreach (var term in title.Split(',')) await WriteTermAsync(term); await WriteEndElementAsync(); //terms await WriteStartElementAsync("definition"); return SectionState.Content; }
private static void PrintPosition(bool enabled, TextWriter writer, Block block) { if (enabled) { writer.Write(" ["); writer.Write(block.SourcePosition); writer.Write('-'); writer.Write(block.SourceLastPosition); writer.Write(']'); } }
public void WriteChainedInlineHeader() { // Process Block block = new Block(BlockTag.AtxHeader, 0); block.InlineContent = new Inline("Title"); block.InlineContent.NextSibling = new Inline(" in many"); block.InlineContent.NextSibling.NextSibling = new Inline(" siblings"); string output = this.Process(block); // Assert Assert.AreEqual(string.Format(@"<!--UT [Title in many siblings](page-title-in-many-siblings)-->{0}<h0>Title in many siblings</h0>{0}", Environment.NewLine), output); }
public static IEnumerable<Block> EnumerateBlocks(Block block) { if (block == null) yield break; var stack = new Stack<Block>(); stack.Push(block); while (stack.Any()) { var next = stack.Pop(); yield return next; if (next.NextSibling != null) stack.Push(next.NextSibling); if (next.FirstChild != null) stack.Push(next.FirstChild); } }
internal override async Task WriteListItemAsync(Block block) { if (GetSectionState() != SectionState.Sections) { await base.WriteListItemAsync(block); return; } await WriteStartElementAsync("step"); await WriteStartElementAsync("content"); await WriteChildBlocksAsync(block); await WriteEndElementAsync(); //content await WriteEndElementAsync(); //step }
public FlowDocument BlocksToXaml(Block block, CommonMarkSettings settings) { //_checkBoxNumber = 0; FlowDocument document = new FlowDocument(); document.PagePadding = new Thickness(0); if (DocumentStyle != null) { document.Style = DocumentStyle; } BlocksToXamlInner(document, block, settings); return(document); }
public void WriteChainedInlineHeader() { // Process Block block = new Block(BlockTag.AtxHeader, 0); block.InlineContent = new Inline("Title"); block.InlineContent.NextSibling = new Inline(" in many"); block.InlineContent.NextSibling.NextSibling = new Inline(" siblings"); string output = this.Process(block); // Assert Assert.IsTrue(output.StartsWith(@"<a name=""page-title+in+many+siblings"">")); Assert.AreEqual(1, this.Formatter.Anchors.Length); Assert.AreEqual("page-title+in+many+siblings", this.Formatter.Anchors[0].Value); Assert.AreEqual("Title in many siblings", this.Formatter.Anchors[0].Label); Assert.AreEqual(0, this.Formatter.Anchors[0].Level); }
public static FlowDocument Format( CMBlock block, Func <string, string> urlResolver = null, Func <string, string> placeholderResolver = null) { var document = new FlowDocument(); FormatBlocks( new Settings { UrlResolver = urlResolver, PlaceholderResolver = placeholderResolver, }, block.FirstChild, document.Blocks); return(document); }
/// <summary> /// Check to see if a block ends with a blank line, descending if needed into lists and sublists. /// </summary> private static bool EndsWithBlankLine(Block block) { while (true) { if (block.IsLastLineBlank) return true; if (block.Tag != BlockTag.List && block.Tag != BlockTag.ListItem) return false; block = block.LastChild; if (block == null) return false; } }
internal override async Task WriteListAsync(Block block) { if (SectionLevel < 2) { await base.WriteListAsync(block); return; } await WriteEndIntroductionAsync(); //TODO procedure? await WriteStartElementAsync("steps"); await WriteListClassAsync(block, ListClass.Bullet); SetSectionState(SectionState.Sections); await WriteChildBlocksAsync(block); await WriteEndElementAsync(); //steps }
public static void ProcessStage3(Syntax.Block document, TextWriter target, CommonMarkSettings settings = null) { if (document == null) { throw new ArgumentNullException("document"); } if (target == null) { throw new ArgumentNullException("target"); } if (document.Tag != Syntax.BlockTag.Document) { throw new ArgumentException("The block element passed to this method must represent a top level document.", "document"); } if (settings == null) { settings = CommonMarkSettings.Default; } try { if (settings.OutputFormat == OutputFormat.SyntaxTree) { Printer.PrintBlocks(target, document, settings); } else { HtmlPrinter.BlocksToHtml(target, document, settings); } } catch (CommonMarkException) { throw; } catch (IOException) { throw; } catch (Exception ex) { throw new CommonMarkException("An error occurred during formatting of the document.", ex); } }
/// <summary> /// Specializes block writing for anchor injection. /// For each formatted header we generate an anchor based on the context name, the header content and eventually add an integer suffix in order to prevent conflicts. /// </summary> /// <param name="block">The block to process.</param> /// <param name="isOpening">Define whether the block is opening.</param> /// <param name="isClosing">Defines whether the block is closing.</param> /// <param name="ignoreChildNodes">return whether the processing ignored child nodes.</param> protected override void WriteBlock(Block block, bool isOpening, bool isClosing, out bool ignoreChildNodes) { // Filter opening header if (isOpening && null != block && block.Tag == BlockTag.AtxHeader) { // Retrieve header content string headerContent; if (null != block.InlineContent && null != block.InlineContent.LiteralContent) { // Read the whole content Inline inline = block.InlineContent; StringBuilder stringBuilder = new StringBuilder(); do { stringBuilder.Append(inline.LiteralContent); inline = inline.NextSibling; } while (null != inline); headerContent = stringBuilder.ToString(); } else { headerContent = "unknown"; } // Compute the anchor value string sectionId = HttpUtility.UrlEncode(headerContent.ToLower()); sectionId = string.Format("{0}-{1}", this.ContextName, sectionId) .Replace('+', '-'); // Detect anchor conflict if (sectionConflict.ContainsKey(sectionId)) { // Append the index sectionId = string.Format("{0}-{1}", sectionId, ++sectionConflict[sectionId]); } // Write anchor this.Write(string.Format(@"<!--{0} [{1}]({2})-->", this.PageSplittingIdentifier, headerContent, sectionId)); sectionConflict[sectionId] = 1; } // Trigger parent rendering for the default html rendering base.WriteBlock(block, isOpening, isClosing, out ignoreChildNodes); }
/// <summary> /// Writes the given CommonMark document to the output stream as HTML. /// </summary> public void WriteDocument(Block document) { if (document == null) throw new ArgumentNullException(nameof(document)); bool ignoreChildNodes; Block ignoreUntilBlockCloses = null; Inline ignoreUntilInlineCloses = null; foreach (var node in document.AsEnumerable()) { if (node.Block != null) { if (ignoreUntilBlockCloses != null) { if (ignoreUntilBlockCloses != node.Block) continue; ignoreUntilBlockCloses = null; } WriteBlock(node.Block, node.IsOpening, node.IsClosing, out ignoreChildNodes); if (ignoreChildNodes && !node.IsClosing) ignoreUntilBlockCloses = node.Block; } else if (ignoreUntilBlockCloses == null && node.Inline != null) { if (ignoreUntilInlineCloses != null) { if (ignoreUntilInlineCloses != node.Inline) continue; ignoreUntilInlineCloses = null; } WriteInline(node.Inline, node.IsOpening, node.IsClosing, out ignoreChildNodes); if (ignoreChildNodes && !node.IsClosing) ignoreUntilInlineCloses = node.Inline; } } }
/// <summary> /// Break out of all containing lists /// </summary> private static void BreakOutOfLists(ref Block blockRef, LineInfo line) { Block container = blockRef; Block b = container.Top; // find first containing list: while (b != null && b.Tag != BlockTag.List) b = b.LastChild; if (b != null) { while (container != null && container != b) { Finalize(container, line); container = container.Parent; } Finalize(b, line); blockRef = b.Parent; } }
private static void BlockToDocumentBlock(CommonMark.Syntax.Block block, ListItemCollection collection) { if (block == null) { return; } switch (block.Tag) { case BlockTag.ListItem: var listitemBlock = new ListItem(); collection.Add(listitemBlock); BlockToDocumentBlock(block.FirstChild, listitemBlock.Blocks); BlockToDocumentBlock(block.NextSibling, collection); break; default: throw new CommonMarkException("Block type " + block.Tag + " is not supported.", block); } }
static void FormatBlocks(Settings settings, CMBlock cmBlock, BlockCollection wdBlocks) { for (; cmBlock != null; cmBlock = cmBlock.NextSibling) { switch (cmBlock.Tag) { case BlockTag.AtxHeading: case BlockTag.SetextHeading: case BlockTag.Paragraph: var wdParagraph = new Paragraph(); if (cmBlock.Tag != BlockTag.Paragraph) { // FIXME: this should probably be a Section with header // level stored in Section.Tag or a custom style so it // can be configured in XAML. Out of time. wdParagraph.FontWeight = FontWeights.Bold; wdParagraph.FontSize = 14; } FormatInlines(settings, cmBlock.InlineContent, wdParagraph.Inlines); wdBlocks.Add(wdParagraph); break; case BlockTag.List: var wdList = new List(); for (var cmChildBlock = cmBlock.FirstChild; cmChildBlock != null; cmChildBlock = cmChildBlock.NextSibling) { var wdListItem = new ListItem(); FormatBlocks(settings, cmChildBlock.FirstChild, wdListItem.Blocks); wdList.ListItems.Add(wdListItem); } wdBlocks.Add(wdList); break; default: throw new NotImplementedException($"BlockTag.{cmBlock.Tag}"); } } }
private static void AddLine(Block block, LineInfo lineInfo, string ln, int offset, int length = -1) { if (!block.IsOpen) throw new CommonMarkException(string.Format(CultureInfo.InvariantCulture, "Attempted to add line '{0}' to closed container ({1}).", ln, block.Tag)); var len = length == -1 ? ln.Length - offset : length; if (len <= 0) return; var curSC = block.StringContent; if (curSC == null) { block.StringContent = curSC = new StringContent(); if (lineInfo.IsTrackingPositions) curSC.PositionTracker = new PositionTracker(lineInfo.LineOffset); } if (lineInfo.IsTrackingPositions) curSC.PositionTracker.AddOffset(lineInfo, offset, len); curSC.Append(ln, offset, len); }
public static IEnumerable<Block> EnumerateSpanningBlocks(Block ast, int startOffset, int endOffset) { return EnumerateBlocks(ast.FirstChild) .Where(b => (b.SourcePosition + b.SourceLength) > startOffset) .TakeWhile(b => b.SourcePosition < endOffset); }
public void OnTextChanged(string text) { var ast = ParseDocument(text); _abstractSyntaxTree = ast; }
/// <summary> /// Writes a <c>data-sourcepos="start-end"</c> attribute to the target writer. /// This method should only be called if <see cref="CommonMarkSettings.TrackSourcePosition"/> is set to <see langword="true"/>. /// Note that the attribute is preceded (but not succeeded) by a single space. /// </summary> protected void WritePositionAttribute(Block block) { HtmlFormatterSlim.PrintPosition(_target, block); }
/// <summary> /// Specializes block writing for anchor injection. /// For each formatted header we generate an anchor based on the context name, the header content and eventually add an integer suffix in order to prevent conflicts. /// </summary> /// <param name="block">The block to process.</param> /// <param name="isOpening">Define whether the block is opening.</param> /// <param name="isClosing">Defines whether the block is closing.</param> /// <param name="ignoreChildNodes">return whether the processing ignored child nodes.</param> protected override void WriteBlock(Block block, bool isOpening, bool isClosing, out bool ignoreChildNodes) { // Filter opening header if (isOpening && null != block && block.Tag == BlockTag.AtxHeader) { // Retrieve header content string headerContent; if (null != block.InlineContent && null != block.InlineContent.LiteralContent) { // Read the whole content Inline inline = block.InlineContent; StringBuilder stringBuilder = new StringBuilder(); do { stringBuilder.Append(inline.LiteralContent); inline = inline.NextSibling; } while (null != inline); headerContent = stringBuilder.ToString(); } else { headerContent = "unknown"; } // Compute the anchor value string anchor = string.Format("{0}-{1}", this.ContextName, headerContent).ToLower(); // Encode for url usage anchor = HttpUtility.UrlEncode(anchor); // Detect anchor conflict if (anchors.ContainsKey(anchor)) { // Compute an index to resolve conflicts int index; if (anchorConflicts.ContainsKey(anchor)) { index = ++anchorConflicts[anchor]; } else { index = anchorConflicts[anchor] = 2; } // Append the index anchor = string.Format("{0}-{1}", anchor, index); } // Write anchor this.Write(string.Format(@"<a name=""{0}""></a>", anchor)); // Keep track of the created anchor anchors[anchor] = new Anchor( label: headerContent, level: block.HeaderLevel, value: anchor); } // Trigger parent rendering for the default html rendering base.WriteBlock(block, isOpening, isClosing, out ignoreChildNodes); }
internal override async Task WriteRelatedTopicsAsync(Block block) { await WriteEndElementAsync(); //glossary }
public static void BlocksToFlowDocument(CommonMark.Syntax.Block blocks, FlowDocument document) { BlockToDocumentBlock(blocks, document.Blocks); }
/// <summary> /// Walk through the block, its children and siblings, parsing string content into inline content where appropriate. /// </summary> /// <param name="block">The document level block from which to start the processing.</param> /// <param name="refmap">The reference mapping used when parsing links.</param> /// <param name="settings">The settings that influence how the inline parsing is performed.</param> public static void ProcessInlines(Block block, Dictionary<string, Reference> refmap, CommonMarkSettings settings) { Stack<Inline> inlineStack = null; var stack = new Stack<Block>(); var parsers = settings.InlineParsers; var specialCharacters = settings.InlineParserSpecialCharacters; var subj = new Subject(refmap); StringContent sc; int delta; while (block != null) { var tag = block.Tag; if (tag == BlockTag.Paragraph || tag == BlockTag.AtxHeader || tag == BlockTag.SETextHeader) { sc = block.StringContent; if (sc != null) { sc.FillSubject(subj); delta = subj.Position; block.InlineContent = InlineMethods.parse_inlines(subj, refmap, parsers, specialCharacters); block.StringContent = null; if (sc.PositionTracker != null) { sc.PositionTracker.AddBlockOffset(-delta); AdjustInlineSourcePosition(block.InlineContent, sc.PositionTracker, ref inlineStack); } } } if (block.FirstChild != null) { if (block.NextSibling != null) stack.Push(block.NextSibling); block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else if (stack.Count > 0) { block = stack.Pop(); } else { block = null; } } }
private void WriteTable(Block table, IAddChild parent, CommonMarkSettings settings, Stack <InlineStackEntry> stack) { if ((settings.AdditionalFeatures & CommonMarkAdditionalFeatures.GithubStyleTables) == 0) { throw new CommonMarkException("Table encountered in AST, but GithubStyleTables are not enabled"); } var header = table.FirstChild; var firstRow = table.FirstChild.NextSibling; Table t = new Table(); parent.AddChild(t); if (TableStyle != null) { t.Style = TableStyle; } var tableHeadRowGroup = new TableRowGroup(); t.RowGroups.Add(tableHeadRowGroup); TableRow headRow = new TableRow(); if (TableHeadStyle != null) { headRow.Style = TableHeadStyle; } tableHeadRowGroup.Rows.Add(headRow); var numHeadings = 0; var curHeaderCell = header.FirstChild; while (curHeaderCell != null) { if (numHeadings >= table.TableHeaderAlignments.Length) { break; } var alignment = table.TableHeaderAlignments[numHeadings]; numHeadings++; TableCell cell = new TableCell(); if (TableCellStyle != null) { cell.Style = TableCellStyle; } InlinesToXaml(cell, curHeaderCell.InlineContent, settings, stack); if (alignment != TableHeaderAlignment.None) { switch (alignment) { case TableHeaderAlignment.Center: cell.TextAlignment = TextAlignment.Center; break; case TableHeaderAlignment.Left: cell.TextAlignment = TextAlignment.Left; break; case TableHeaderAlignment.Right: cell.TextAlignment = TextAlignment.Right; break; default: throw new CommonMarkException("Unexpected TableHeaderAlignment [" + alignment + "]"); } } headRow.Cells.Add(cell); curHeaderCell = curHeaderCell.NextSibling; } var tableBodyRowGroup = new TableRowGroup(); t.RowGroups.Add(tableBodyRowGroup); var curRow = firstRow; while (curRow != null) { TableRow row = new TableRow(); if (TableRowStyle != null) { row.Style = TableRowStyle; } tableBodyRowGroup.Rows.Add(row); var curRowCell = curRow.FirstChild; var numCells = 0; while (curRowCell != null && numCells < numHeadings) { var alignment = table.TableHeaderAlignments[numCells]; numCells++; TableCell cell = new TableCell(); if (TableCellStyle != null) { cell.Style = TableCellStyle; } row.Cells.Add(cell); if (alignment != TableHeaderAlignment.None) { switch (alignment) { case TableHeaderAlignment.Center: cell.TextAlignment = TextAlignment.Center; break; case TableHeaderAlignment.Left: cell.TextAlignment = TextAlignment.Left; break; case TableHeaderAlignment.Right: cell.TextAlignment = TextAlignment.Right; break; default: throw new CommonMarkException("Unexpected TableHeaderAlignment [" + alignment + "]"); } } InlinesToXaml(cell, curRowCell.InlineContent, settings, stack); curRowCell = curRowCell.NextSibling; } while (numCells < numHeadings) { numCells++; TableCell cell = new TableCell(); if (TableCellStyle != null) { cell.Style = TableCellStyle; } row.Cells.Add(cell); } curRow = curRow.NextSibling; } }
private void BlocksToXamlInner(FlowDocument parent, Block block, CommonMarkSettings settings) { _checkBoxNumber = 0; var stack = new Stack <BlockStackEntry>(); var inlineStack = new Stack <InlineStackEntry>(); bool stackTight = false; bool tight = false; bool trackPositions = settings.TrackSourcePosition; int x; IAddChild blockParent = parent; while (block != null) { var visitChildren = false; IAddChild lastParent = null; switch (block.Tag) { case BlockTag.Document: stackTight = false; visitChildren = true; lastParent = parent; break; case BlockTag.Paragraph: if (tight) { InlinesToXaml(blockParent, block.InlineContent, settings, inlineStack); } else { Paragraph paragraph = new Paragraph(); if (trackPositions) { PrintPosition(paragraph, block); } InlinesToXaml(paragraph, block.InlineContent, settings, inlineStack); blockParent.AddChild(paragraph); } break; case BlockTag.BlockQuote: Section blockquoteParagraph = new Section(); if (trackPositions) { PrintPosition(blockquoteParagraph, block); } if (QuoteStyle != null) { blockquoteParagraph.Style = QuoteStyle; } blockParent.AddChild(blockquoteParagraph); lastParent = blockParent; blockParent = blockquoteParagraph; stackTight = true; visitChildren = true; break; case BlockTag.ListItem: stackTight = tight; if (block.ListData != null && block.ListData.ListType == ListType.TaskList) { BlockUIContainer ctnr = (BlockUIContainer)blockParent; StackPanel sp = (StackPanel)ctnr.Child; visitChildren = false; TextBlock spcb = new TextBlock(); spcb.Style = TodoTextStyle; Inline checkBoxInline = block.FirstChild?.InlineContent; if (checkBoxInline != null) { InlinesToXaml(spcb, checkBoxInline, settings, inlineStack); } CheckBox bt = new CheckBox { IsChecked = block.TaskListItemIsChecked, Content = spcb, Tag = _checkBoxNumber }; bt.CommandParameter = bt; bt.Command = CheckBoxCheckedCommand; bt.Style = TodoCheckBoxStyle; sp.Children.Add(bt); //TODO: Add child stack panel and add block.FirstChild.NextSibling _checkBoxNumber++; } else { ListItem listItem = new ListItem(); if (ListItemStyle != null) { listItem.Style = ListItemStyle; } if (trackPositions) { PrintPosition(listItem, block); } blockParent.AddChild(listItem); lastParent = blockParent; blockParent = listItem; visitChildren = true; } break; case BlockTag.List: var data = block.ListData; IAddChild list; if (data.ListType == ListType.TaskList) { StackPanel sp = new StackPanel(); list = new BlockUIContainer(sp); } else { List theList = new List(); list = theList; theList.MarkerStyle = data.ListType == ListType.Bullet ? TextMarkerStyle.Disc : TextMarkerStyle.Decimal; if (ListStyle != null) { theList.Style = ListStyle; } } if (trackPositions) { PrintPosition((FrameworkContentElement)list, block); } // TODO: Check if first child starts with [ ] then it is a todo-list item // TODO: Set list.StartIndex if > 1 blockParent.AddChild(list); lastParent = blockParent; blockParent = list; stackTight = data.IsTight; visitChildren = true; break; case BlockTag.AtxHeading: case BlockTag.SetextHeading: Paragraph headerParagraph = new Paragraph(); if (trackPositions) { PrintPosition(headerParagraph, block); } InlinesToXaml(headerParagraph, block.InlineContent, settings, inlineStack); x = block.Heading.Level; switch (x) { case 1: headerParagraph.Style = Heading1Style; break; case 2: headerParagraph.Style = Heading2Style; break; case 3: headerParagraph.Style = Heading3Style; break; case 4: headerParagraph.Style = Heading4Style; break; default: headerParagraph.Style = Heading1Style; break; } blockParent.AddChild(headerParagraph); break; case BlockTag.IndentedCode: case BlockTag.FencedCode: Paragraph codeblockParagraph = new Paragraph(); if (trackPositions) { PrintPosition(codeblockParagraph, block); } if (CodeBlockStyle != null) { codeblockParagraph.Style = CodeBlockStyle; } var info = block.FencedCodeData == null ? null : block.FencedCodeData.Info; if (info != null && info.Length > 0) { //x = info.IndexOf(' '); //if (x == -1) // x = info.Length; //parent.WriteConstant(" class=\"language-"); //EscapeHtml(new StringPart(info, 0, x), parent); //parent.Write('\"'); } EscapeHtml(block.StringContent, codeblockParagraph); blockParent.AddChild(codeblockParagraph); break; case BlockTag.HtmlBlock: // cannot output source position for HTML blocks // block.StringContent.WriteTo(parent); //TODO: Unable to convert html to break; case BlockTag.ThematicBreak: var line = new Line() { X2 = 1, StrokeThickness = 1.0 }; var container = new BlockUIContainer(line); if (trackPositions) { PrintPosition(container, block); } if (LineStyle != null) { line.Style = LineStyle; } blockParent.AddChild(container); break; case BlockTag.Table: WriteTable(block, blockParent, settings, inlineStack); break; case BlockTag.ReferenceDefinition: break; default: throw new CommonMarkException("Block type " + block.Tag + " is not supported.", block); } if (visitChildren) { stack.Push(new BlockStackEntry(lastParent, block.NextSibling, tight)); tight = stackTight; block = block.FirstChild; } else if (block.NextSibling != null) { block = block.NextSibling; } else { block = null; } while (block == null && stack.Count > 0) { var entry = stack.Pop(); blockParent = entry.Parent; tight = entry.IsTight; block = entry.Target; } } }
internal static void PrintPosition(FrameworkContentElement writer, Block block) { writer.Tag = "DataSourcePos:" + block.SourcePosition.ToString(CultureInfo.InvariantCulture) + " - " + (block.SourcePosition + block.SourceLength).ToString(CultureInfo.InvariantCulture); }
public BlockStackEntry(IAddChild parent, Block target, bool isTight) { Parent = parent; Target = target; IsTight = isTight; }
/// <summary> /// Computes line number of a block in a page. /// </summary> /// <param name="block">The block to locate.</param> /// <param name="page">The page where the block is.</param> /// <returns>The block line.</returns> private int LocateBlockLine(Block block, Page page) { // Initialize the line to 1 int line = 1; // Load the page until the block source position char[] buffer = new char[block.SourcePosition]; string pagePath = this.fileSystem.FileInfo.FromFileName(page.FileSystemPath).FullName; using (StreamReader reader = new StreamReader(this.fileSystem.File.Open(pagePath, FileMode.Open, FileAccess.Read, FileShare.Read))) { reader.Read(buffer, 0, buffer.Length); } // Count the number of line break to increment line number foreach (char currentChar in buffer) { if ('\n' == currentChar) { ++line; } } // Return the line number return line; }
private static void BlockToDocumentBlock(CommonMark.Syntax.Block block, BlockCollection collection) { if (block == null) { return; } switch (block.Tag) { case BlockTag.Document: BlockToDocumentBlock(block.FirstChild, collection); break; case BlockTag.Paragraph: var paragraph = new Paragraph(); CommonMark.Syntax.Inline current = block.InlineContent; while (current != null) { var inline = InlineToDocumentInline(current); if (inline != null) { paragraph.Inlines.Add(inline); } current = current.NextSibling; } collection.Add(paragraph); BlockToDocumentBlock(block.NextSibling, collection); break; case BlockTag.BlockQuote: var quoteBlock = new Section() { Background = Brushes.LightGray, Margin = new Thickness(10) }; collection.Add(quoteBlock); BlockToDocumentBlock(block.FirstChild, quoteBlock.Blocks); BlockToDocumentBlock(block.NextSibling, collection); break; case BlockTag.ListItem: throw new InvalidOperationException(); case BlockTag.List: var listBlock = new List(); collection.Add(listBlock); if (block.FirstChild != null) { if (block.FirstChild.ListData.ListType == ListType.Ordered) { listBlock.MarkerStyle = TextMarkerStyle.Decimal; listBlock.StartIndex = block.FirstChild.ListData.Start; } BlockToDocumentBlock(block.FirstChild, listBlock.ListItems); } BlockToDocumentBlock(block.NextSibling, collection); break; case BlockTag.AtxHeading: case BlockTag.SetextHeading: var heading = GetHeadingItem(block.Heading.Level); heading.Inlines.Add(InlineToDocumentInline(block.InlineContent)); collection.Add(heading); BlockToDocumentBlock(block.NextSibling, collection); break; case BlockTag.IndentedCode: case BlockTag.FencedCode: var codeBlock = new Section() { Background = Brushes.LightGray, Margin = new Thickness(2) }; collection.Add(codeBlock); var code = block.StringContent.TakeFromStart(block.StringContent.Length - 1); codeBlock.Blocks.Add(new Paragraph(new Run(code) { FontFamily = new FontFamily("Courier New") })); BlockToDocumentBlock(block.NextSibling, collection); break; case BlockTag.HtmlBlock: // cannot output source position for HTML blocks //block.StringContent.WriteTo(writer); break; case BlockTag.ThematicBreak: var separator = new Rectangle(); separator.Stroke = new SolidColorBrush(Colors.LightGray); separator.StrokeThickness = 3; separator.Height = 2; separator.Width = double.NaN; var lineBlock = new BlockUIContainer(separator); collection.Add(lineBlock); BlockToDocumentBlock(block.NextSibling, collection); break; case BlockTag.ReferenceDefinition: break; default: throw new CommonMarkException("Block type " + block.Tag + " is not supported.", block); } }
/// <summary> /// Initializes a new instance of the <see cref="EnumeratorEntry"/> class. /// </summary> /// <param name="opening">Specifies if this instance represents the opening of the block element.</param> /// <param name="closing">Specifies if this instance represents the closing of the block element (returned by the /// enumerator after the children have been enumerated). Both <paramref name="closing"/> and <paramref name="opening"/> /// can be specified at the same time if there are no children for the <paramref name="block"/> element.</param> /// <param name="block">The block element being returned from the enumerator.</param> public EnumeratorEntry(bool opening, bool closing, Block block) { this.IsOpening = opening; this.IsClosing = closing; this.Block = block; }
/// <summary> /// Writes the specified block element to the output stream. Does not write the child nodes, instead /// the <paramref name="ignoreChildNodes"/> is used to notify the caller whether it should recurse /// into the child nodes. /// </summary> /// <param name="block">The block element to be written to the output stream.</param> /// <param name="isOpening">Specifies whether the block element is being opened (or started).</param> /// <param name="isClosing">Specifies whether the block element is being closed. If the block does not /// have child nodes, then both <paramref name="isClosing"/> and <paramref name="isOpening"/> can be /// <see langword="true"/> at the same time.</param> /// <param name="ignoreChildNodes">Instructs the caller whether to skip processing of child nodes or not.</param> protected virtual void WriteBlock(Block block, bool isOpening, bool isClosing, out bool ignoreChildNodes) { ignoreChildNodes = false; int x; switch (block.Tag) { case BlockTag.Document: break; case BlockTag.Paragraph: if (RenderTightParagraphs.Peek()) break; if (isOpening) { EnsureNewLine(); Write("<p"); if (Settings.TrackSourcePosition) WritePositionAttribute(block); Write('>'); } if (isClosing) WriteLine("</p>"); break; case BlockTag.BlockQuote: if (isOpening) { EnsureNewLine(); Write("<blockquote"); if (Settings.TrackSourcePosition) WritePositionAttribute(block); WriteLine(">"); RenderTightParagraphs.Push(false); } if (isClosing) { RenderTightParagraphs.Pop(); WriteLine("</blockquote>"); } break; case BlockTag.ListItem: if (isOpening) { EnsureNewLine(); Write("<li"); if (Settings.TrackSourcePosition) WritePositionAttribute(block); Write('>'); } if (isClosing) WriteLine("</li>"); break; case BlockTag.List: var data = block.ListData; if (isOpening) { EnsureNewLine(); Write(data.ListType == ListType.Bullet ? "<ul" : "<ol"); if (data.Start != 1) { Write(" start=\""); Write(data.Start.ToString(CultureInfo.InvariantCulture)); Write('\"'); } if (Settings.TrackSourcePosition) WritePositionAttribute(block); WriteLine(">"); RenderTightParagraphs.Push(data.IsTight); } if (isClosing) { WriteLine(data.ListType == ListType.Bullet ? "</ul>" : "</ol>"); RenderTightParagraphs.Pop(); } break; case BlockTag.AtxHeading: case BlockTag.SetextHeading: x = block.Heading.Level; if (isOpening) { EnsureNewLine(); Write("<h" + x.ToString(CultureInfo.InvariantCulture)); if (Settings.TrackSourcePosition) WritePositionAttribute(block); Write('>'); } if (isClosing) WriteLine("</h" + x.ToString(CultureInfo.InvariantCulture) + ">"); break; case BlockTag.IndentedCode: case BlockTag.FencedCode: ignoreChildNodes = true; EnsureNewLine(); Write("<pre><code"); if (Settings.TrackSourcePosition) WritePositionAttribute(block); var info = block.FencedCodeData == null ? null : block.FencedCodeData.Info; if (info != null && info.Length > 0) { x = info.IndexOf(' '); if (x == -1) x = info.Length; Write(" class=\"language-"); WriteEncodedHtml(new StringPart(info, 0, x)); Write('\"'); } Write('>'); WriteEncodedHtml(block.StringContent); WriteLine("</code></pre>"); break; case BlockTag.HtmlBlock: ignoreChildNodes = true; // cannot output source position for HTML blocks Write(block.StringContent); break; case BlockTag.ThematicBreak: ignoreChildNodes = true; if (Settings.TrackSourcePosition) { Write("<hr"); WritePositionAttribute(block); WriteLine(); } else { WriteLine("<hr />"); } break; case BlockTag.ReferenceDefinition: break; default: throw new CommonMarkException("Block type " + block.Tag + " is not supported.", block); } if (ignoreChildNodes && !isClosing) throw new InvalidOperationException("Block of type " + block.Tag + " cannot contain child nodes."); }
internal override async Task WriteStartIntroductionAsync(Block block) { await WriteStartElementAsync("glossary"); }
// Process one line at a time, modifying a block. // Returns 0 if successful. curptr is changed to point to // the currently open block. public static void IncorporateLine(LineInfo line, ref Block curptr) { var ln = line.Line; Block last_matched_container; var offset = 0; int matched; int i; ListData data; bool all_matched = true; Block cur = curptr; bool blank = false; int first_nonspace; char curChar; int indent; // container starts at the document root. var container = cur.Top; // for each containing block, try to parse the associated line start. // bail out on failure: container will point to the last matching block. while (container.LastChild != null && container.LastChild.IsOpen) { container = container.LastChild; first_nonspace = offset; while ((curChar = ln[first_nonspace]) == ' ') first_nonspace++; indent = first_nonspace - offset; blank = curChar == '\n'; switch (container.Tag) { case BlockTag.BlockQuote: { if (indent <= 3 && curChar == '>') { offset = first_nonspace + 1; if (ln[offset] == ' ') offset++; } else { all_matched = false; } break; } case BlockTag.ListItem: { if (indent >= container.ListData.MarkerOffset + container.ListData.Padding) offset += container.ListData.MarkerOffset + container.ListData.Padding; else if (blank) offset = first_nonspace; else all_matched = false; break; } case BlockTag.IndentedCode: { if (indent >= CODE_INDENT) offset += CODE_INDENT; else if (blank) offset = first_nonspace; else all_matched = false; break; } case BlockTag.AtxHeader: case BlockTag.SETextHeader: { // a header can never contain more than one line all_matched = false; if (blank) container.IsLastLineBlank = true; break; } case BlockTag.FencedCode: { // -1 means we've seen closer if (container.FencedCodeData.FenceLength == -1) { all_matched = false; if (blank) container.IsLastLineBlank = true; } else { // skip optional spaces of fence offset i = container.FencedCodeData.FenceOffset; while (i > 0 && ln[offset] == ' ') { offset++; i--; } } break; } case BlockTag.HtmlBlock: { if (blank) { container.IsLastLineBlank = true; all_matched = false; } break; } case BlockTag.Paragraph: { if (blank) { container.IsLastLineBlank = true; all_matched = false; } break; } } if (!all_matched) { container = container.Parent; // back up to last matching block break; } } last_matched_container = container; // check to see if we've hit 2nd blank line, break out of list: if (blank && container.IsLastLineBlank) BreakOutOfLists(ref container, line); var maybeLazy = cur.Tag == BlockTag.Paragraph; // unless last matched container is code block, try new container starts: while (container.Tag != BlockTag.FencedCode && container.Tag != BlockTag.IndentedCode && container.Tag != BlockTag.HtmlBlock) { first_nonspace = offset; while ((curChar = ln[first_nonspace]) == ' ') first_nonspace++; indent = first_nonspace - offset; blank = curChar == '\n'; var indented = indent >= CODE_INDENT; if (!indented && curChar == '>') { offset = first_nonspace + 1; // optional following character if (ln[offset] == ' ') offset++; container = CreateChildBlock(container, line, BlockTag.BlockQuote, first_nonspace); } else if (!indented && curChar == '#' && 0 != (matched = Scanner.scan_atx_header_start(ln, first_nonspace, ln.Length, out i))) { offset = first_nonspace + matched; container = CreateChildBlock(container, line, BlockTag.AtxHeader, first_nonspace); container.HeaderLevel = i; } else if (!indented && (curChar == '`' || curChar == '~') && 0 != (matched = Scanner.scan_open_code_fence(ln, first_nonspace, ln.Length))) { container = CreateChildBlock(container, line, BlockTag.FencedCode, first_nonspace); container.FencedCodeData = new FencedCodeData(); container.FencedCodeData.FenceChar = curChar; container.FencedCodeData.FenceLength = matched; container.FencedCodeData.FenceOffset = first_nonspace - offset; offset = first_nonspace + matched; } else if (!indented && curChar == '<' && Scanner.scan_html_block_tag(ln, first_nonspace, ln.Length)) { container = CreateChildBlock(container, line, BlockTag.HtmlBlock, first_nonspace); // note, we don't adjust offset because the tag is part of the text } else if (!indented && container.Tag == BlockTag.Paragraph && (curChar == '=' || curChar == '-') && 0 != (matched = Scanner.scan_setext_header_line(ln, first_nonspace, ln.Length)) && ContainsSingleLine(container.StringContent)) { container.Tag = BlockTag.SETextHeader; container.HeaderLevel = matched; offset = ln.Length - 1; } else if (!indented && !(container.Tag == BlockTag.Paragraph && !all_matched) && 0 != (Scanner.scan_hrule(ln, first_nonspace, ln.Length))) { // it's only now that we know the line is not part of a setext header: container = CreateChildBlock(container, line, BlockTag.HorizontalRuler, first_nonspace); Finalize(container, line); container = container.Parent; offset = ln.Length - 1; } else if ((!indented || container.Tag == BlockTag.List) && 0 != (matched = ParseListMarker(ln, first_nonspace, out data))) { // compute padding: offset = first_nonspace + matched; i = 0; while (i <= 5 && ln[offset + i] == ' ') i++; // i = number of spaces after marker, up to 5 if (i >= 5 || i < 1 || ln[offset] == '\n') { data.Padding = matched + 1; if (i > 0) offset++; } else { data.Padding = matched + i; offset += i; } // check container; if it's a list, see if this list item // can continue the list; otherwise, create a list container. data.MarkerOffset = indent; if (container.Tag != BlockTag.List || !ListsMatch(container.ListData, data)) { container = CreateChildBlock(container, line, BlockTag.List, first_nonspace); container.ListData = data; } // add the list item container = CreateChildBlock(container, line, BlockTag.ListItem, first_nonspace); container.ListData = data; } else if (indented && !maybeLazy && !blank) { offset += CODE_INDENT; container = CreateChildBlock(container, line, BlockTag.IndentedCode, offset); } else { break; } if (AcceptsLines(container.Tag)) { // if it's a line container, it can't contain other containers break; } maybeLazy = false; } // what remains at offset is a text line. add the text to the // appropriate container. first_nonspace = offset; if (offset >= ln.Length) curChar = '\0'; else while ((curChar = ln[first_nonspace]) == ' ') first_nonspace++; indent = first_nonspace - offset; blank = curChar == '\n'; // block quote lines are never blank as they start with > // and we don't count blanks in fenced code for purposes of tight/loose // lists or breaking out of lists. we also don't set last_line_blank // on an empty list item. container.IsLastLineBlank = (blank && container.Tag != BlockTag.BlockQuote && container.Tag != BlockTag.SETextHeader && container.Tag != BlockTag.FencedCode && !(container.Tag == BlockTag.ListItem && container.FirstChild == null && container.SourcePosition >= line.LineOffset)); Block cont = container; while (cont.Parent != null) { cont.Parent.IsLastLineBlank = false; cont = cont.Parent; } if (cur != last_matched_container && container == last_matched_container && !blank && cur.Tag == BlockTag.Paragraph && cur.StringContent.Length > 0) { AddLine(cur, line, ln, offset); } else { // not a lazy continuation // finalize any blocks that were not matched and set cur to container: while (cur != last_matched_container) { Finalize(cur, line); cur = cur.Parent; if (cur == null) throw new CommonMarkException("Cannot finalize container block. Last matched container tag = " + last_matched_container.Tag); } if (container.Tag == BlockTag.IndentedCode) { AddLine(container, line, ln, offset); } else if (container.Tag == BlockTag.FencedCode) { if ((indent <= 3 && curChar == container.FencedCodeData.FenceChar) && (0 != Scanner.scan_close_code_fence(ln, first_nonspace, container.FencedCodeData.FenceLength, ln.Length))) { // if closing fence, set fence length to -1. it will be closed when the next line is processed. container.FencedCodeData.FenceLength = -1; } else { AddLine(container, line, ln, offset); } } else if (container.Tag == BlockTag.HtmlBlock) { AddLine(container, line, ln, offset); } else if (blank) { // ??? do nothing } else if (container.Tag == BlockTag.AtxHeader) { int p = ln.Length - 1; // trim trailing spaces while (p >= 0 && (ln[p] == ' ' || ln[p] == '\n')) p--; // if string ends in #s, remove these: while (p >= 0 && ln[p] == '#') p--; // there must be a space before the last hashtag if (p < 0 || ln[p] != ' ') p = ln.Length - 1; AddLine(container, line, ln, first_nonspace, p - first_nonspace + 1); Finalize(container, line); container = container.Parent; } else if (AcceptsLines(container.Tag)) { AddLine(container, line, ln, first_nonspace); } else if (container.Tag != BlockTag.HorizontalRuler && container.Tag != BlockTag.SETextHeader) { // create paragraph container for line container = CreateChildBlock(container, line, BlockTag.Paragraph, first_nonspace); AddLine(container, line, ln, first_nonspace); } else { Utilities.Warning("Line {0} with container type {1} did not match any condition:\n\"{2}\"", line.LineNumber, container.Tag, ln); } curptr = container; } }
/// <summary> /// Adds a new block as child of another. Return the child. /// </summary> /// <remarks>Original: add_child</remarks> public static Block CreateChildBlock(Block parent, LineInfo line, BlockTag blockType, int startColumn) { // if 'parent' isn't the kind of block that can accept this child, // then back up til we hit a block that can. while (!CanContain(parent.Tag, blockType)) { Finalize(parent, line); parent = parent.Parent; } var startPosition = line.IsTrackingPositions ? line.CalculateOrigin(startColumn, true) : line.LineOffset; #pragma warning disable 0618 Block child = new Block(blockType, line.LineNumber, startColumn + 1, startPosition); #pragma warning restore 0618 child.Parent = parent; child.Top = parent.Top; var lastChild = parent.LastChild; if (lastChild != null) { lastChild.NextSibling = child; #pragma warning disable 0618 child.Previous = lastChild; #pragma warning restore 0618 } else { parent.FirstChild = child; } parent.LastChild = child; return child; }
/// <summary>Initializes a new instance of the <see cref="CommonMarkException" /> class with a specified error message, a reference to the element that caused it and a reference to the inner exception that is the cause of this exception.</summary> /// <param name="message">The error message that explains the reason for the exception.</param> /// <param name="block">The block element that is related to the exception cause.</param> /// <param name="innerException">The exception that is the cause of the current exception, or a <c>null</c> reference (<c>Nothing</c> in Visual Basic) if no inner exception is specified.</param> public CommonMarkException(string message, Block block, Exception innerException = null) : base(message, innerException) { this.BlockElement = block; }