Пример #1
0
        /* Function: TryToGetLineComment
         * If the iterator is on a line that starts with a line comment symbol, this function moves the iterator past the entire
         * comment and returns true.  If the comment is a candidate for documentation it will also return it as a
         * <PossibleDocumentationComment>.  If the line does not start with a line comment symbol it will return false and
         * leave the iterator where it is.
         */
        protected bool TryToGetLineComment(ref LineIterator lineIterator, string commentSymbol,
                                           out PossibleDocumentationComment comment)
        {
            TokenIterator firstToken = lineIterator.FirstToken(LineBoundsMode.ExcludeWhitespace);

            if (firstToken.MatchesAcrossTokens(commentSymbol) == false)
            {
                comment = null;
                return(false);
            }

            comment       = new PossibleDocumentationComment();
            comment.Start = lineIterator;
            lineIterator.Next();

            // Since we're definitely returning a comment we can mark the comment symbols as we go rather than waiting until
            // the end.
            firstToken.SetCommentParsingTypeByCharacters(CommentParsingType.CommentSymbol, commentSymbol.Length);

            while (lineIterator.IsInBounds)
            {
                firstToken = lineIterator.FirstToken(LineBoundsMode.ExcludeWhitespace);

                if (firstToken.MatchesAcrossTokens(commentSymbol) == false)
                {
                    break;
                }

                firstToken.SetCommentParsingTypeByCharacters(CommentParsingType.CommentSymbol, commentSymbol.Length);
                lineIterator.Next();
            }

            comment.End = lineIterator;
            return(true);
        }
Пример #2
0
        /* Function: GetPossibleDocumentationComments
         *
         * Goes through the file looking for comments that could possibly contain documentation and returns them as a list.  These
         * comments are not guaranteed to have documentation in them, just to be acceptable candidates for them.  If there are no
         * comments it will return an empty list.
         *
         * All the comments in the returned list will have their comment symbols marked as <CommentParsingType.CommentSymbol>
         * in the tokenizer.  This allows further operations to be done on them in a language independent manner.  If you want to also
         * filter out text boxes and lines, use <Comments.LineFinder>.
         */
        override public List <PossibleDocumentationComment> GetPossibleDocumentationComments(Tokenizer source)
        {
            List <PossibleDocumentationComment> possibleDocumentationComments = new List <PossibleDocumentationComment>();

            LineIterator lineIterator = source.FirstLine;

            while (lineIterator.IsInBounds)
            {
                PossibleDocumentationComment possibleDocumentationComment = null;

                if (TryToGetBlockComment(ref lineIterator, out possibleDocumentationComment) ||
                    TryToGetLineComment(ref lineIterator, out possibleDocumentationComment))
                {
                    if (possibleDocumentationComment != null)
                    {
                        // XML can actually use the Javadoc comment format in addition to its own.
                        if (possibleDocumentationComment.Javadoc == true)
                        {
                            possibleDocumentationComment.XML = true;
                        }

                        possibleDocumentationComments.Add(possibleDocumentationComment);
                    }

                    // lineIterator should already be moved
                }
                else
                {
                    lineIterator.Next();
                }
            }

            return(possibleDocumentationComments);
        }
Пример #3
0
        /* Function: TryToGetPDLineComment
         *
         * If the line iterator is on a line comment, return it and all connected line comments as a
         * <PossibleDocumentationComment> and mark the symbols as <CommentParsingType.CommentSymbol>.  Returns
         * null otherwise.
         *
         * This function takes a separate comment symbol for the first line and all remaining lines, allowing you to detect
         * Javadoc line comments that start with ## and the remaining lines use #.  Both symbols can be the same if this isn't
         * required.  If openingMustBeAlone is set, no symbol can appear immediately after the first line symbol for this
         * function to succeed.  This allows you to specifically detect something like ## without also matching #######.
         */
        protected PossibleDocumentationComment TryToGetPDLineComment(LineIterator lineIterator,
                                                                     string firstSymbol, string remainderSymbol,
                                                                     bool openingMustBeAlone)
        {
            TokenIterator firstToken = lineIterator.FirstToken(LineBoundsMode.ExcludeWhitespace);

            if (firstToken.MatchesAcrossTokens(firstSymbol) == false)
            {
                return(null);
            }

            if (openingMustBeAlone)
            {
                TokenIterator nextToken = firstToken;
                nextToken.NextByCharacters(firstSymbol.Length);
                if (nextToken.FundamentalType == FundamentalType.Symbol)
                {
                    return(null);
                }
            }

            PossibleDocumentationComment comment = new PossibleDocumentationComment();

            comment.Start = lineIterator;
            lineIterator.Next();

            // Since we're definitely returning a comment (barring the operation being cancelled) we can mark the comment
            // symbols as we go rather than waiting until the end.
            firstToken.SetCommentParsingTypeByCharacters(CommentParsingType.CommentSymbol, firstSymbol.Length);

            while (lineIterator.IsInBounds)
            {
                firstToken = lineIterator.FirstToken(LineBoundsMode.ExcludeWhitespace);

                if (firstToken.MatchesAcrossTokens(remainderSymbol) == false)
                {
                    break;
                }

                firstToken.SetCommentParsingTypeByCharacters(CommentParsingType.CommentSymbol, remainderSymbol.Length);
                lineIterator.Next();
            }

            comment.End = lineIterator;
            return(comment);
        }
Пример #4
0
        /* Function: Parse
         *
         * Attempts to parse the passed comment into <Topics>.  Returns whether it was successful, and if so, adds them
         * to the list.  These fields will be set:
         *
         *		- CommentLineNumber
         *		- Body, if present
         *		- Summary, if available
         */
        public bool Parse(PossibleDocumentationComment sourceComment, List <Topic> topics)
        {
            if (HasAnyTag(sourceComment.Start.FirstToken(LineBoundsMode.CommentContent),
                          sourceComment.End.FirstToken(LineBoundsMode.Everything)) == false)
            {
                return(false);
            }

            LineIterator firstBlockLine = sourceComment.Start;

            while (firstBlockLine < sourceComment.End && IsFirstBlockLine(firstBlockLine) == false)
            {
                firstBlockLine.Next();
            }

            JavadocComment parsedComment = new JavadocComment();

            if (sourceComment.Start < firstBlockLine)
            {
                parsedComment.Description = GetText(sourceComment.Start, firstBlockLine);
            }

            while (firstBlockLine < sourceComment.End)
            {
                // There may be unrecognized blocks so we have to manually advance if GetBlockTag() fails.
                if (!TryToGetBlock(ref firstBlockLine, sourceComment.End, parsedComment))
                {
                    firstBlockLine.Next();
                }
            }

            Topic topic = GenerateTopic(parsedComment);

            if (topic != null)
            {
                topic.CommentLineNumber = sourceComment.Start.LineNumber;
                topics.Add(topic);
            }

            return(true);
        }
