/* 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); }
/* Function: TryToGetFirstBlockLine * If the iterator is on a line that starts with one of the <BlockTags>, extracts the components and returns true. * Use <GetBlockTag()> to get the complete block since it may span multiple lines. */ protected bool TryToGetFirstBlockLine(LineIterator lineIterator, out string tag, out TokenIterator startOfContent) { tag = null; startOfContent = default(TokenIterator); TokenIterator tokenIterator = lineIterator.FirstToken(LineBoundsMode.CommentContent); if (tokenIterator.Character != '@') { return(false); } tokenIterator.Next(); if (tokenIterator.FundamentalType != FundamentalType.Text) { return(false); } string possibleTag = tokenIterator.String; if (BlockTags.Contains(possibleTag) == false) { return(false); } tokenIterator.Next(); tokenIterator.NextPastWhitespace(); tag = possibleTag; startOfContent = tokenIterator; return(true); }
/* 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); }
// 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); }
/* Function: GetText */ protected string GetText(LineIterator start, LineIterator end) { return(GetText(start.FirstToken(LineBoundsMode.Everything), end.FirstToken(LineBoundsMode.Everything))); }
/* 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); } }
// 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); }
/* 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); }