/// <summary> /// Convert a JSON string into an instance of this class /// </summary> /// <param name="json"></param> /// <returns></returns> public static CodeBlockAnnotation ParseMetadata(string json, MarkdownDeep.Block codeBlock = null) { var response = JsonConvert.DeserializeObject <CodeBlockAnnotation>(json); // Check for Collection() syntax if (!string.IsNullOrEmpty(response.ResourceType)) { const string collectionPrefix = "collection("; const string collectionSuffix = ")"; if (response.ResourceType.StartsWith(collectionPrefix, StringComparison.OrdinalIgnoreCase) && response.ResourceType.EndsWith(collectionSuffix, StringComparison.OrdinalIgnoreCase)) { response.IsCollection = true; var newResourceType = response.ResourceType.Substring(collectionPrefix.Length); newResourceType = newResourceType.Substring(0, newResourceType.Length - collectionSuffix.Length); response.ResourceType = newResourceType; } } if (codeBlock != null) { // See if we can infer anything that's missing from response if (response.BlockType == CodeBlockType.Unknown) { response.BlockType = InferBlockType(codeBlock, response.ResourceType); } } return(response); }
/// <summary> /// Convert a JSON string into an instance of this class /// </summary> /// <param name="json"></param> /// <returns></returns> public static CodeBlockAnnotation ParseMetadata(string json, MarkdownDeep.Block codeBlock = null) { var response = JsonConvert.DeserializeObject <CodeBlockAnnotation>(json); if (codeBlock != null) { // See if we can infer anything that's missing from response if (response.BlockType == CodeBlockType.Unknown) { response.BlockType = InferBlockType(codeBlock, response.ResourceType); } } return(response); }
internal void FreeBlock(Block b) { m_SpareBlocks.Push(b); }
public void AddFootnote(Block footnote) { m_Footnotes[(string)footnote.data] = footnote; }
// Scan from the current position to the end of the html section internal bool ScanHtml(Block b) { // Remember start of html int posStartPiece = this.position; // Parse a HTML tag HtmlTag openingTag = HtmlTag.Parse(this); if (openingTag == null) return false; // Closing tag? if (openingTag.closing) return false; // Safe mode? bool bHasUnsafeContent = false; if (m_markdown.SafeMode && !openingTag.IsSafe()) bHasUnsafeContent = true; HtmlTagFlags flags = openingTag.Flags; // Is it a block level tag? if ((flags & HtmlTagFlags.Block) == 0) return false; // Closed tag, hr or comment? if ((flags & HtmlTagFlags.NoClosing) != 0 || openingTag.closed) { SkipLinespace(); SkipEol(); b.contentEnd = position; b.blockType = bHasUnsafeContent ? BlockType.unsafe_html : BlockType.html; return true; } // Can it also be an inline tag? if ((flags & HtmlTagFlags.Inline) != 0) { // Yes, opening tag must be on a line by itself SkipLinespace(); if (!eol) return false; } // Head block extraction? bool bHeadBlock = m_markdown.ExtractHeadBlocks && string.Compare(openingTag.name, "head", true) == 0; int headStart = this.position; // Work out the markdown mode for this element if (!bHeadBlock && m_markdown.ExtraMode) { MarkdownInHtmlMode MarkdownMode = this.GetMarkdownMode(openingTag); if (MarkdownMode != MarkdownInHtmlMode.NA) { return this.ProcessMarkdownEnabledHtml(b, openingTag, MarkdownMode); } } List<Block> childBlocks = null; // Now capture everything up to the closing tag and put it all in a single HTML block int depth = 1; while (!eof) { // Find next angle bracket if (!Find('<')) break; // Save position of current tag int posStartCurrentTag = position; // Is it a html tag? HtmlTag tag = HtmlTag.Parse(this); if (tag == null) { // Nope, skip it SkipForward(1); continue; } // Safe mode checks if (m_markdown.SafeMode && !tag.IsSafe()) bHasUnsafeContent = true; // Ignore self closing tags if (tag.closed) continue; // Markdown enabled content? if (!bHeadBlock && !tag.closing && m_markdown.ExtraMode && !bHasUnsafeContent) { MarkdownInHtmlMode MarkdownMode = this.GetMarkdownMode(tag); if (MarkdownMode != MarkdownInHtmlMode.NA) { Block markdownBlock = this.CreateBlock(); if (this.ProcessMarkdownEnabledHtml(markdownBlock, tag, MarkdownMode)) { if (childBlocks==null) { childBlocks = new List<Block>(); } // Create a block for everything before the markdown tag if (posStartCurrentTag > posStartPiece) { Block htmlBlock = this.CreateBlock(); htmlBlock.buf = input; htmlBlock.blockType = BlockType.html; htmlBlock.contentStart = posStartPiece; htmlBlock.contentLen = posStartCurrentTag - posStartPiece; childBlocks.Add(htmlBlock); } // Add the markdown enabled child block childBlocks.Add(markdownBlock); // Remember start of the next piece posStartPiece = position; continue; } else { this.FreeBlock(markdownBlock); } } } // Same tag? if (tag.name == openingTag.name) { if (tag.closing) { depth--; if (depth == 0) { // End of tag? SkipLinespace(); SkipEol(); // If anything unsafe detected, just encode the whole block if (bHasUnsafeContent) { b.blockType = BlockType.unsafe_html; b.contentEnd = position; return true; } // Did we create any child blocks if (childBlocks != null) { // Create a block for the remainder if (position > posStartPiece) { Block htmlBlock = this.CreateBlock(); htmlBlock.buf = input; htmlBlock.blockType = BlockType.html; htmlBlock.contentStart = posStartPiece; htmlBlock.contentLen = position - posStartPiece; childBlocks.Add(htmlBlock); } // Return a composite block b.blockType = BlockType.Composite; b.contentEnd = position; b.children = childBlocks; return true; } // Extract the head block content if (bHeadBlock) { var content = this.Substring(headStart, posStartCurrentTag - headStart); m_markdown.HeadBlockContent = (m_markdown.HeadBlockContent ?? "") + content.Trim() + "\n"; b.blockType = BlockType.html; b.contentStart = position; b.contentEnd = position; b.lineStart = position; return true; } // Straight html block b.blockType = BlockType.html; b.contentEnd = position; return true; } } else { depth++; } } } // Rewind to just after the tag return false; }
internal void FreeBlock(Block b) { m_markdown.FreeBlock(b); }
BlockType EvaluateLine(Block b) { // Empty line? if (eol) return BlockType.Blank; // Save start of line position int line_start= position; // ## Heading ## char ch=current; if (ch == '#') { // Work out heading level int level = 1; SkipForward(1); while (current == '#') { level++; SkipForward(1); } // Limit of 6 if (level > 6) level = 6; // Skip any whitespace SkipLinespace(); // Save start position b.contentStart = position; // Jump to end SkipToEol(); // In extra mode, check for a trailing HTML ID if (m_markdown.ExtraMode && !m_markdown.SafeMode) { int end=position; string strID = Utils.StripHtmlID(input, b.contentStart, ref end); if (strID!=null) { b.data = strID; position = end; } } // Rewind over trailing hashes while (position>b.contentStart && CharAtOffset(-1) == '#') { SkipForward(-1); } // Rewind over trailing spaces while (position>b.contentStart && char.IsWhiteSpace(CharAtOffset(-1))) { SkipForward(-1); } // Create the heading block b.contentEnd = position; SkipToEol(); return BlockType.h1 + (level - 1); } // Check for entire line as - or = for setext h1 and h2 if (ch=='-' || ch=='=') { // Skip all matching characters char chType = ch; while (current==chType) { SkipForward(1); } // Trailing whitespace allowed SkipLinespace(); // If not at eol, must have found something other than setext header if (eol) { return chType == '=' ? BlockType.post_h1 : BlockType.post_h2; } position = line_start; } // MarkdownExtra Table row indicator? if (m_markdown.ExtraMode) { TableSpec spec = TableSpec.Parse(this); if (spec!=null) { b.data = spec; return BlockType.table_spec; } position = line_start; } // Fenced code blocks? if (m_markdown.ExtraMode && (ch == '~' || ch == '`')) { if (ProcessFencedCodeBlock(b)) return b.blockType; // Rewind position = line_start; } // Scan the leading whitespace, remembering how many spaces and where the first tab is int tabPos = -1; int leadingSpaces = 0; while (!eol) { if (current == ' ') { if (tabPos < 0) leadingSpaces++; } else if (current == '\t') { if (tabPos < 0) tabPos = position; } else { // Something else, get out break; } SkipForward(1); } // Blank line? if (eol) { b.contentEnd = b.contentStart; return BlockType.Blank; } // 4 leading spaces? if (leadingSpaces >= 4) { b.contentStart = line_start + 4; return BlockType.indent; } // Tab in the first 4 characters? if (tabPos >= 0 && tabPos - line_start<4) { b.contentStart = tabPos + 1; return BlockType.indent; } // Treat start of line as after leading whitespace b.contentStart = position; // Get the next character ch = current; // Html block? if (ch == '<') { // Scan html block if (ScanHtml(b)) return b.blockType; // Rewind position = b.contentStart; } // Block quotes start with '>' and have one space or one tab following if (ch == '>') { // Block quote followed by space if (IsLineSpace(CharAtOffset(1))) { // Skip it and create quote block SkipForward(2); b.contentStart = position; return BlockType.quote; } SkipForward(1); b.contentStart = position; return BlockType.quote; } // Horizontal rule - a line consisting of 3 or more '-', '_' or '*' with optional spaces and nothing else if (ch == '-' || ch == '_' || ch == '*') { int count = 0; while (!eol) { char chType = current; if (current == ch) { count++; SkipForward(1); continue; } if (IsLineSpace(current)) { SkipForward(1); continue; } break; } if (eol && count >= 3) { if (m_markdown.UserBreaks) return BlockType.user_break; else return BlockType.hr; } // Rewind position = b.contentStart; } // Abbreviation definition? if (m_markdown.ExtraMode && ch == '*' && CharAtOffset(1) == '[') { SkipForward(2); SkipLinespace(); Mark(); while (!eol && current != ']') { SkipForward(1); } var abbr = Extract().Trim(); if (current == ']' && CharAtOffset(1) == ':' && !string.IsNullOrEmpty(abbr)) { SkipForward(2); SkipLinespace(); Mark(); SkipToEol(); var title = Extract(); m_markdown.AddAbbreviation(abbr, title); return BlockType.Blank; } position = b.contentStart; } // Unordered list if ((ch == '*' || ch == '+' || ch == '-') && IsLineSpace(CharAtOffset(1))) { // Skip it SkipForward(1); SkipLinespace(); b.contentStart = position; return BlockType.ul_li; } // Definition if (ch == ':' && m_markdown.ExtraMode && IsLineSpace(CharAtOffset(1))) { SkipForward(1); SkipLinespace(); b.contentStart = position; return BlockType.dd; } // Ordered list if (char.IsDigit(ch)) { // Ordered list? A line starting with one or more digits, followed by a '.' and a space or tab // Skip all digits SkipForward(1); while (char.IsDigit(current)) SkipForward(1); if (SkipChar('.') && SkipLinespace()) { b.contentStart = position; return BlockType.ol_li; } position=b.contentStart; } // Reference link definition? if (ch == '[') { // Footnote definition? if (m_markdown.ExtraMode && CharAtOffset(1) == '^') { var savepos = position; SkipForward(2); string id; if (SkipFootnoteID(out id) && SkipChar(']') && SkipChar(':')) { SkipLinespace(); b.contentStart = position; b.data = id; return BlockType.footnote; } position = savepos; } // Parse a link definition LinkDefinition l = LinkDefinition.ParseLinkDefinition(this, m_markdown.ExtraMode); if (l!=null) { m_markdown.AddLinkDefinition(l); return BlockType.Blank; } } // Nothing special return BlockType.p; }
public Block CopyFrom(Block other) { blockType = other.blockType; buf = other.buf; contentStart = other.contentStart; contentLen = other.contentLen; lineStart = other.lineStart; lineLen = other.lineLen; return this; }
private static bool IsHeaderBlock(Block block, int maxDepth = 2) { if (null == block) { return false; } var blockType = block.BlockType; if (maxDepth >= 1 && blockType == BlockType.h1) return true; if (maxDepth >= 2 && blockType == BlockType.h2) return true; if (maxDepth >= 3 && blockType == BlockType.h3) return true; if (maxDepth >= 4 && blockType == BlockType.h4) return true; if (maxDepth >= 5 && blockType == BlockType.h5) return true; if (maxDepth >= 6 && blockType == BlockType.h6) return true; return false; }
protected string PreviewOfBlockContent(Block block) { if (block == null) return string.Empty; if (block.Content == null) return string.Empty; const int previewLength = 35; string contentPreview = block.Content.Length > previewLength ? block.Content.Substring(0, previewLength) : block.Content; contentPreview = contentPreview.Replace('\n', ' ').Replace('\r', ' '); return contentPreview; }
protected Config.DocumentHeader CreateHeaderFromBlock(Block block) { var header = new Config.DocumentHeader(); switch (block.BlockType) { case BlockType.h1: header.Level = 1; break; case BlockType.h2: header.Level = 2; break; case BlockType.h3: header.Level = 3; break; case BlockType.h4: header.Level = 4; break; case BlockType.h5: header.Level = 5; break; case BlockType.h6: header.Level = 6; break; default: throw new InvalidOperationException("block wasn't a header!"); } header.Title = block.Content; return header; }
/// <summary> /// Filters the blocks to just a collection of blocks that may be /// relevent for our purposes /// </summary> /// <returns>The code blocks.</returns> /// <param name="blocks">Blocks.</param> protected static List<Block> FindCodeBlocks(Block[] blocks) { var blockList = new List<Block>(); foreach (var block in blocks) { switch (block.BlockType) { case BlockType.codeblock: case BlockType.html: blockList.Add(block); break; default: break; } } return blockList; }
/// <summary> /// Convert an annotation and fenced code block in the documentation into something usable. Adds /// the detected object into one of the internal collections of resources, methods, or examples. /// </summary> /// <param name="metadata"></param> /// <param name="code"></param> public ItemDefinition ParseCodeBlock(Block metadata, Block code) { if (metadata.BlockType != BlockType.html) throw new ArgumentException("metadata block does not appear to be metadata"); if (code.BlockType != BlockType.codeblock) throw new ArgumentException("code block does not appear to be code"); var metadataJsonString = metadata.Content.Substring(4, metadata.Content.Length - 9); var annotation = CodeBlockAnnotation.FromJson(metadataJsonString); switch (annotation.BlockType) { case CodeBlockType.Resource: { var resource = new ResourceDefinition(annotation, code.Content, this, code.CodeLanguage); this.resources.Add(resource); return resource; } case CodeBlockType.Request: { var method = MethodDefinition.FromRequest(code.Content, annotation, this); if (string.IsNullOrEmpty(method.Identifier)) method.Identifier = string.Format("{0} #{1}", this.DisplayName, this.requests.Count); this.requests.Add(method); return method; } case CodeBlockType.Response: { MethodDefinition pairedRequest = null; if (!string.IsNullOrEmpty(annotation.MethodName)) { // Look up paired request by name pairedRequest = (from m in this.requests where m.Identifier == annotation.MethodName select m).FirstOrDefault(); } else { pairedRequest = Enumerable.Last(this.requests); } if (null == pairedRequest) { throw new InvalidOperationException(string.Format("Unable to locate the corresponding request for response block: {0}. Requests must be defined before a response.", annotation.MethodName)); } pairedRequest.AddExpectedResponse(code.Content, annotation); return pairedRequest; } case CodeBlockType.Example: { var example = new ExampleDefinition(annotation, code.Content, this, code.CodeLanguage); this.examples.Add(example); return example; } case CodeBlockType.Ignored: return null; case CodeBlockType.SimulatedResponse: { var method = Enumerable.Last(this.requests); method.AddSimulatedResponse(code.Content, annotation); return method; } case CodeBlockType.TestParams: { var method = Enumerable.Last(this.requests); method.AddTestParams(code.Content); return method; } default: throw new NotSupportedException("Unsupported block type: " + annotation.BlockType); } }
private PageAnnotation ParsePageAnnotation(Block block) { var commentText = StripHtmlCommentTags(block.Content).Trim(); if (!commentText.StartsWith("{")) return null; var response = JsonConvert.DeserializeObject<PageAnnotation>(commentText); if (null != response && null != response.Type && response.Type.Equals(PageAnnotationType, StringComparison.OrdinalIgnoreCase)) return response; return null; }
/// <summary> /// Convert an annotation and fenced code block in the documentation into something usable. Adds /// the detected object into one of the internal collections of resources, methods, or examples. /// </summary> /// <param name="metadata"></param> /// <param name="code"></param> public ItemDefinition ParseCodeBlock(Block metadata, Block code) { if (metadata.BlockType != BlockType.html) throw new ArgumentException("metadata block does not appear to be metadata"); if (code.BlockType != BlockType.codeblock) throw new ArgumentException("code block does not appear to be code"); var metadataJsonString = StripHtmlCommentTags(metadata.Content); CodeBlockAnnotation annotation = CodeBlockAnnotation.ParseMetadata(metadataJsonString, code); switch (annotation.BlockType) { case CodeBlockType.Resource: { ResourceDefinition resource; if (code.CodeLanguage.Equals("json", StringComparison.OrdinalIgnoreCase)) { resource = new JsonResourceDefinition(annotation, code.Content, this); } //else if (code.CodeLanguage.Equals("xml", StringComparison.OrdinalIgnoreCase)) //{ // //} else { throw new NotSupportedException("Unsupported resource definition language: " + code.CodeLanguage); } if (string.IsNullOrEmpty(resource.Name)) { throw new InvalidDataException("Resource definition is missing a name"); } this.resources.Add(resource); return resource; } case CodeBlockType.Request: { var method = MethodDefinition.FromRequest(code.Content, annotation, this); if (string.IsNullOrEmpty(method.Identifier)) method.Identifier = string.Format("{0} #{1}", this.DisplayName, this.requests.Count); this.requests.Add(method); return method; } case CodeBlockType.Response: { MethodDefinition pairedRequest = null; if (!string.IsNullOrEmpty(annotation.MethodName)) { // Look up paired request by name pairedRequest = (from m in this.requests where m.Identifier == annotation.MethodName select m).FirstOrDefault(); } else if (this.requests.Any()) { pairedRequest = Enumerable.Last(this.requests); } if (null == pairedRequest) { throw new InvalidOperationException(string.Format("Unable to locate the corresponding request for response block: {0}. Requests must be defined before a response.", annotation.MethodName)); } pairedRequest.AddExpectedResponse(code.Content, annotation); return pairedRequest; } case CodeBlockType.Example: { var example = new ExampleDefinition(annotation, code.Content, this, code.CodeLanguage); this.examples.Add(example); return example; } case CodeBlockType.Ignored: return null; case CodeBlockType.SimulatedResponse: { var method = Enumerable.Last(this.requests); method.AddSimulatedResponse(code.Content, annotation); return method; } case CodeBlockType.TestParams: { var method = Enumerable.Last(this.requests); method.AddTestParams(code.Content); return method; } default: { var errorMessage = string.Format("Unable to parse metadata block or unsupported block type. Line {1}. Content: {0}", metadata.Content, metadata.LineStart); throw new NotSupportedException(errorMessage); } } }
bool ProcessFencedCodeBlock(Block b) { char delim = current; // Extract the fence Mark(); while (current == delim) SkipForward(1); string strFence = Extract(); // Must be at least 3 long if (strFence.Length < 3) return false; // Rest of line must be blank SkipLinespace(); if (!eol) { // Look for a language specifier Mark(); while (char.IsLetterOrDigit(current) || current == '-') { SkipForward(1); } string codeblockLangauge = Extract(); b.CodeLanguage = codeblockLangauge; //return false; SkipLinespace(); } // Skip the eol and remember start of code SkipEol(); int startCode = position; // Find the end fence if (!Find(strFence)) return false; // Character before must be a eol char if (!IsLineEnd(CharAtOffset(-1))) return false; int endCode = position; // Skip the fence SkipForward(strFence.Length); // Whitespace allowed at end SkipLinespace(); if (!eol) return false; // Create the code block b.blockType = BlockType.codeblock; b.children = new List<Block>(); // Remove the trailing line end if (input[endCode - 1] == '\r' && input[endCode - 2] == '\n') endCode -= 2; else if (input[endCode - 1] == '\n' && input[endCode - 2] == '\r') endCode -= 2; else endCode--; // Create the child block with the entire content var child = CreateBlock(); child.blockType = BlockType.indent; child.buf = input; child.contentStart = startCode; child.contentEnd = endCode; b.children.Add(child); return true; }
private void AddHeaderToHierarchy(Stack<Config.DocumentHeader> headerStack, Block block) { var header = CreateHeaderFromBlock(block); if (header.Level == 1 || headerStack.Count == 0) { DocumentHeaders.Add(header); headerStack.Clear(); headerStack.Push(header); } else { var parentHeader = headerStack.Peek(); if (null != parentHeader && parentHeader.Level < header.Level) { // This is a child of that previous level, so we add it and push parentHeader.ChildHeaders.Add(header); headerStack.Push(header); } else if (null != parentHeader && parentHeader.Level >= header.Level) { // We need to pop back and find the right parent for this higher level while (headerStack.Count > 0 && headerStack.Peek().Level >= header.Level) { headerStack.Pop(); } if (headerStack.Count > 0) { parentHeader = headerStack.Peek(); parentHeader.ChildHeaders.Add(header); } headerStack.Push(header); } else { throw new InvalidOperationException("Something went wrong in the outline creation"); } } }
private PageAnnotation ParsePageAnnotation(Block block) { try { var response = JsonConvert.DeserializeObject<PageAnnotation>(StripHtmlCommentTags(block.Content)); if (null != response && null != response.Type && response.Type.Equals(PageAnnotationType, StringComparison.OrdinalIgnoreCase)) return response; } catch (Exception ex) { Debug.WriteLine("Ignored potential page annotation [{0}]: {1}", ex.Message, block.Content); } return null; }
/* * Spacing * * 1-3 spaces - Promote to indented if more spaces than original item * */ /* * BuildList - build a single <ol> or <ul> list */ private Block BuildList(List<Block> lines) { // What sort of list are we dealing with BlockType listType = lines[0].blockType; System.Diagnostics.Debug.Assert(listType == BlockType.ul_li || listType == BlockType.ol_li); // Preprocess // 1. Collapse all plain lines (ie: handle hardwrapped lines) // 2. Promote any unindented lines that have more leading space // than the original list item to indented, including leading // special chars int leadingSpace = lines[0].leadingSpaces; for (int i = 1; i < lines.Count; i++) { // Join plain paragraphs if ((lines[i].blockType == BlockType.p) && (lines[i - 1].blockType == BlockType.p || lines[i - 1].blockType == BlockType.ul_li || lines[i - 1].blockType==BlockType.ol_li)) { lines[i - 1].contentEnd = lines[i].contentEnd; FreeBlock(lines[i]); lines.RemoveAt(i); i--; continue; } if (lines[i].blockType != BlockType.indent && lines[i].blockType != BlockType.Blank) { int thisLeadingSpace = lines[i].leadingSpaces; if (thisLeadingSpace > leadingSpace) { // Change line to indented, including original leading chars // (eg: '* ', '>', '1.' etc...) lines[i].blockType = BlockType.indent; int saveend = lines[i].contentEnd; lines[i].contentStart = lines[i].lineStart + thisLeadingSpace; lines[i].contentEnd = saveend; } } } // Create the wrapping list item var List = new Block(listType == BlockType.ul_li ? BlockType.ul : BlockType.ol); List.children = new List<Block>(); // Process all lines in the range for (int i = 0; i < lines.Count; i++) { System.Diagnostics.Debug.Assert(lines[i].blockType == BlockType.ul_li || lines[i].blockType==BlockType.ol_li); // Find start of item, including leading blanks int start_of_li = i; while (start_of_li > 0 && lines[start_of_li - 1].blockType == BlockType.Blank) start_of_li--; // Find end of the item, including trailing blanks int end_of_li = i; while (end_of_li < lines.Count - 1 && lines[end_of_li + 1].blockType != BlockType.ul_li && lines[end_of_li + 1].blockType != BlockType.ol_li) end_of_li++; // Is this a simple or complex list item? if (start_of_li == end_of_li) { // It's a simple, single line item item System.Diagnostics.Debug.Assert(start_of_li == i); List.children.Add(CreateBlock().CopyFrom(lines[i])); } else { // Build a new string containing all child items bool bAnyBlanks = false; StringBuilder sb = m_markdown.GetStringBuilder(); for (int j = start_of_li; j <= end_of_li; j++) { var l = lines[j]; sb.Append(l.buf, l.contentStart, l.contentLen); sb.Append('\n'); if (lines[j].blockType == BlockType.Blank) { bAnyBlanks = true; } } // Create the item and process child blocks var item = new Block(BlockType.li); item.children = new BlockProcessor(m_markdown, m_bMarkdownInHtml, listType).Process(sb.ToString()); // If no blank lines, change all contained paragraphs to plain text if (!bAnyBlanks) { foreach (var child in item.children) { if (child.blockType == BlockType.p) { child.blockType = BlockType.span; } } } // Add the complex item List.children.Add(item); } // Continue processing from end of li i = end_of_li; } FreeBlocks(lines); lines.Clear(); // Continue processing after this item return List; }
public Block CopyFrom(Block other) { BlockType = other.BlockType; Buf = other.Buf; ContentStart = other.ContentStart; ContentLen = other.ContentLen; LineStart = other.LineStart; LineLen = other.LineLen; return this; }
bool ProcessFencedCodeBlock(Block b) { // Extract the fence Mark(); while (current == '~' || current == '`') SkipForward(1); string strFence = Extract(); // Must be at least 3 long if (strFence.Length < 3) return false; // Skip a space if needed SkipLinespace(); var lang = string.Empty; if (!eol) { // process language Mark(); SkipToEol(); lang = Extract(); } // Skip the eol and remember start of code SkipEol(); int startCode = position; // Find the end fence if (!Find(strFence)) return false; // Character before must be a eol char if (!IsLineEnd(CharAtOffset(-1))) return false; int endCode = position; // Skip the fence SkipForward(strFence.Length); // Whitespace allowed at end SkipLinespace(); if (!eol) return false; // Create the code block b.blockType = BlockType.codeblock; b.children = new List<Block>(); b.codeBlockLang = lang; // Remove the trailing line end if (input[endCode - 1] == '\r' && input[endCode - 2] == '\n') endCode -= 2; else if (input[endCode - 1] == '\n' && input[endCode - 2] == '\r') endCode -= 2; else endCode--; // Create the child block with the entire content var child = CreateBlock(); child.blockType = BlockType.indent; child.buf = input; child.contentStart = startCode; child.contentEnd = endCode; b.children.Add(child); return true; }
bool ProcessFencedCodeBlock(Block b) { // Extract the fence Mark(); while (Current == '~') SkipForward(1); var strFence = Extract(); // Must be at least 3 long if (strFence.Length < 3) return false; // Rest of line must be blank SkipLinespace(); if (!EndOfLine) return false; // Skip the EndOfLine and remember start of code SkipEol(); var startCode = Position; // Find the end fence if (!Find(strFence)) return false; // Character before must be a EndOfLine char if (!IsLineEnd(CharAtOffset(-1))) return false; var endCode = Position; // Skip the fence SkipForward(strFence.Length); // Whitespace allowed at end SkipLinespace(); if (!EndOfLine) return false; // Create the code block b.blockType = BlockType.codeblock; b.children = new List<Block>(); // Remove the trailing line end if (Input[endCode - 1] == '\r' && Input[endCode - 2] == '\n') endCode -= 2; else if (Input[endCode - 1] == '\n' && Input[endCode - 2] == '\r') endCode -= 2; else endCode--; // Create the child block with the entire content var child = CreateBlock(); child.blockType = BlockType.indent; child.buf = Input; child.contentStart = startCode; child.contentEnd = endCode; b.children.Add(child); return true; }
internal bool ProcessMarkdownEnabledHtml(Block b, HtmlTag openingTag, MarkdownInHtmlMode mode) { // Current position is just after the opening tag // Scan until we find matching closing tag int inner_pos = position; int depth = 1; bool bHasUnsafeContent = false; while (!eof) { // Find next angle bracket if (!Find('<')) break; // Is it a html tag? int tagpos = position; HtmlTag tag = HtmlTag.Parse(this); if (tag == null) { // Nope, skip it SkipForward(1); continue; } // In markdown off mode, we need to check for unsafe tags if (m_markdown.SafeMode && mode == MarkdownInHtmlMode.Off && !bHasUnsafeContent) { if (!tag.IsSafe()) bHasUnsafeContent = true; } // Ignore self closing tags if (tag.closed) continue; // Same tag? if (tag.name == openingTag.name) { if (tag.closing) { depth--; if (depth == 0) { // End of tag? SkipLinespace(); SkipEol(); b.blockType = BlockType.HtmlTag; b.data = openingTag; b.contentEnd = position; switch (mode) { case MarkdownInHtmlMode.Span: { Block span = this.CreateBlock(); span.buf = input; span.blockType = BlockType.span; span.contentStart = inner_pos; span.contentLen = tagpos - inner_pos; b.children = new List<Block>(); b.children.Add(span); break; } case MarkdownInHtmlMode.Block: case MarkdownInHtmlMode.Deep: { // Scan the internal content var bp = new BlockProcessor(m_markdown, mode == MarkdownInHtmlMode.Deep); b.children = bp.ScanLines(input, inner_pos, tagpos - inner_pos); break; } case MarkdownInHtmlMode.Off: { if (bHasUnsafeContent) { b.blockType = BlockType.unsafe_html; b.contentEnd = position; } else { Block span = this.CreateBlock(); span.buf = input; span.blockType = BlockType.html; span.contentStart = inner_pos; span.contentLen = tagpos - inner_pos; b.children = new List<Block>(); b.children.Add(span); } break; } } return true; } } else { depth++; } } } // Missing closing tag(s). return false; }
internal void CollapseLines(List<Block> blocks, List<Block> lines) { // Remove trailing blank lines while (lines.Count>0 && lines.Last().blockType == BlockType.Blank) { FreeBlock(lines.Pop()); } // Quit if empty if (lines.Count == 0) { return; } // What sort of block? switch (lines[0].blockType) { case BlockType.p: { // Collapse all lines into a single paragraph var para = CreateBlock(); para.blockType = BlockType.p; para.buf = lines[0].buf; para.contentStart = lines[0].contentStart; para.contentEnd = lines.Last().contentEnd; blocks.Add(para); FreeBlocks(lines); break; } case BlockType.quote: { // Create a new quote block var quote = new Block(BlockType.quote); quote.children = new BlockProcessor(m_markdown, m_bMarkdownInHtml, BlockType.quote).Process(RenderLines(lines)); FreeBlocks(lines); blocks.Add(quote); break; } case BlockType.ol_li: case BlockType.ul_li: blocks.Add(BuildList(lines)); break; case BlockType.dd: if (blocks.Count > 0) { var prev=blocks[blocks.Count-1]; switch (prev.blockType) { case BlockType.p: prev.blockType = BlockType.dt; break; case BlockType.dd: break; default: var wrapper = CreateBlock(); wrapper.blockType = BlockType.dt; wrapper.children = new List<Block>(); wrapper.children.Add(prev); blocks.Pop(); blocks.Add(wrapper); break; } } blocks.Add(BuildDefinition(lines)); break; case BlockType.footnote: m_markdown.AddFootnote(BuildFootnote(lines)); break; case BlockType.indent: { var codeblock = new Block(BlockType.codeblock); codeblock.children = new List<Block>(); codeblock.children.AddRange(lines); blocks.Add(codeblock); lines.Clear(); break; } } }
internal void CollapseLines(List<Block> blocks, List<Block> lines) { // Remove trailing blank lines while (lines.Count>0 && lines.Last().blockType == BlockType.Blank) { FreeBlock(lines.Pop()); } // Quit if empty if (lines.Count == 0) { return; } // What sort of block? switch (lines[0].blockType) { case BlockType.p: { // Collapse all lines into a single paragraph var para = CreateBlock(); para.blockType = BlockType.p; para.buf = lines[0].buf; para.contentStart = lines[0].contentStart; para.contentEnd = lines.Last().contentEnd; blocks.Add(para); FreeBlocks(lines); break; } case BlockType.quote: { // Create a new quote block var quote = new Block(BlockType.quote); quote.children = new BlockProcessor(m_markdown, m_bMarkdownInHtml, BlockType.quote).Process(RenderLines(lines)); FreeBlocks(lines); blocks.Add(quote); break; } case BlockType.ol_li: case BlockType.ul_li: blocks.Add(BuildList(lines)); break; case BlockType.dd: if (blocks.Count > 0) { var prev=blocks[blocks.Count-1]; switch (prev.blockType) { case BlockType.p: prev.blockType = BlockType.dt; break; case BlockType.dd: break; default: var wrapper = CreateBlock(); wrapper.blockType = BlockType.dt; wrapper.children = new List<Block>(); wrapper.children.Add(prev); blocks.Pop(); blocks.Add(wrapper); break; } } blocks.Add(BuildDefinition(lines)); break; case BlockType.footnote: m_markdown.AddFootnote(BuildFootnote(lines)); break; case BlockType.indent: { var codeblock = new Block(BlockType.codeblock); /* if (m_markdown.FormatCodeBlockAttributes != null) { // Does the line first line look like a syntax specifier var firstline = lines[0].Content; if (firstline.StartsWith("{{") && firstline.EndsWith("}}")) { codeblock.data = firstline.Substring(2, firstline.Length - 4); lines.RemoveAt(0); } } */ codeblock.children = new List<Block>(); codeblock.children.AddRange(lines); blocks.Add(codeblock); lines.Clear(); break; } } }
bool ProcessFencedCodeBlock(Block b) { char delim = current; // Extract the fence Mark(); while (current == delim) SkipForward(1); string strFence = Extract(); // Must be at least 3 long if (strFence.Length < 3) return false; if(m_markdown.GitHubCodeBlocks) { // check whether a name has been specified after the start ```. If so we'll store that into 'Data'. var languageName = string.Empty; // allow space between first fence and name SkipLinespace(); SkipIdentifier(ref languageName); b.data = string.IsNullOrWhiteSpace(languageName) ? "nohighlight" : languageName; // skip linespace to EOL SkipLinespace(); } else { // Rest of line must be blank SkipLinespace(); if(!eol) return false; } // Skip the eol and remember start of code SkipEol(); int startCode = position; // Find the end fence if (!Find(strFence)) return false; // Character before must be a eol char if (!IsLineEnd(CharAtOffset(-1))) return false; int endCode = position; // Skip the fence SkipForward(strFence.Length); // Whitespace allowed at end SkipLinespace(); if (!eol) return false; // Create the code block b.blockType = BlockType.codeblock; b.children = new List<Block>(); // Remove the trailing line end if (input[endCode - 1] == '\r' && input[endCode - 2] == '\n') endCode -= 2; else if (input[endCode - 1] == '\n' && input[endCode - 2] == '\r') endCode -= 2; else endCode--; // Create the child block with the entire content var child = CreateBlock(); child.blockType = BlockType.indent; child.buf = input; child.contentStart = startCode; child.contentEnd = endCode; b.children.Add(child); return true; }
public void FreeBlock(Block b) { m_SpareBlocks.Push(b); }
/// <summary> /// Convert a tablespec block into one of our internal object model representations /// </summary> /// <param name="tableSpecBlock"></param> /// <param name="lastHeaderBlock"></param> /// <param name="errors"></param> /// <returns></returns> public TableDefinition ParseTableSpec(Block tableSpecBlock, Block lastHeaderBlock, out ValidationError[] errors) { List<ValidationError> discoveredErrors = new List<ValidationError>(); List<ItemDefinition> items = new List<ItemDefinition>(); var tableShape = tableSpecBlock.Table; TableDecoder decoder = new TableDecoder { Type = TableBlockType.Unknown }; string headerText = null; // Try matching based on header if (null != lastHeaderBlock && null != lastHeaderBlock.Content) { headerText = lastHeaderBlock.Content; var matchingDecoder = FindDecoderFromHeaderText(headerText); if (null != matchingDecoder) decoder = matchingDecoder; } // Try matching based on shape if (decoder.Type == TableBlockType.Unknown && null != tableSpecBlock.Table) { var matchingDecoder = FindDecoderFromShape(tableShape); if (null != matchingDecoder) decoder = matchingDecoder; } switch (decoder.Type) { case TableBlockType.ErrorCodes: items.AddRange(ParseErrorTable(tableShape, decoder)); break; case TableBlockType.PathParameters: items.AddRange(ParseParameterTable(tableShape, ParameterLocation.Path, decoder)); break; case TableBlockType.ResourcePropertyDescriptions: case TableBlockType.RequestObjectProperties: case TableBlockType.ResponseObjectProperties: items.AddRange(ParseParameterTable(tableShape, ParameterLocation.JsonObject, decoder)); break; case TableBlockType.ResourceNavigationPropertyDescriptions: items.AddRange(ParseParameterTable(tableShape, ParameterLocation.JsonObject, decoder, true)); break; case TableBlockType.HttpHeaders: items.AddRange(ParseParameterTable(tableShape, ParameterLocation.Header, decoder)); break; case TableBlockType.QueryStringParameters: items.AddRange(ParseParameterTable(tableShape, ParameterLocation.QueryString, decoder)); break; case TableBlockType.EnumerationValues: items.AddRange(ParseEnumerationTable(tableShape, decoder)); break; case TableBlockType.AuthScopes: items.AddRange(ParseAuthScopeTable(tableShape, decoder)); break; case TableBlockType.Unknown: discoveredErrors.Add(new ValidationMessage(null, "Ignored unclassified table: headerText='{0}', tableHeaders='{1}'", headerText, tableShape.ColumnHeaders != null ? tableShape.ColumnHeaders.ComponentsJoinedByString(",") : "null")); break; default: discoveredErrors.Add(new ValidationMessage(null, "Ignored table: classification='{2}', headerText='{0}', tableHeaders='{1}'", headerText, tableShape.ColumnHeaders != null ? tableShape.ColumnHeaders.ComponentsJoinedByString(",") : "null", decoder.Type)); break; } errors = discoveredErrors.ToArray(); return new TableDefinition(decoder.Type, items, headerText); }
internal void AddFootnote(Block footnote) { m_Footnotes[(string)footnote.data] = footnote; }
public void FreeBlock(Block b) { m_markdown.FreeBlock(b); }
bool IsSectionHeader(Block b) { return b.blockType >= BlockType.h1 && b.blockType <= BlockType.h3; }
private bool HandleTabsExtension(Block b) { // skip '@tabs' if(!SkipString("@tabs")) { return false; } // ignore what's specified behind @tabs SkipToNextLine(); int startContent = this.Position; // find @end. if(!Find("@endtabs")) { return false; } // Character before must be a eol char if(!IsLineEnd(CharAtOffset(-1))) { return false; } int endContent = Position; // skip @end SkipString("@endtabs"); SkipLinespace(); if(!Eol) { return false; } // Remove the trailing line end endContent = UnskipCRLFBeforePos(endContent); b.BlockType = BlockType.tabs; // scan the content, as it can contain markdown statements. var contentProcessor = new BlockProcessor(m_markdown, m_markdown.MarkdownInHtml); var scanLines = contentProcessor.ScanLines(this.Input, startContent, endContent - startContent); // check whether the content is solely tab blocks. If not, we ignore this tabs specification. if(scanLines.Any(x=>x.BlockType != BlockType.tab)) { return false; } b.Children = scanLines; return true; }