Пример #5
0
        /* Function: GetPossibleDocumentationComments
         *
         * Goes through the file looking for comments that could possibly contain documentation and returns them as a list.  These
         * comments are not guaranteed to have documentation in them, just to be acceptable candidates for them.  If there are no
         * comments it will return an empty list.
         */
        protected List <PossibleDocumentationComment> GetPossibleDocumentationComments(Tokenizer source)
        {
            List <PossibleDocumentationComment> possibleDocumentationComments = new List <PossibleDocumentationComment>();

            LineIterator lineIterator = source.FirstLine;

            while (lineIterator.IsInBounds)
            {
                bool foundComment = false;
                PossibleDocumentationComment possibleDocumentationComment = null;

                // Block comments
                if (blockCommentStringPairs != null)
                {
                    for (int i = 0; foundComment == false && i < blockCommentStringPairs.Length; i += 2)
                    {
                        foundComment = TryToGetBlockComment(ref lineIterator, blockCommentStringPairs[i], blockCommentStringPairs[i + 1],
                                                            out possibleDocumentationComment);
                    }
                }

                // Plain line comments
                if (foundComment == false && lineCommentStrings != null)
                {
                    for (int i = 0; foundComment == false && i < lineCommentStrings.Length; i++)
                    {
                        foundComment = TryToGetLineComment(ref lineIterator, lineCommentStrings[i], out possibleDocumentationComment);
                    }
                }

                // Nada.
                if (foundComment == false)
                {
                    lineIterator.Next();
                }
                else
                {
                    if (possibleDocumentationComment != null)
                    {
                        possibleDocumentationComments.Add(possibleDocumentationComment);
                    }

                    // lineIterator would have been moved already if foundComment is true
                }
            }

            return(possibleDocumentationComments);
        }
