Beispiel #1
0
        /// <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);
        }
Beispiel #2
0
        /// <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;
        }
Beispiel #8
0
 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;
 }
Beispiel #9
0
        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;
        }
Beispiel #10
0
        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;
        }
Beispiel #11
0
 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;
 }
Beispiel #12
0
 /// <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;
 }
Beispiel #13
0
        /// <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);
            }
        }
Beispiel #14
0
        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;
        }
Beispiel #15
0
        /// <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;
        }
Beispiel #17
0
 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");
         }
     }
 }
Beispiel #18
0
 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;
        }
Beispiel #20
0
 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;
        }
Beispiel #22
0
        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;
        }
Beispiel #24
0
        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;
 }
Beispiel #32
0
 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;
 }