/* Function: ApplySubstitutions * Finds all substitutions in the source that match those in the table and replaces them with their values. Will also comment * out any substitution definitions found. */ protected Tokenizer ApplySubstitutions(Tokenizer source, StringToStringTable substitutions, bool applyNestedSubstitutions = true) { TokenIterator iterator = source.FirstToken; // Find the first valid substitution identifier or definition. If there aren't any we don't want to \do unnecessary memory // allocation and processing. bool foundSubstitution = false; string identifier = null; string localeIdentifier, value, declaration; while (iterator.IsInBounds) { if (TryToSkipSubstitutionIdentifier(ref iterator, out identifier) || TryToSkipLocaleSubstitutionIdentifier(ref iterator, out identifier, out localeIdentifier)) { foundSubstitution = true; break; } // else if (TryToSkipSubstitutionDefinition()) // { // Unnecessary because definitions will start with identifiers so it will get picked up by that // } else { GenericSkip(ref iterator); } } if (!foundSubstitution) { return(source); } // Now that we know we have one, we can back up the iterator and build new output iterator.PreviousByCharacters(identifier.Length); StringBuilder output = new StringBuilder(source.RawText.Length); output.Append(source.RawText, 0, iterator.RawTextIndex); while (iterator.IsInBounds) { TokenIterator previousIterator = iterator; if (TryToSkipSubstitutionDefinition(ref iterator, out identifier, out value, out declaration)) { if (this.blockCommentStringPairs != null) { output.Append(this.blockCommentStringPairs[0] + ' ' + declaration + ' ' + this.blockCommentStringPairs[1]); } } else if (TryToSkipSubstitutionIdentifier(ref iterator, out identifier)) { string substitution = substitutions[identifier]; if (substitution == null) { output.Append(identifier); } else { if (applyNestedSubstitutions) { substitution = ApplyNestedSubstitutions(substitution, substitutions); } output.Append(substitution); } } else if (TryToSkipLocaleSubstitutionIdentifier(ref iterator, out identifier, out localeIdentifier)) { string substitution = Engine.Locale.SafeGet("NaturalDocs.Engine", localeIdentifier, null); if (substitution == null) { output.Append(identifier); } else { output.Append('"' + substitution.StringEscape() + '"'); } } else { GenericSkip(ref iterator); source.AppendTextBetweenTo(previousIterator, iterator, output); } } return(new Tokenizer(output.ToString())); }
/* Function: TryToGetBlockComment * * If the iterator is on a line that starts with the opening symbol of a block comment, 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 an opening comment symbol it will return false and leave the iterator where it is. */ protected bool TryToGetBlockComment(ref LineIterator lineIterator, out PossibleDocumentationComment comment) { TokenIterator firstToken, endOfLine; lineIterator.GetBounds(LineBoundsMode.ExcludeWhitespace, out firstToken, out endOfLine); // Are we on a block comment? TokenIterator lookahead = firstToken; string closingSymbol; if (TryToSkipOpeningBlockCommentSymbol(ref lookahead, out closingSymbol) == false) { comment = null; return(false); } // We are. Create a possible documentation comment. comment = new PossibleDocumentationComment(); comment.Start = lineIterator; // Check if we're on a Javadoc comment, which will be an extra [. if (lookahead.Character == '[') { lookahead.Next(); if (lookahead.FundamentalType != FundamentalType.Symbol) { comment.Javadoc = true; } } // Find the end of the comment, which could be on the same line as the start. var tokenizer = lineIterator.Tokenizer; var lineLookahead = lineIterator; bool hadTrailingDashes = false; for (;;) { TokenIterator closingSymbolIterator; if (tokenizer.FindTokensBetween(closingSymbol, false, firstToken, endOfLine, out closingSymbolIterator) == true) { // Move past the end of the comment regardless of whether it's acceptable for documentation or not lineLookahead.Next(); // Make sure nothing appears after the closing symbol on the line closingSymbolIterator.NextByCharacters(closingSymbol.Length); // We'll allow -- though since some people use --[[ and ]]-- for balance even though the latter is actually the // closing comment symbol followed by a line comment. if (closingSymbolIterator.MatchesAcrossTokens("--")) { hadTrailingDashes = true; closingSymbolIterator.Next(2); } closingSymbolIterator.NextPastWhitespace(); if (closingSymbolIterator.FundamentalType != FundamentalType.LineBreak && closingSymbolIterator.FundamentalType != FundamentalType.Null) { comment = null; } else { comment.End = lineLookahead; } break; } lineLookahead.Next(); // If we're not in bounds that means there was an unclosed comment at the end of the file. Skip it but don't treat // it as a documentation candidate. if (!lookahead.IsInBounds) { comment = null; break; } lineLookahead.GetBounds(LineBoundsMode.ExcludeWhitespace, out firstToken, out endOfLine); } if (comment != null) { // Mark the symbols before returning firstToken = comment.Start.FirstToken(LineBoundsMode.ExcludeWhitespace); lookahead = firstToken; TryToSkipOpeningBlockCommentSymbol(ref lookahead, out closingSymbol); if (comment.Javadoc) { lookahead.Next(); } firstToken.SetCommentParsingTypeBetween(lookahead, CommentParsingType.CommentSymbol); LineIterator lastLine = comment.End; lastLine.Previous(); lastLine.GetBounds(LineBoundsMode.ExcludeWhitespace, out firstToken, out endOfLine); lookahead = endOfLine; if (hadTrailingDashes) { lookahead.Previous(2); } lookahead.PreviousByCharacters(closingSymbol.Length); lookahead.SetCommentParsingTypeBetween(endOfLine, CommentParsingType.CommentSymbol); } // If we made it this far that means we found a comment and can move the line iterator and return true. Whether // that comment was suitable for documentation will be determined by the comment variable, but we are moving the // iterator and returning true either way. lineIterator = lineLookahead; return(true); }