Пример #6
0
        // Function: GetPossibleDocumentationComments
        //
        // Goes through the file looking for comments that could possibly contain documentation and returns them as a list.  These
        // comments are not guaranteed to have documentation in them, just to be acceptable candidates for them.  If there are no
        // comments it will return an empty list.
        //
        // All the comments in the returned list will have their comment symbols marked as <CommentParsingType.CommentSymbol>
        // in the tokenizer.  This allows further operations to be done on them in a language independent manner.  If you want to also
        // filter out text boxes and lines, use <Comments.LineFinder>.
        //
        // Default Implementation:
        //
        // The default implementation uses the comment symbols found in <Language> or passed to the constructor.  You can override
        // this function if you need to do something more sophisticated, such as interpret the POD directives in Perl.
        //
        // Comments must be alone on a line to be a candidate for documentation, meaning that the comment symbol must be the
        // first non-whitespace character on a line, and in the case of block comments, nothing but whitespace may trail the closing
        // symbol.  The latter rule is important because a comment may start correctly but not end so, as in this prototype with Splint
        // annotation:
        //
        // > int get_array(integer_t id,
        // >               /*@out@*/ array_t array);
        //
        // Speaking of which, block comments surrounded by @ symbols are not included because they're Splint comments.  Not
        // including them in the possible documentation comments list means the Splint comment below won't end prototype detection.
        //
        // > void initialize ()
        // >    /*@globals undef globnum,
        // >               undef globname @*/
        // >    { ... }
        //
        // It also goes through the code line by line in a simple manner, not accounting for things like strings, so if a language contains
        // a multiline string whose content looks like a language comment it will be interpreted as one.  This isn't ideal but is accepted
        // as a conscious tradeoff because there are actually many different string formats (literal quotes denoted with \", literal quotes
        // denoted with "", Perl's q{} forms and here doc) so you can't account for them all in a generalized way.  Also, putting this in
        // an independent stage even when using full language support means comments don't disappear the way prototypes do if the
        // parser gets tripped up on something like an unmatched brace.
        //
        virtual public List <PossibleDocumentationComment> GetPossibleDocumentationComments(Tokenizer source)
        {
            List <PossibleDocumentationComment> possibleDocumentationComments = new List <PossibleDocumentationComment>();

            LineIterator lineIterator = source.FirstLine;

            while (lineIterator.IsInBounds)
            {
                bool foundComment = false;
                PossibleDocumentationComment possibleDocumentationComment = null;


                // Javadoc block comments

                // We test for these before regular block comments because they are usually extended versions of them, such
                // as /** and /*.

                if (javadocBlockCommentStringPairs != null)
                {
                    for (int i = 0; foundComment == false && i < javadocBlockCommentStringPairs.Length; i += 2)
                    {
                        foundComment = TryToGetBlockComment(ref lineIterator,
                                                            javadocBlockCommentStringPairs[i], javadocBlockCommentStringPairs[i + 1], true,
                                                            out possibleDocumentationComment);
                    }

                    if (possibleDocumentationComment != null)
                    {
                        possibleDocumentationComment.Javadoc = true;
                    }
                }


                // Plain block comments

                // We test block comments ahead of line comments because in Lua the line comments are a substring of them: --
                // versus --[[ and ]]--.

                if (foundComment == false && blockCommentStringPairs != null)
                {
                    for (int i = 0; foundComment == false && i < blockCommentStringPairs.Length; i += 2)
                    {
                        foundComment = TryToGetBlockComment(ref lineIterator,
                                                            blockCommentStringPairs[i], blockCommentStringPairs[i + 1], false,
                                                            out possibleDocumentationComment);
                    }

                    // Skip Splint comments so that they can appear in prototypes.
                    if (possibleDocumentationComment != null &&
                        possibleDocumentationComment.Start.FirstToken(LineBoundsMode.CommentContent).Character == '@')
                    {
                        LineIterator lastLine = possibleDocumentationComment.End;
                        lastLine.Previous();

                        TokenIterator lastToken, ignore;
                        lastLine.GetBounds(LineBoundsMode.CommentContent, out ignore, out lastToken);
                        lastToken.Previous();

                        if (lastToken.Character == '@')
                        {
                            possibleDocumentationComment = null;
                        }
                    }
                }


                // XML line comments

                if (foundComment == false && xmlLineCommentStrings != null)
                {
                    for (int i = 0; foundComment == false && i < xmlLineCommentStrings.Length; i++)
                    {
                        foundComment = TryToGetLineComment(ref lineIterator,
                                                           xmlLineCommentStrings[i], xmlLineCommentStrings[i], true,
                                                           out possibleDocumentationComment);
                    }

                    if (possibleDocumentationComment != null)
                    {
                        possibleDocumentationComment.XML = true;
                    }
                }


                // Ambiguous XML/Javadoc line comments

                // If an XML comment is found we check the same position for Javadoc because they may share an opening
                // symbol, such as ///.

                if (possibleDocumentationComment != null && possibleDocumentationComment.XML == true &&
                    javadocLineCommentStringPairs != null)
                {
                    LineIterator javadocLineIterator = possibleDocumentationComment.Start;
                    PossibleDocumentationComment possibleJavadocDocumentationComment = null;
                    bool foundJavadocComment = false;

                    for (int i = 0; foundJavadocComment == false && i < javadocLineCommentStringPairs.Length; i += 2)
                    {
                        foundJavadocComment = TryToGetLineComment(ref javadocLineIterator,
                                                                  javadocLineCommentStringPairs[i], javadocLineCommentStringPairs[i + 1], true,
                                                                  out possibleJavadocDocumentationComment);
                    }

                    if (possibleJavadocDocumentationComment != null)
                    {
                        // If the Javadoc comment is longer we use that instead of the XML since it may have detected the first
                        // line as XML and ignored the rest for not having the same symbol.  For example:
                        //
                        // ## Comment
                        // #
                        // #
                        //
                        // This will be detected as a one line XML comment and a three line Javadoc comment.

                        if (possibleJavadocDocumentationComment.End.LineNumber >
                            possibleDocumentationComment.End.LineNumber)
                        {
                            possibleDocumentationComment         = possibleJavadocDocumentationComment;
                            possibleDocumentationComment.Javadoc = true;
                            lineIterator = javadocLineIterator;
                        }


                        // If they're the same length...

                        else if (possibleJavadocDocumentationComment.End.LineNumber ==
                                 possibleDocumentationComment.End.LineNumber)
                        {
                            // If the comments are both one line long then it's genuinely ambiguous.  For example:
                            //
                            // ## Comment
                            //
                            // Is that a one line XML comment or a one line Javadoc comment?  We can't tell, so mark it as
                            // potentially either.

                            if (possibleDocumentationComment.Start.LineNumber ==
                                possibleDocumentationComment.End.LineNumber - 1)
                            {
                                possibleDocumentationComment.Javadoc = true;
                                // XML should already be set to true
                            }

                            // If the comments are equal length but more than one line then it's just interpreting the XML as
                            // a Javadoc start with a vertical line for the remainder, so leave it as XML.  For example:
                            //
                            // ## Comment
                            // ##
                            // ##
                            //
                            // That's clearly a three line XML comment and not a Javadoc comment with a vertical line.
                        }

                        // If the XML comment is longer just leave it and ignore the Javadoc one.
                    }
                }


                // Javadoc line comments

                if (foundComment == false && javadocLineCommentStringPairs != null)
                {
                    for (int i = 0; foundComment == false && i < javadocLineCommentStringPairs.Length; i += 2)
                    {
                        foundComment = TryToGetLineComment(ref lineIterator,
                                                           javadocLineCommentStringPairs[i], javadocLineCommentStringPairs[i + 1], true,
                                                           out possibleDocumentationComment);
                    }

                    if (possibleDocumentationComment != null)
                    {
                        possibleDocumentationComment.Javadoc = true;
                    }
                }


                // Plain line comments

                if (foundComment == false && lineCommentStrings != null)
                {
                    for (int i = 0; foundComment == false && i < lineCommentStrings.Length; i++)
                    {
                        foundComment = TryToGetLineComment(ref lineIterator,
                                                           lineCommentStrings[i], lineCommentStrings[i], false,
                                                           out possibleDocumentationComment);
                    }
                }


                // Nada.

                if (foundComment == false)
                {
                    lineIterator.Next();
                }
                else
                {
                    if (possibleDocumentationComment != null)
                    {
                        // XML can actually use the Javadoc comment format in addition to its own.
                        if (possibleDocumentationComment.Javadoc == true)
                        {
                            possibleDocumentationComment.XML = true;
                        }

                        possibleDocumentationComments.Add(possibleDocumentationComment);
                    }

                    // lineIterator would have been moved already if foundComment is true
                }
            }

            return(possibleDocumentationComments);
        }
Пример #7
0
        // Function: GetPossibleDocumentationComments
        //
        // Goes through the file looking for comments that could possibly contain documentation and returns them as a list.  These
        // comments are not guaranteed to have documentation in them, just to be acceptable candidates for them.  If there are no
        // comments it will return an empty list.
        //
        // All the comments in the returned list will have their comment symbols marked as <CommentParsingType.CommentSymbol>
        // in the tokenizer.  This allows further operations to be done on them in a language independent manner.  If you want to also
        // filter out text boxes and lines, use <Comments.LineFinder>.
        //
        override public List <PossibleDocumentationComment> GetPossibleDocumentationComments(Tokenizer source)
        {
            List <PossibleDocumentationComment> comments = new List <PossibleDocumentationComment>();
            LineIterator lineIterator = source.FirstLine;

            PODLineType podLineType;

            while (lineIterator.IsInBounds)
            {
                TokenIterator tokenIterator = lineIterator.FirstToken(LineBoundsMode.ExcludeWhitespace);


                // Hash comments

                if (tokenIterator.Character == '#')
                {
                    PossibleDocumentationComment comment = new PossibleDocumentationComment();
                    comment.Start = lineIterator;


                    // First line

                    if (tokenIterator.MatchesAcrossTokens("###"))
                    {
                        comment.Javadoc = false;
                        comment.XML     = true;
                    }
                    else if (tokenIterator.MatchesAcrossTokens("##"))
                    {
                        comment.Javadoc = true;
                        comment.XML     = false;
                    }
                    else                     // just "#"
                    {
                        comment.Javadoc = false;
                        comment.XML     = false;
                    }

                    lineIterator.Next();


                    // Subsequent lines

                    while (lineIterator.IsInBounds)
                    {
                        tokenIterator = lineIterator.FirstToken(LineBoundsMode.ExcludeWhitespace);

                        if (tokenIterator.Character != '#')
                        {
                            break;
                        }

                        if (tokenIterator.MatchesAcrossTokens("###"))
                        {
                            comment.Javadoc = false;
                            // XML is still possible
                        }
                        else if (tokenIterator.MatchesAcrossTokens("##"))
                        {
                            comment.Javadoc = false;
                            comment.XML     = false;
                        }
                        else                         // just "#"
                        {
                            // Javadoc is still possible
                            comment.XML = false;
                        }

                        lineIterator.Next();
                    }

                    comment.End = lineIterator;
                    comments.Add(comment);


                    // Go back and mark the tokens

                    int firstLineCount, subsequentLineCount;

                    if (comment.XML)
                    {
                        firstLineCount      = 3;
                        subsequentLineCount = 3;
                    }
                    else if (comment.Javadoc)
                    {
                        firstLineCount      = 2;
                        subsequentLineCount = 1;
                    }
                    else                     // plain
                    {
                        firstLineCount      = 1;
                        subsequentLineCount = 1;
                    }

                    LineIterator temp = comment.Start;
                    tokenIterator = temp.FirstToken(LineBoundsMode.ExcludeWhitespace);

                    tokenIterator.SetCommentParsingTypeByCharacters(CommentParsingType.CommentSymbol, firstLineCount);
                    temp.Next();

                    while (temp < comment.End)
                    {
                        tokenIterator = temp.FirstToken(LineBoundsMode.ExcludeWhitespace);
                        tokenIterator.SetCommentParsingTypeByCharacters(CommentParsingType.CommentSymbol, subsequentLineCount);
                        temp.Next();
                    }


                    // XML can actually appear in Javadoc comments

                    if (comment.Javadoc)
                    {
                        comment.XML = true;
                    }
                }


                // POD comments

                else if (TryToSkipPODLine(ref tokenIterator, out podLineType))
                {
                    TokenIterator podLineStart, podLineEnd;
                    lineIterator.GetBounds(LineBoundsMode.CommentContent, out podLineStart, out podLineEnd);

                    lineIterator.Tokenizer.SetCommentParsingTypeBetween(podLineStart, podLineEnd, CommentParsingType.CommentSymbol);

                    if (podLineType == PODLineType.StartNaturalDocs ||
                        podLineType == PODLineType.StartJavadoc)
                    {
                        PossibleDocumentationComment comment = new PossibleDocumentationComment();
                        comment.Start = lineIterator;

                        if (podLineType == PODLineType.StartJavadoc)
                        {
                            comment.Javadoc = true;
                        }

                        for (;;)
                        {
                            lineIterator.Next();

                            if (lineIterator.IsInBounds == false)
                            {
                                break;
                            }

                            tokenIterator = lineIterator.FirstToken(LineBoundsMode.CommentContent);

                            if (TryToSkipPODLine(ref tokenIterator, out podLineType) == true)
                            {
                                if (podLineType == PODLineType.End)
                                {
                                    lineIterator.GetBounds(LineBoundsMode.CommentContent, out podLineStart, out podLineEnd);
                                    lineIterator.Tokenizer.SetCommentParsingTypeBetween(podLineStart, podLineEnd, CommentParsingType.CommentSymbol);

                                    lineIterator.Next();
                                }

                                break;
                            }
                        }

                        comment.End = lineIterator;
                        comments.Add(comment);
                    }
                    else
                    {
                        lineIterator.Next();
                    }
                }

                else
                {
                    lineIterator.Next();
                }
            }

            return(comments);
        }
Пример #8
0
        /* Function: MarkTextBoxes
         *
         * Finds all text boxes in a comment and marks their tokens as <Tokenization.CommentParsingType.CommentDecoration>.
         * Vertical lines will only be detected if they are continuous throughout the comment and horizontal lines if they
         * are connected to it.  Freestanding horizontal lines are *not* detected here.  This function tolerates differing
         * symbols on corners and where embedded horizontal lines connect to the vertical.  It also tolerates tokens
         * marked with <Tokenization.CommentParsingType.CommentSymbol> differing.
         *
         * Examples:
         *
         * The box below will be marked completely, including the middle horizontal line.
         *
         * > // +----------+
         * > // | Title    |
         * > // +----------+
         * > // | Text     |
         * > // +----------+
         *
         * The middle horizontal line below will not be marked, because it is not attached.
         *
         * > // +----------+
         * > // | Title    |
         * > // | -------- |
         * > // | Text     |
         * > // +----------+
         *
         * Nor will the horizontal line below since there is no vertical.
         *
         * > // Title
         * > // ----------
         * > // Text
         *
         * Freestanding horizontal lines are not detected because they may be intended literally, such as when part of
         * a code section.  If you're not in such a section use <IsHorizontalLine()> before parsing a line to filter it out.
         *
         * > // (start code)
         * > // +-----+
         * > // | box |
         * > // +-----+
         * > // (end code)
         */
        public static void MarkTextBoxes(PossibleDocumentationComment comment)
        {
            char symbolA, symbolB, symbolC;
            int  symbolACount, symbolBCount, symbolCCount;

            char leftSymbol       = '\0';
            char rightSymbol      = '\0';
            int  leftSymbolCount  = 0;
            int  rightSymbolCount = 0;

            bool symbolIsAloneOnLine    = false;
            bool testedForVerticalLines = false;

            LineIterator  line = comment.Start;
            TokenIterator lineStart, lineEnd;

            // This should be okay to use since line numbers start at one.
            IDObjects.NumberSet horizontalLines = new IDObjects.NumberSet();

            // Skip leading blank lines since it's okay if they're not part of the text box.
            while (line < comment.End && line.IsEmpty(LineBoundsMode.CommentContent))
            {
                line.Next();
            }

            while (line < comment.End && line.IsEmpty(LineBoundsMode.CommentContent) == false)
            {
                line.GetBounds(LineBoundsMode.ExcludeWhitespace, out lineStart, out lineEnd);

                // Shrink the line to exclude its comment symbols, if any.  We didn't do this using the line bounds mode because
                // we need to know whether there was any whitespace between them and any horizontal lines.

                bool commentSymbolWithoutWhitespaceAtStart = false;
                bool commentSymbolWithoutWhitespaceAtEnd   = false;

                if (lineStart.CommentParsingType == CommentParsingType.CommentSymbol)
                {
                    commentSymbolWithoutWhitespaceAtStart = true;

                    do
                    {
                        lineStart.Next();
                    }while (lineStart.CommentParsingType == CommentParsingType.CommentSymbol);

                    if (lineStart.FundamentalType == FundamentalType.Whitespace)
                    {
                        commentSymbolWithoutWhitespaceAtStart = false;

                        do
                        {
                            lineStart.Next();
                        }while (lineStart.FundamentalType == FundamentalType.Whitespace);
                    }
                }

                lineEnd.Previous();
                if (lineEnd.CommentParsingType == CommentParsingType.CommentSymbol)
                {
                    commentSymbolWithoutWhitespaceAtEnd = true;

                    do
                    {
                        lineEnd.Previous();
                    }while (lineEnd.CommentParsingType == CommentParsingType.CommentSymbol);

                    if (lineEnd.FundamentalType == FundamentalType.Whitespace)
                    {
                        commentSymbolWithoutWhitespaceAtEnd = false;

                        do
                        {
                            lineEnd.Previous();
                        }while (lineEnd.FundamentalType == FundamentalType.Whitespace);
                    }
                }
                lineEnd.Next();


                // Horizontal line detection

                bool isHorizontalLine = false;

                CountSymbolLine(ref lineStart, lineEnd, out symbolA, out symbolB, out symbolC,
                                out symbolACount, out symbolBCount, out symbolCCount);

                if (commentSymbolWithoutWhitespaceAtStart == true &&
                    commentSymbolWithoutWhitespaceAtEnd == true &&
                    symbolACount >= 4 && symbolBCount == 0)
                {
                    isHorizontalLine = true;
                }

                else if (commentSymbolWithoutWhitespaceAtStart == true &&
                         symbolACount >= 4 &&
                         (symbolBCount == 0 || (symbolBCount <= 3 && symbolCCount == 0)))
                {
                    isHorizontalLine = true;
                }

                else if (commentSymbolWithoutWhitespaceAtEnd == true &&
                         ((symbolACount >= 1 && symbolACount <= 3 && symbolBCount >= 4 && symbolCCount == 0) ||
                          (symbolACount >= 4 && symbolBCount == 0)))
                {
                    isHorizontalLine = true;
                }

                else if ((symbolACount >= 4 && symbolBCount == 0) ||
                         (symbolACount >= 1 && symbolACount <= 3 && symbolBCount >= 4 && symbolCCount <= 3))
                {
                    isHorizontalLine = true;
                }

                // The horizontal line has to be the only thing on the line to count.
                if (isHorizontalLine && lineStart == lineEnd)
                {
                    horizontalLines.Add(line.LineNumber);
                }


                // Vertical line detection

                else if (testedForVerticalLines == false)
                {
                    // We permit the very first line to be different to allow for this:
                    //    /** text
                    //     * text
                    //     */
                    //
                    // However, don't skip the first line if it's a one line comment or we wouldn't be able to handle this:
                    //    ### text
                    //
                    if (line != comment.Start ||
                        (comment.End.LineNumber - comment.Start.LineNumber) == 1)
                    {
                        if (CountEdgeSymbols(line, out leftSymbol, out rightSymbol, out leftSymbolCount, out rightSymbolCount,
                                             out symbolIsAloneOnLine) == false)
                        {
                            return;
                        }

                        testedForVerticalLines = true;
                    }
                }

                else                 // testedForVerticalLines == true
                {
                    char lineLeftSymbol, lineRightSymbol;
                    int  lineLeftSymbolCount, lineRightSymbolCount;
                    bool lineSymbolIsAloneOnLine;

                    CountEdgeSymbols(line, out lineLeftSymbol, out lineRightSymbol, out lineLeftSymbolCount, out lineRightSymbolCount,
                                     out lineSymbolIsAloneOnLine);


                    // Account for a lone symbol being the right symbol.

                    if (lineSymbolIsAloneOnLine == true && symbolIsAloneOnLine == false && leftSymbolCount == 0 && rightSymbolCount > 0)
                    {
                        if (lineLeftSymbol != rightSymbol || lineLeftSymbolCount != rightSymbolCount)
                        {
                            return;
                        }
                    }
                    else if (lineSymbolIsAloneOnLine == false && symbolIsAloneOnLine == true && lineLeftSymbolCount == 0 && lineRightSymbolCount > 0)
                    {
                        if (lineRightSymbol != leftSymbol || lineRightSymbolCount != leftSymbolCount)
                        {
                            return;
                        }

                        rightSymbol      = leftSymbol;
                        leftSymbol       = '\0';
                        rightSymbolCount = leftSymbolCount;
                        leftSymbolCount  = 0;
                    }

                    // Otherwise it's okay to do a straight compare.
                    else
                    {
                        if (lineLeftSymbol != leftSymbol || lineLeftSymbolCount != leftSymbolCount)
                        {
                            leftSymbol      = '\0';
                            leftSymbolCount = 0;
                        }

                        if (lineRightSymbol != rightSymbol || lineRightSymbolCount != rightSymbolCount)
                        {
                            rightSymbol      = '\0';
                            rightSymbolCount = 0;
                        }

                        if (leftSymbolCount == 0 && rightSymbolCount == 0)
                        {
                            return;
                        }
                    }

                    // Turn off the overall alone flag if this line didn't have it.
                    if (lineSymbolIsAloneOnLine == false)
                    {
                        symbolIsAloneOnLine = false;
                    }
                }


                line.Next();
            }


            // If we stopped because we hit a blank line, this comment is only acceptable for marking text boxes if all the lines
            // left are blank.

            while (line < comment.End && line.IsEmpty(LineBoundsMode.CommentContent))
            {
                line.Next();
            }

            if (line != comment.End)
            {
                return;
            }


            // If we made it this far without returning it means we have a valid text box which we have to mark as comment decoration.

            line = comment.Start;

            while (line < comment.End)
            {
                line.GetBounds(LineBoundsMode.CommentContent, out lineStart, out lineEnd);

                if (horizontalLines.Contains(line.LineNumber))
                {
                    while (lineStart < lineEnd)
                    {
                        lineStart.CommentParsingType = CommentParsingType.CommentDecoration;
                        lineStart.Next();
                    }
                }

                else if (lineEnd > lineStart)
                {
                    // We test the characters against the symbols to account for any exceptions we allowed to go through
                    // in previous code.

                    for (int i = 0; i < leftSymbolCount; i++)
                    {
                        if (lineStart.Character == leftSymbol)
                        {
                            lineStart.CommentParsingType = CommentParsingType.CommentDecoration;
                            lineStart.Next();
                        }
                    }

                    lineEnd.Previous();
                    for (int i = 0; i < rightSymbolCount; i++)
                    {
                        if (lineEnd.Character == rightSymbol)
                        {
                            lineEnd.CommentParsingType = CommentParsingType.CommentDecoration;
                            lineEnd.Previous();
                        }
                    }
                }

                line.Next();
            }
        }
Пример #9
0
        /* Function: FindIncludeInOutput
         * Extracts and returns any comment content marked "include in output".  All comment symbols and extra indents
         * will be removed.  Returns null if the comment doesn't have any.
         */
        protected string FindIncludeInOutput(PossibleDocumentationComment comment)
        {
            Comments.LineFinder.MarkTextBoxes(comment);

            LineIterator iterator = comment.Start;


            // Find the "include in output" header if it exists

            while (iterator < comment.End &&
                   iterator.Match(IncludeInOutputRegex, LineBoundsMode.CommentContent).Success == false)
            {
                iterator.Next();
            }

            if (iterator >= comment.End)
            {
                return(null);
            }


            // Find the bounds of the content excluding whitespace and the shared indent level

            iterator.Next();

            while (iterator < comment.End && iterator.IsEmpty(LineBoundsMode.CommentContent))
            {
                iterator.Next();
            }

            LineIterator start        = iterator;
            LineIterator end          = iterator;
            int          commonIndent = 9999;

            while (iterator < comment.End)
            {
                if (iterator.IsEmpty(LineBoundsMode.CommentContent))
                {
                    iterator.Next();
                }
                else
                {
                    if (iterator.Indent(LineBoundsMode.CommentContent) < commonIndent)
                    {
                        commonIndent = iterator.Indent(LineBoundsMode.CommentContent);
                    }

                    iterator.Next();
                    end = iterator;
                }
            }


            // Build and return the comment content

            if (start >= end)
            {
                return(null);
            }

            StringBuilder output = new StringBuilder();

            do
            {
                int indentDifference = start.Indent(LineBoundsMode.CommentContent) - commonIndent;

                if (indentDifference > 0)
                {
                    output.Append(' ', indentDifference);
                }

                start.AppendTo(output, LineBoundsMode.CommentContent);
                output.AppendLine();

                start.Next();
            }while (start < end);

            return(output.ToString());
        }
Пример #10
0
        /* Function: TryToGetBlock
         * If the iterator is on a line that starts with one of the <BlockTags>, parses it, adds its content to the comment,
         * moves the iterator past it, and returns true.  If it is not at the start of a tag block it will return false and change
         * nothing.
         */
        protected bool TryToGetBlock(ref LineIterator lineIterator, LineIterator limit, JavadocComment comment)
        {
            if (lineIterator >= limit)
            {
                return(false);
            }


            // Get the complete content across multiple lines.

            string        tag;
            TokenIterator startOfContent;

            if (TryToGetFirstBlockLine(lineIterator, out tag, out startOfContent) == false)
            {
                return(false);
            }

            for (;;)
            {
                lineIterator.Next();

                if (lineIterator >= limit || IsFirstBlockLine(lineIterator))
                {
                    break;
                }
            }

            TokenIterator endOfContent = lineIterator.FirstToken(LineBoundsMode.Everything);


            // Any "@tag item description", possibly in a list
            if (tag == "exception" ||
                tag == "param" ||
                tag == "throws")
            {
                TokenIterator iterator = startOfContent;

                string symbol = null;
                TryToGetBlockSymbol(ref iterator, endOfContent, out symbol);
                iterator.NextPastWhitespace();

                string description = GetText(iterator, endOfContent);
                description = Normalize(description);

                if (symbol == null || symbol == "" || description == null || description == "")
                {
                    return(false);
                }

                var commentBlock = comment.GetListBlock(tag);
                commentBlock.Add(symbol, description);
                return(true);
            }

            // Any "@tag description", possibly in a list
            else if (tag == "author" ||
                     tag == "deprecated" ||
                     tag == "since" ||
                     tag == "version")
            {
                string description = GetText(startOfContent, endOfContent);
                description = Normalize(description);

                if (description == null || description == "")
                {
                    return(false);
                }

                if (tag == "deprecated")
                {
                    if (comment.Deprecated == null)
                    {
                        comment.Deprecated = description;
                    }
                    else
                    {
                        comment.Deprecated += description;
                    }
                }
                else
                {
                    var commentBlock = comment.GetListBlock(tag);
                    commentBlock.Add(null, description);
                }

                return(true);
            }

            // Any "@tag description" that can't be in a list
            else if (tag == "return")
            {
                string description = GetText(startOfContent, endOfContent);
                description = Normalize(description);

                if (description == null || description == "")
                {
                    return(false);
                }

                var commentBlock = comment.GetTextBlock(tag);
                commentBlock.Text.Append(description);
                return(true);
            }

            else if (tag == "see")
            {
                string        description = null;
                TokenIterator iterator    = startOfContent;

                // @see "Description"
                // @see <a href="link">Description</a>
                if (iterator.Character == '"' ||
                    iterator.Character == '<')
                {
                    // There's not symbol so interpret the whole thing as the description.  We'll let GetText() handle the HTML link.
                    description = GetText(iterator, endOfContent);
                    description = Normalize(description);
                }

                // @see Class.Class#Member
                // @see Class.Class#Member Description
                else
                {
                    string symbol = GetJavadocLinkSymbol(ref iterator);
                    iterator.NextPastWhitespace();

                    description = GetSimpleText(iterator, endOfContent);
                    description = Normalize(description);

                    if (description == null || description == "")
                    {
                        description = "<p><link type=\"naturaldocs\" originaltext=\"" + symbol.EntityEncode() + "\"></p>";
                    }
                    else
                    {
                        description = "<p><link type=\"naturaldocs\" originaltext=\"" + description.EntityEncode() + " at " + symbol.EntityEncode() + "\"></p>";
                    }
                }

                if (description == null || description == "")
                {
                    return(false);
                }

                var commentBlock = comment.GetListBlock(tag);
                commentBlock.Add(null, description);
                return(true);
            }

            // Ignored blocks
            // - serial
            // - serialField
            // - serialData
            else
            {
                return(true);
            }
        }
Пример #11
0
        // Group: Support Functions
        // __________________________________________________________________________


        /* Function: TryToGetPDBlockComment
         *
         * If the line iterator is on the starting symbol of a block comment, return it as a <PossibleDocumentationComment>
         * and mark the symbols as <CommentParsingType.CommentSymbol>.  If the iterator is not on the opening comment
         * symbol or there is content after the closing comment symbol making it unsuitable as a documentation comment,
         * returns null.
         *
         * If openingMustBeAlone is set, that means no symbol can appear immediately after the opening symbol for this
         * function to succeed.  This allows you to specifically detect something like /** without also matching /******.
         */
        protected PossibleDocumentationComment TryToGetPDBlockComment(LineIterator lineIterator,
                                                                      string openingSymbol, string closingSymbol,
                                                                      bool openingMustBeAlone)
        {
            TokenIterator firstToken = lineIterator.FirstToken(LineBoundsMode.ExcludeWhitespace);

            if (firstToken.MatchesAcrossTokens(openingSymbol) == false)
            {
                return(null);
            }

            if (openingMustBeAlone)
            {
                TokenIterator nextToken = firstToken;
                nextToken.NextByCharacters(openingSymbol.Length);
                if (nextToken.FundamentalType == FundamentalType.Symbol)
                {
                    return(null);
                }
            }

            PossibleDocumentationComment comment = new PossibleDocumentationComment();

            comment.Start = lineIterator;

            for (;;)
            {
                if (!lineIterator.IsInBounds)
                {
                    return(null);
                }

                TokenIterator closingSymbolIterator;

                if (lineIterator.FindAcrossTokens(closingSymbol, false, LineBoundsMode.Everything, out closingSymbolIterator) == true)
                {
                    closingSymbolIterator.NextByCharacters(closingSymbol.Length);

                    closingSymbolIterator.NextPastWhitespace();

                    if (closingSymbolIterator.FundamentalType != FundamentalType.LineBreak &&
                        closingSymbolIterator.FundamentalType != FundamentalType.Null)
                    {
                        return(null);
                    }

                    lineIterator.Next();
                    comment.End = lineIterator;
                    break;
                }

                lineIterator.Next();
            }

            // Success.  Mark the symbols before returning.
            firstToken.SetCommentParsingTypeByCharacters(CommentParsingType.CommentSymbol, openingSymbol.Length);

            TokenIterator lastToken;

            lineIterator.Previous();
            lineIterator.GetBounds(LineBoundsMode.ExcludeWhitespace, out firstToken, out lastToken);
            lastToken.PreviousByCharacters(closingSymbol.Length);
            lastToken.SetCommentParsingTypeByCharacters(CommentParsingType.CommentSymbol, closingSymbol.Length);

            return(comment);
        }
Пример #12
0
        // Function: GetPossibleDocumentationComments
        //
        // Goes through the file looking for comments that could possibly contain documentation and returns them as a list.  These
        // comments are not guaranteed to have documentation in them, just to be acceptable candidates for them.  If there are no
        // comments it will return an empty list.
        //
        // All the comments in the returned list will have their comment symbols marked as <CommentParsingType.CommentSymbol>
        // in the tokenizer.  This allows further operations to be done on them in a language independent manner.  If you want to also
        // filter out text boxes and lines, use <Comments.LineFinder>.
        //
        // Default Implementation:
        //
        // The default implementation uses the comment symbols found in <Language> or passed to the constructor.  You can override
        // this function if you need to do something more sophisticated, such as interpret the POD directives in Perl.
        //
        // Comments must be alone on a line to be a candidate for documentation, meaning that the comment symbol must be the
        // first non-whitespace character on a line, and in the case of block comments, nothing but whitespace may trail the closing
        // symbol.  The latter rule is important because a comment may start correctly but not end so, as in this prototype with Splint
        // annotation:
        //
        // > int get_array(integer_t id,
        // >               /*@out@*/ array_t array);
        //
        // Speaking of which, block comments surrounded by @ symbols are not included because they're Splint comments.  Not
        // including them in the possible documentation comments list means the Splint comment below won't end prototype detection.
        //
        // > void initialize ()
        // >    /*@globals undef globnum,
        // >               undef globname @*/
        // >    { ... }
        //
        // It also goes through the code line by line in a simple manner, not accounting for things like strings, so if a language contains
        // a multiline string whose content looks like a language comment it will be interpreted as one.  This isn't ideal but is accepted
        // as a conscious tradeoff because there are actually many different string formats (literal quotes denoted with \", literal quotes
        // denoted with "", Perl's q{} forms and here doc) so you can't account for them all in a generalized way.  Also, putting this in
        // an independent stage even when using full language support means comments don't disappear the way prototypes do if the
        // parser gets tripped up on something like an unmatched brace.
        //
        virtual public List <PossibleDocumentationComment> GetPossibleDocumentationComments(Tokenizer source)
        {
            List <PossibleDocumentationComment> possibleDocumentationComments = new List <PossibleDocumentationComment>();

            LineIterator lineIterator = source.FirstLine;

            while (lineIterator.IsInBounds)
            {
                PossibleDocumentationComment comment = null;


                // Javadoc block comments

                // We test for these before regular block comments because they are usually extended versions of them, such
                // as /** and /*.

                // We also test block comments in general ahead of line comments because in Lua the line comments are a
                // substring of them: -- versus --[[ and ]]--.

                if (javadocBlockCommentStringPairs != null)
                {
                    for (int i = 0; comment == null && i < javadocBlockCommentStringPairs.Length; i += 2)
                    {
                        comment = TryToGetPDBlockComment(lineIterator, javadocBlockCommentStringPairs[i],
                                                         javadocBlockCommentStringPairs[i + 1], true);
                    }

                    if (comment != null)
                    {
                        comment.Javadoc = true;
                    }
                }


                // Plain block comments

                if (comment == null && blockCommentStringPairs != null)
                {
                    for (int i = 0; comment == null && i < blockCommentStringPairs.Length; i += 2)
                    {
                        comment = TryToGetPDBlockComment(lineIterator, blockCommentStringPairs[i],
                                                         blockCommentStringPairs[i + 1], false);
                    }

                    // Skip Splint comments so that they can appear in prototypes.
                    if (comment != null && comment.Start.FirstToken(LineBoundsMode.CommentContent).Character == '@')
                    {
                        LineIterator lastLine = comment.End;
                        lastLine.Previous();

                        TokenIterator lastToken, ignore;
                        lastLine.GetBounds(LineBoundsMode.CommentContent, out ignore, out lastToken);
                        lastToken.Previous();

                        if (lastToken.Character == '@')
                        {
                            comment = null;
                        }
                    }
                }


                // XML line comments

                if (comment == null && xmlLineCommentStrings != null)
                {
                    for (int i = 0; comment == null && i < xmlLineCommentStrings.Length; i++)
                    {
                        comment = TryToGetPDLineComment(lineIterator, xmlLineCommentStrings[i],
                                                        xmlLineCommentStrings[i], true);
                    }

                    if (comment != null)
                    {
                        comment.XML = true;
                    }
                }


                // Javadoc line comments

                // We check for these even if a XML comment is found because they may share an opening symbol, such as ///.
                // We change it to Javadoc if it's longer.  If it's equal it's just interpreting the XML as a Javadoc start with a
                // vertical line for the remainder, so leave it as XML.  Unless the comment is only one line long, in which case it's
                // genuinely ambiguous.

                if ((comment == null || comment.XML == true) && javadocLineCommentStringPairs != null)
                {
                    PossibleDocumentationComment javadocComment = null;

                    for (int i = 0; javadocComment == null && i < javadocLineCommentStringPairs.Length; i += 2)
                    {
                        javadocComment = TryToGetPDLineComment(lineIterator, javadocLineCommentStringPairs[i],
                                                               javadocLineCommentStringPairs[i + 1], true);
                    }

                    if (javadocComment != null)
                    {
                        javadocComment.Javadoc = true;

                        if (comment == null)
                        {
                            comment = javadocComment;
                        }
                        else
                        {
                            int javadocLength = javadocComment.End.LineNumber - javadocComment.Start.LineNumber;
                            int xmlLength     = comment.End.LineNumber - comment.Start.LineNumber;

                            if (javadocLength > xmlLength)
                            {
                                comment = javadocComment;
                            }
                            else if (javadocLength == 1 && xmlLength == 1)
                            {
                                comment.Javadoc = true;
                            }
                            // else stay with the XML comment
                        }
                    }
                }


                // Plain line comments

                if (comment == null && lineCommentStrings != null)
                {
                    for (int i = 0; comment == null && i < lineCommentStrings.Length; i++)
                    {
                        comment = TryToGetPDLineComment(lineIterator, lineCommentStrings[i], lineCommentStrings[i], false);
                    }
                }


                // Nada.

                if (comment == null)
                {
                    lineIterator.Next();
                }
                else
                {
                    // XML can actually use the Javadoc comment format in addition to its own.
                    if (comment.Javadoc)
                    {
                        comment.XML = true;
                    }

                    possibleDocumentationComments.Add(comment);
                    lineIterator = comment.End;
                }
            }

            return(possibleDocumentationComments);
        }
Пример #13
0
        /* Function: TryToGetLineComment
         *
         * If the iterator is on a line that starts with a line comment symbol, this function moves the iterator past the entire
         * comment and returns true.  If the comment is a candidate for documentation it will also return it as a
         * <PossibleDocumentationComment> and mark the symbols as <CommentParsingType.CommentSymbol>.  If the
         * line does not start with a line comment symbol it will return false and leave the iterator where it is.
         */
        protected bool TryToGetLineComment(ref LineIterator lineIterator, out PossibleDocumentationComment comment)
        {
            TokenIterator firstToken = lineIterator.FirstToken(LineBoundsMode.ExcludeWhitespace);
            TokenIterator lookahead  = firstToken;


            // Are we on a line comment?

            if (TryToSkipLineCommentSymbol(ref lookahead) == false)
            {
                comment = null;
                return(false);
            }


            // We are.  Create a possible documentation comment.

            comment       = new PossibleDocumentationComment();
            comment.Start = lineIterator;


            // Check if we're a Javadoc/XML comment.  We can't tell the difference from just the first line.

            TokenIterator endOfCommentSymbol = lookahead;

            if (lookahead.Character == '-')
            {
                lookahead.Next();

                if (lookahead.FundamentalType != FundamentalType.Symbol)
                {
                    endOfCommentSymbol = lookahead;
                    comment.Javadoc    = true;
                    comment.XML        = true;
                }
            }


            // Mark it.

            firstToken.SetCommentParsingTypeBetween(endOfCommentSymbol, CommentParsingType.CommentSymbol);


            // Continue to find the rest of the comment

            lineIterator.Next();
            bool hasXMLishLines    = false;
            bool hasNonXMLishLines = false;
            bool hasMultipleLines  = false;

            while (lineIterator.IsInBounds)
            {
                firstToken = lineIterator.FirstToken(LineBoundsMode.ExcludeWhitespace);
                lookahead  = firstToken;

                if (TryToSkipLineCommentSymbol(ref lookahead) == false)
                {
                    break;
                }

                hasMultipleLines   = true;
                endOfCommentSymbol = lookahead;

                if (lookahead.Character == '-')
                {
                    lookahead.Next();

                    if (lookahead.FundamentalType != FundamentalType.Symbol)
                    {
                        hasXMLishLines     = true;
                        endOfCommentSymbol = lookahead;
                    }
                    else
                    {
                        hasNonXMLishLines = true;
                    }
                }
                else
                {
                    hasNonXMLishLines = true;
                }

                firstToken.SetCommentParsingTypeBetween(endOfCommentSymbol, CommentParsingType.CommentSymbol);
                lineIterator.Next();
            }

            comment.End = lineIterator;

            if (hasMultipleLines && comment.Javadoc)
            {
                if (hasXMLishLines && !hasNonXMLishLines)
                {
                    comment.Javadoc = false;
                }
                else if (hasNonXMLishLines)
                {
                    comment.XML = false;
                }
            }

            return(true);
        }