/* Function: GetColumn * Returns the bounds of a parameter's column and what type it is, which depends on <ColumnOrder>. You *must* call * <CalculateColumns()> beforehand. Returns false if the column index is out of bounds or the contents are empty for * that particular slot. */ public bool GetColumn(int columnIndex, out TokenIterator start, out TokenIterator end, out ColumnType type) { if (columnIndex >= columnIndexes.Length) { start = parsedPrototype.Tokenizer.LastToken; end = parsedPrototype.Tokenizer.LastToken; type = ColumnType.Name; return(false); } int startIndex = columnIndexes[columnIndex]; int endIndex = (columnIndex + 1 >= columnIndexes.Length ? endOfColumnsIndex : columnIndexes[columnIndex + 1]); start = parsedPrototype.Tokenizer.FirstToken; if (startIndex > 0) { start.Next(startIndex); } end = start; if (endIndex > startIndex) { end.Next(endIndex - startIndex); } type = ColumnOrder[columnIndex]; end.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, start); start.NextPastWhitespace(end); return(end > start); }
/* Function: MarkAnnotationParameter * * Applies types to an annotation parameter, such as ""String"" in "@Copynight("String")" or "id = 12" in * "@RequestForEnhancement(id = 12, engineer = "String")". * * Supported Modes: * * - <ParseMode.ParsePrototype> * - The contents will be marked with parameter tokens. * - Everything else has no effect. */ protected void MarkAnnotationParameter(TokenIterator start, TokenIterator end, ParseMode mode = ParseMode.IterateOnly) { if (mode != ParseMode.ParsePrototype) { return; } start.NextPastWhitespace(end); end.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, start); if (start >= end) { return; } // Find and mark the equals sign, if there is one TokenIterator equals = start; while (equals < end) { if (equals.Character == '=') { equals.PrototypeParsingType = PrototypeParsingType.PropertyValueSeparator; break; } else if (TryToSkipComment(ref equals) || TryToSkipString(ref equals) || TryToSkipBlock(ref equals, true)) { } else { equals.Next(); } } // The equals sign will be at or past the end if it doesn't exist. if (equals >= end) { start.SetPrototypeParsingTypeBetween(end, PrototypeParsingType.PropertyValue); } else { TokenIterator iterator = equals; iterator.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, start); if (start < iterator) { start.SetPrototypeParsingTypeBetween(iterator, PrototypeParsingType.Name); } iterator = equals; iterator.Next(); iterator.NextPastWhitespace(end); if (iterator < end) { iterator.SetPrototypeParsingTypeBetween(end, PrototypeParsingType.PropertyValue); } } }
/* Function: ParsePrototype * Converts a raw text prototype into a <ParsedPrototype>. */ override public ParsedPrototype ParsePrototype(string stringPrototype, int commentTypeID) { Tokenizer tokenizedPrototype = new Tokenizer(stringPrototype, tabWidth: EngineInstance.Config.TabWidth); ParsedPrototype parsedPrototype; // Mark any leading annotations. TokenIterator iterator = tokenizedPrototype.FirstToken; TryToSkipWhitespace(ref iterator, true, ParseMode.ParsePrototype); if (TryToSkipAnnotations(ref iterator, ParseMode.ParsePrototype)) { TryToSkipWhitespace(ref iterator, true, ParseMode.ParsePrototype); } // Search for the first opening bracket or brace. char closingBracket = '\0'; while (iterator.IsInBounds) { if (iterator.Character == '(') { closingBracket = ')'; break; } else if (iterator.Character == '[') { // Only treat brackets as parameters if it's following "this", meaning it's an iterator. Ignore all others so we // don't get tripped up on metadata or array brackets on return values. TokenIterator lookbehind = iterator; lookbehind.Previous(); lookbehind.PreviousPastWhitespace(PreviousPastWhitespaceMode.Iterator); if (lookbehind.MatchesToken("this")) { closingBracket = ']'; break; } else { iterator.Next(); } } else if (iterator.Character == '{') { closingBracket = '}'; break; } else if (TryToSkipComment(ref iterator) || TryToSkipString(ref iterator)) { } else { iterator.Next(); } } // If we found brackets, it's either a function prototype or a class prototype that includes members. // Mark the delimiters. if (closingBracket != '\0') { iterator.PrototypeParsingType = PrototypeParsingType.StartOfParams; iterator.Next(); while (iterator.IsInBounds) { if (iterator.Character == ',') { iterator.PrototypeParsingType = PrototypeParsingType.ParamSeparator; iterator.Next(); } else if (iterator.Character == closingBracket) { iterator.PrototypeParsingType = PrototypeParsingType.EndOfParams; break; } // Unlike prototype detection, here we treat < as an opening bracket. Since we're already in the parameter list // we shouldn't run into it as part of an operator overload, and we need it to not treat the comma in "template<a,b>" // as a parameter divider. else if (TryToSkipComment(ref iterator) || TryToSkipString(ref iterator) || TryToSkipBlock(ref iterator, true)) { } else { iterator.Next(); } } // We have enough tokens marked to create the parsed prototype. This will also let us iterate through the parameters // easily. parsedPrototype = new ParsedPrototype(tokenizedPrototype); // Set the main section to the last one, since any annotations present will each be in their own section. Some can have // parameter lists and we don't want those confused for the actual parameter list. parsedPrototype.MainSectionIndex = parsedPrototype.Sections.Count - 1; // Mark the part before the parameters, which includes the name and return value. TokenIterator start, end; parsedPrototype.GetBeforeParameters(out start, out end); // Exclude the opening bracket end.Previous(); end.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, start); if (start < end) { MarkCParameter(start, end); } // If there are any parameters, mark the tokens in them. if (parsedPrototype.NumberOfParameters > 0) { for (int i = 0; i < parsedPrototype.NumberOfParameters; i++) { parsedPrototype.GetParameter(i, out start, out end); MarkCParameter(start, end); } } } // If there's no brackets, it's a variable, property, or class. else { parsedPrototype = new ParsedPrototype(tokenizedPrototype); TokenIterator start = tokenizedPrototype.FirstToken; TokenIterator end = tokenizedPrototype.LastToken; MarkCParameter(start, end); } return(parsedPrototype); }
/* Function: BuildCurrentClass */ protected void BuildCurrentClass() { htmlOutput.Append("<div class=\"CPEntry T" + EngineInstance.CommentTypes.FromID(topic.CommentTypeID).SimpleIdentifier + " Current\">"); // Pre-prototype lines int lineCount = parsedPrototype.NumberOfPrePrototypeLines; TokenIterator start, end; for (int i = 0; i < lineCount; i++) { parsedPrototype.GetPrePrototypeLine(i, out start, out end); htmlOutput.Append("<div class=\"CPPrePrototypeLine\">"); BuildSyntaxHighlightedText(start, end); htmlOutput.Append("</div>"); } // Keyword and modifiers. We only show the keyword if it's not "class". TokenIterator startKeyword, endKeyword; topic.ParsedClassPrototype.GetKeyword(out startKeyword, out endKeyword); string keyword = startKeyword.String; TokenIterator startModifiers, endModifiers; bool hasModifiers = topic.ParsedClassPrototype.GetModifiers(out startModifiers, out endModifiers); if (hasModifiers || keyword != "class") { StringBuilder modifiersOutput = new StringBuilder(); TokenIterator partial; bool hasPartial = startModifiers.Tokenizer.FindTokenBetween("partial", EngineInstance.Languages.FromID(topic.LanguageID).CaseSensitive, startModifiers, endModifiers, out partial); if (hasPartial) { TokenIterator lookahead = partial; lookahead.Next(); if (lookahead < endModifiers && (lookahead.FundamentalType == FundamentalType.Text || lookahead.Character == '_')) { hasPartial = false; } TokenIterator lookbehind = partial; lookbehind.Previous(); if (lookbehind >= startModifiers && (lookbehind.FundamentalType == FundamentalType.Text || lookbehind.Character == '_')) { hasPartial = false; } } if (hasModifiers && hasPartial) { if (partial > startModifiers) { TokenIterator lookbehind = partial; lookbehind.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds); BuildSyntaxHighlightedText(startModifiers, lookbehind, modifiersOutput); } partial.Next(); partial.NextPastWhitespace(); if (partial < endModifiers) { if (modifiersOutput.Length > 0) { modifiersOutput.Append(' '); } BuildSyntaxHighlightedText(partial, endModifiers, modifiersOutput); } } else if (hasModifiers) { BuildSyntaxHighlightedText(startModifiers, endModifiers, modifiersOutput); } if (keyword != "class") { if (modifiersOutput.Length > 0) { modifiersOutput.Append(' '); } BuildSyntaxHighlightedText(startKeyword, endKeyword, modifiersOutput); } if (modifiersOutput.Length > 0) { htmlOutput.Append("<div class=\"CPModifiers\">"); htmlOutput.Append(modifiersOutput.ToString()); htmlOutput.Append("</div>"); } } // Name. We use the fully resolved name in the symbol instead of the prototype name, which may just be the last segment. htmlOutput.Append("<div class=\"CPName\">"); BuildWrappedTitle(topic.Symbol.FormatWithSeparator(this.language.MemberOperator), topic.CommentTypeID, htmlOutput); TokenIterator startTemplate, endTemplate; if (topic.ParsedClassPrototype.GetTemplateSuffix(out startTemplate, out endTemplate)) { // Include a zero-width space for wrapping htmlOutput.Append("​<span class=\"TemplateSignature\">"); htmlOutput.EntityEncodeAndAppend(startTemplate.Tokenizer.TextBetween(startTemplate, endTemplate)); htmlOutput.Append("</span>"); } htmlOutput.Append("</div>"); // Post-prototype lines lineCount = parsedPrototype.NumberOfPostPrototypeLines; for (int i = 0; i < lineCount; i++) { parsedPrototype.GetPostPrototypeLine(i, out start, out end); htmlOutput.Append("<div class=\"CPPostPrototypeLine\">"); BuildSyntaxHighlightedText(start, end); htmlOutput.Append("</div>"); } htmlOutput.Append("</div>"); }
/* Function: CalculateSections */ protected void CalculateSections() { sections = new List <Section>(); Section section = null; TokenIterator iterator = tokenizer.FirstToken; iterator.NextPastWhitespace(); // Pre-Prototype Lines while (iterator.IsInBounds && iterator.ClassPrototypeParsingType == ClassPrototypeParsingType.StartOfPrePrototypeLine) { section = new Section(); section.Type = SectionType.PrePrototypeLine; section.StartIndex = iterator.TokenIndex; do { iterator.Next(); }while (iterator.IsInBounds && iterator.ClassPrototypeParsingType == ClassPrototypeParsingType.PrePrototypeLine); section.EndIndex = iterator.TokenIndex; sections.Add(section); iterator.NextPastWhitespace(); } // Before Parents TokenIterator startOfSection = iterator; section = new Section(); section.Type = SectionType.BeforeParents; section.StartIndex = startOfSection.TokenIndex; while (iterator.IsInBounds && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.StartOfParents && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.StartOfPostPrototypeLine && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.StartOfBody) { iterator.Next(); } TokenIterator lookbehind = iterator; lookbehind.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, startOfSection); section.EndIndex = lookbehind.TokenIndex; sections.Add(section); // Parents if (iterator.ClassPrototypeParsingType == ClassPrototypeParsingType.StartOfParents) { do { iterator.Next(); }while (iterator.ClassPrototypeParsingType == ClassPrototypeParsingType.StartOfParents); iterator.NextPastWhitespace(); while (iterator.IsInBounds && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.EndOfParents && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.StartOfPostPrototypeLine && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.StartOfBody) { startOfSection = iterator; section = new Section(); section.Type = SectionType.Parent; section.StartIndex = startOfSection.TokenIndex; while (iterator.IsInBounds && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.ParentSeparator && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.EndOfParents && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.StartOfPostPrototypeLine && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.StartOfBody) { iterator.Next(); } lookbehind = iterator; lookbehind.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, startOfSection); section.EndIndex = lookbehind.TokenIndex; sections.Add(section); if (iterator.ClassPrototypeParsingType == ClassPrototypeParsingType.ParentSeparator) { do { iterator.Next(); }while (iterator.ClassPrototypeParsingType == ClassPrototypeParsingType.ParentSeparator); iterator.NextPastWhitespace(); } } } // After Parents if (iterator.IsInBounds && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.StartOfPostPrototypeLine && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.StartOfBody) { startOfSection = iterator; section = new Section(); section.Type = SectionType.AfterParents; section.StartIndex = iterator.TokenIndex; do { iterator.Next(); }while (iterator.IsInBounds && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.StartOfPostPrototypeLine && iterator.ClassPrototypeParsingType != ClassPrototypeParsingType.StartOfBody); lookbehind = iterator; lookbehind.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, startOfSection); section.EndIndex = lookbehind.TokenIndex; sections.Add(section); } // Post-Prototype Lines while (iterator.IsInBounds && iterator.ClassPrototypeParsingType == ClassPrototypeParsingType.StartOfPostPrototypeLine) { section = new Section(); section.Type = SectionType.PostPrototypeLine; section.StartIndex = iterator.TokenIndex; do { iterator.Next(); }while (iterator.IsInBounds && iterator.ClassPrototypeParsingType == ClassPrototypeParsingType.PostPrototypeLine); section.EndIndex = iterator.TokenIndex; sections.Add(section); iterator.NextPastWhitespace(); } }
/* Function: AppendCellContents */ protected void AppendCellContents(int parameterIndex, int cellIndex, StringBuilder output) { TokenIterator start = parameterTableSection.Start; start.Next(parameterTableTokenIndexes[parameterIndex, cellIndex] - start.TokenIndex); TokenIterator end = start; end.Next(parameterTableTokenIndexes[parameterIndex, cellIndex + 1] - end.TokenIndex); bool hadTrailingWhitespace = end.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, start); ColumnType type = ColumnOrder[cellIndex]; // Find the type of the next used cell ColumnType?nextType = null; for (int nextCellIndex = cellIndex + 1; nextCellIndex < NumberOfColumns; nextCellIndex++) { if (parameterTableColumnsUsed[nextCellIndex]) { nextType = ColumnOrder[nextCellIndex]; break; } } // Default value separators always get spaces before. // Property value separators get them unless they're ":", but watch out for ":=". // Type-name separators get them if they're text (SQL's "AS") instead of symbols (Pascal's ":"). if (type == ColumnType.DefaultValueSeparator || (type == ColumnType.PropertyValueSeparator && (start.Character != ':' || start.MatchesAcrossTokens(":="))) || (type == ColumnType.TypeNameSeparator && start.FundamentalType == FundamentalType.Text)) { output.Append(" "); } // We don't want to highlight keywords on the Name cell because identifiers can accidentally be marked as them with // basic language support, such as "event" in "wxPaintEvent &event". if (type == ColumnType.Name) { AppendSyntaxHighlightedText(start, end, output, excludeKeywords: true); } else if (addLinks) { AppendSyntaxHighlightedTextWithTypeLinks(start, end, output, links, linkTargets, extendTypeSearch: true); } else { AppendSyntaxHighlightedText(start, end, output); } // Default value separators, property value separators, and type/name separators always get spaces after. Make sure // the spaces aren't duplicated by the preceding cells. if (type == ColumnType.DefaultValueSeparator || type == ColumnType.PropertyValueSeparator || type == ColumnType.TypeNameSeparator || (hadTrailingWhitespace && type != ColumnType.DefaultValue && nextType != ColumnType.DefaultValueSeparator && nextType != ColumnType.PropertyValueSeparator && nextType != ColumnType.TypeNameSeparator)) { output.Append(" "); } }
// Group: Parsing Functions // __________________________________________________________________________ /* Function: TryToSkipPODLine * If the iterator is on a POD line such as "=pod", moves the iterator past it and returns true and the type. If the iterator * isn't on a POD line it will return false and leave the iterator alone. */ protected bool TryToSkipPODLine(ref TokenIterator iterator, out PODLineType type) { type = 0; if (iterator.Character != '=') { return(false); } TokenIterator lookbehind = iterator; for (;;) { lookbehind.Previous(); if (lookbehind.IsInBounds == false || lookbehind.FundamentalType == FundamentalType.LineBreak) { break; } else if (lookbehind.FundamentalType != FundamentalType.Whitespace) { return(false); } } TokenIterator endOfLine = iterator; endOfLine.Next(); if (endOfLine.FundamentalType != FundamentalType.Text) { return(false); } do { endOfLine.Next(); }while (endOfLine.IsInBounds && endOfLine.FundamentalType != FundamentalType.LineBreak); TokenIterator endOfContent = endOfLine; endOfContent.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds); // ND and Javadoc lines may also match PODBeginLineRegex so test them first. if (iterator.Tokenizer.MatchTextBetween(PODBeginNDLineRegex, iterator, endOfContent).Success) { type = PODLineType.StartNaturalDocs; } else if (iterator.Tokenizer.MatchTextBetween(PODBeginJavadocLineRegex, iterator, endOfContent).Success) { type = PODLineType.StartJavadoc; } else if (iterator.Tokenizer.MatchTextBetween(PODBeginLineRegex, iterator, endOfContent).Success) { type = PODLineType.StartPOD; } else if (iterator.Tokenizer.MatchTextBetween(PODEndLineRegex, iterator, endOfContent).Success) { type = PODLineType.End; } else { return(false); } iterator = endOfLine; if (iterator.FundamentalType == FundamentalType.LineBreak) { iterator.Next(); } return(true); }
/* Function: BuildCellContents */ protected void BuildCellContents(int parameterIndex, int cellIndex) { TokenIterator start = parameterTableSection.Start; start.Next(parameterTableTokenIndexes[parameterIndex, cellIndex] - start.TokenIndex); TokenIterator end = start; end.Next(parameterTableTokenIndexes[parameterIndex, cellIndex + 1] - end.TokenIndex); bool hadTrailingWhitespace = end.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, start); ColumnType type = ColumnOrder[cellIndex]; // Find the type of the next used cell ColumnType?nextType = null; for (int nextCellIndex = cellIndex + 1; nextCellIndex < NumberOfColumns; nextCellIndex++) { if (parameterTableColumnsUsed[nextCellIndex]) { nextType = ColumnOrder[nextCellIndex]; break; } } // Default value separators always get spaces before. Type-name separators get them if they're text (SQL's "AS") instead // of symbols (Pascal's ":"). if (type == ColumnType.DefaultValueSeparator || (type == ColumnType.TypeNameSeparator && start.FundamentalType == FundamentalType.Text)) { htmlOutput.Append(" "); } // We don't want syntax highlighting on the Name cell because identifiers can accidentally be marked as keywords with // simple highlighting and basic language support, such as "event" in "wxPaintEvent &event". if (type == ColumnType.Name) { htmlOutput.EntityEncodeAndAppend(start.TextBetween(end)); } else if (addLinks) { BuildTypeLinkedAndSyntaxHighlightedText(start, end, true, htmlOutput); } else { BuildSyntaxHighlightedText(start, end, htmlOutput); } // Default value separators and type/name separators always get spaces after. Make sure the spaces aren't duplicated // by the preceding cells. if (type == ColumnType.DefaultValueSeparator || type == ColumnType.TypeNameSeparator || (hadTrailingWhitespace && type != ColumnType.DefaultValue && nextType != ColumnType.DefaultValueSeparator && nextType != ColumnType.TypeNameSeparator)) { htmlOutput.Append(" "); } }
/* Function: AppendCurrentClassPrototype */ protected void AppendCurrentClassPrototype(StringBuilder output) { // Main tag string simpleTypeIdentifier = EngineInstance.CommentTypes.FromID(context.Topic.CommentTypeID).SimpleIdentifier; output.Append("<div class=\"CPEntry T" + simpleTypeIdentifier + " Current\">"); // Pre-prototype lines int lineCount = parsedClassPrototype.NumberOfPrePrototypeLines; TokenIterator start, end; for (int i = 0; i < lineCount; i++) { parsedClassPrototype.GetPrePrototypeLine(i, out start, out end); output.Append("<div class=\"CPPrePrototypeLine\">"); AppendSyntaxHighlightedText(start, end, output); output.Append("</div>"); } // Keyword and modifiers. We only show the keyword if it's not "class", and we exclude "partial" from the list of modifiers. TokenIterator startKeyword, endKeyword; parsedClassPrototype.GetKeyword(out startKeyword, out endKeyword); string keyword = startKeyword.String; TokenIterator startModifiers, endModifiers; bool hasModifiers = parsedClassPrototype.GetModifiers(out startModifiers, out endModifiers); if (hasModifiers || keyword != "class") { StringBuilder modifiersOutput = new StringBuilder(); TokenIterator partial; bool hasPartial = startModifiers.Tokenizer.FindTokenBetween("partial", language.CaseSensitive, startModifiers, endModifiers, out partial); // Make sure "partial" is a keyword and not part of a longer identifier if (hasPartial) { TokenIterator lookahead = partial; lookahead.Next(); if (lookahead < endModifiers && (lookahead.FundamentalType == FundamentalType.Text || lookahead.Character == '_')) { hasPartial = false; } TokenIterator lookbehind = partial; lookbehind.Previous(); if (lookbehind >= startModifiers && (lookbehind.FundamentalType == FundamentalType.Text || lookbehind.Character == '_')) { hasPartial = false; } } // Add the modifiers sans-"partial" if (hasModifiers && hasPartial) { if (partial > startModifiers) { TokenIterator lookbehind = partial; lookbehind.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds); AppendSyntaxHighlightedText(startModifiers, lookbehind, modifiersOutput); } partial.Next(); partial.NextPastWhitespace(); if (partial < endModifiers) { if (modifiersOutput.Length > 0) { modifiersOutput.Append(' '); } AppendSyntaxHighlightedText(partial, endModifiers, modifiersOutput); } } else if (hasModifiers) { AppendSyntaxHighlightedText(startModifiers, endModifiers, modifiersOutput); } // Add the keyword if it isn't "class" if (keyword != "class") { if (modifiersOutput.Length > 0) { modifiersOutput.Append(' '); } AppendSyntaxHighlightedText(startKeyword, endKeyword, modifiersOutput); } if (modifiersOutput.Length > 0) { output.Append("<div class=\"CPModifiers\">"); output.Append(modifiersOutput.ToString()); output.Append("</div>"); } } // Name. We use the fully resolved name in the symbol instead of the prototype name, which may just be the last segment. output.Append("<div class=\"CPName\">"); AppendWrappedTitle(context.Topic.Symbol.FormatWithSeparator(language.MemberOperator), WrappedTitleMode.Code, output); TokenIterator startTemplate, endTemplate; if (parsedClassPrototype.GetTemplateSuffix(out startTemplate, out endTemplate)) { // Include preceding whitespace if present, or a zero-width space for wrapping if not if (!startTemplate.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds)) { output.Append("​"); } output.Append("<span class=\"TemplateSignature\">"); AppendSyntaxHighlightedText(startTemplate, endTemplate, output); output.Append("</span>"); } output.Append("</div>"); // Post-prototype lines lineCount = parsedClassPrototype.NumberOfPostPrototypeLines; for (int i = 0; i < lineCount; i++) { parsedClassPrototype.GetPostPrototypeLine(i, out start, out end); output.Append("<div class=\"CPPostPrototypeLine\">"); AppendSyntaxHighlightedText(start, end, output); output.Append("</div>"); } output.Append("</div>"); }
/* Function: RecalculateSections * * Recalculates the <Sections> list. If you've set <MainSectionIndex> manually, it will have to be set again after calling this * function. * * Sections are delimited with <PrototypeParsingType.StartOfPrototypeSection> and <PrototypeParsingType.EndOfPrototypeSection>. * Neither of these token types are required to appear, and if they do not the entire prototype will be in one section. Also, they are * not required to appear together. Sections can be delimited by only start tokens or only end tokens, whichever is most convenient * to the language parser and won't interfere with marking other types. * * Each section containing <PrototypeParsingType.StartOfParams> will generate a <ParameterSection>. All others will generate a * regular <Section>. */ public void RecalculateSections() { if (sections == null) { sections = new List <Section>(1); } else { sections.Clear(); } TokenIterator startOfSection = tokenizer.FirstToken; TokenIterator endOfSection = startOfSection; int firstSectionWithName = -1; int firstSectionWithParams = -1; for (;;) { // Find the section bounds bool sectionIsEmpty = true; bool sectionHasName = false; bool sectionHasParams = false; while (endOfSection.IsInBounds) { // Break if we hit the beginning of the next section, but not if it's the start of the current section if (endOfSection.PrototypeParsingType == PrototypeParsingType.StartOfPrototypeSection && endOfSection > startOfSection) { break; } if (endOfSection.FundamentalType != FundamentalType.Whitespace) { sectionIsEmpty = false; } if (endOfSection.PrototypeParsingType == PrototypeParsingType.Name) { sectionHasName = true; } else if (endOfSection.PrototypeParsingType == PrototypeParsingType.StartOfParams) { sectionHasParams = true; } else if (endOfSection.PrototypeParsingType == PrototypeParsingType.EndOfPrototypeSection) { endOfSection.Next(); break; } endOfSection.Next(); } // Process the section if (!sectionIsEmpty) { endOfSection.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, startOfSection); startOfSection.NextPastWhitespace(endOfSection); if (sectionHasParams) { sections.Add(new ParameterSection(startOfSection, endOfSection, supportsImpliedTypes)); } else { sections.Add(new Section(startOfSection, endOfSection)); } if (sectionHasName && firstSectionWithName == -1) { firstSectionWithName = sections.Count - 1; } if (sectionHasParams && firstSectionWithParams == -1) { firstSectionWithParams = sections.Count - 1; } } // Continue? if (endOfSection.IsInBounds) { startOfSection = endOfSection; } else { break; } } // Sanity check. This should only happen if all the sections were whitespace, which shouldn't normally happen but I // suppose could with a manual prototype. if (sections.Count < 1) { sections.Add(new Section(tokenizer.FirstToken, tokenizer.LastToken)); } // Determine main section if (sections.Count == 1) { mainSectionIndex = 0; } else if (firstSectionWithName != -1) { mainSectionIndex = firstSectionWithName; } else if (firstSectionWithParams != -1) { mainSectionIndex = firstSectionWithParams; } else { mainSectionIndex = 0; } }
/* Function: CalculateSections */ protected void CalculateSections() { sections = new List <Section>(); Section section = null; TokenIterator iterator = tokenizer.FirstToken; iterator.NextPastWhitespace(); // Pre-Prototype Lines while (iterator.IsInBounds && iterator.PrototypeParsingType == PrototypeParsingType.StartOfPrePrototypeLine) { section = new Section(); section.Type = SectionType.PrePrototypeLine; section.StartIndex = iterator.TokenIndex; do { iterator.Next(); }while (iterator.IsInBounds && iterator.PrototypeParsingType == PrototypeParsingType.PrePrototypeLine); section.EndIndex = iterator.TokenIndex; sections.Add(section); iterator.NextPastWhitespace(); } // Before Parameters section = new Section(); section.Type = SectionType.BeforeParameters; section.StartIndex = iterator.TokenIndex; while (iterator.IsInBounds && iterator.PrototypeParsingType != PrototypeParsingType.StartOfParams && iterator.PrototypeParsingType != PrototypeParsingType.StartOfPostPrototypeLine) { iterator.Next(); } if (iterator.PrototypeParsingType == PrototypeParsingType.StartOfParams) { // Include the StartOfParams symbol in the section iterator.Next(); section.EndIndex = iterator.TokenIndex; sections.Add(section); iterator.NextPastWhitespace(); // Parameters while (iterator.IsInBounds && iterator.PrototypeParsingType != PrototypeParsingType.EndOfParams && iterator.PrototypeParsingType != PrototypeParsingType.StartOfPostPrototypeLine) { section = new Section(); section.Type = SectionType.Parameter; section.StartIndex = iterator.TokenIndex; while (iterator.IsInBounds && iterator.PrototypeParsingType != PrototypeParsingType.EndOfParams && iterator.PrototypeParsingType != PrototypeParsingType.ParamSeparator && iterator.PrototypeParsingType != PrototypeParsingType.StartOfPostPrototypeLine) { iterator.Next(); } // Include the separator in the parameter block if (iterator.PrototypeParsingType == PrototypeParsingType.ParamSeparator) { iterator.Next(); section.EndIndex = iterator.TokenIndex; } else { TokenIterator lookbehind = iterator; lookbehind.PreviousPastWhitespace(PreviousPastWhitespaceMode.Iterator, iterator); section.EndIndex = lookbehind.TokenIndex; } sections.Add(section); iterator.NextPastWhitespace(); } // After Parameters if (iterator.IsInBounds && iterator.PrototypeParsingType != PrototypeParsingType.StartOfPostPrototypeLine) { section = new Section(); section.Type = SectionType.AfterParameters; section.StartIndex = iterator.TokenIndex; do { iterator.Next(); }while (iterator.IsInBounds && iterator.PrototypeParsingType != PrototypeParsingType.StartOfPostPrototypeLine); TokenIterator lookbehind = iterator; lookbehind.PreviousPastWhitespace(PreviousPastWhitespaceMode.Iterator, iterator); section.EndIndex = lookbehind.TokenIndex; sections.Add(section); } } else // there was no StartOfParams { // We still have to finish the BeforeParameters section. TokenIterator lookbehind = iterator; lookbehind.PreviousPastWhitespace(PreviousPastWhitespaceMode.Iterator, iterator); section.EndIndex = lookbehind.TokenIndex; sections.Add(section); } // Post-Prototype Lines while (iterator.IsInBounds && iterator.PrototypeParsingType == PrototypeParsingType.StartOfPostPrototypeLine) { section = new Section(); section.Type = SectionType.PostPrototypeLine; section.StartIndex = iterator.TokenIndex; do { iterator.Next(); }while (iterator.IsInBounds && iterator.PrototypeParsingType == PrototypeParsingType.PostPrototypeLine); section.EndIndex = iterator.TokenIndex; sections.Add(section); iterator.NextPastWhitespace(); } }
/* Function: GetFullParameterType * * Returns the bounds of the type of the passed parameter, or false if it couldn't find it. This includes modifiers and type * suffixes. Since the type token may not be continuous, it returns separate start and end iterators for extra type modifiers * ("const" in "const x: integer") prefixes ("*" in "int *x") and suffixes ("[12]" in "int x[12]"). Regular modifiers attached to * the type ("unsigned" in "unsigned int x") will be accounted for by the main iterators. * * If the implied types flag is set this will return "int" for y in "int x, y". If it is not then it will return false for y. */ public bool GetFullParameterType(int index, out TokenIterator start, out TokenIterator end, out TokenIterator extraModifierStart, out TokenIterator extraModifierEnd, out TokenIterator prefixStart, out TokenIterator prefixEnd, out TokenIterator suffixStart, out TokenIterator suffixEnd, bool impliedTypes = true) { TokenIterator paramStart, paramEnd; if (!GetParameter(index, out paramStart, out paramEnd)) { start = paramEnd; end = paramEnd; extraModifierStart = paramEnd; extraModifierEnd = paramEnd; prefixStart = paramEnd; prefixEnd = paramEnd; suffixStart = paramEnd; suffixEnd = paramEnd; return(false); } // Find the beginning of the type by finding the first type token start = paramStart; PrototypeParsingType type = start.PrototypeParsingType; while (start < paramEnd && type != PrototypeParsingType.Type && type != PrototypeParsingType.TypeModifier && type != PrototypeParsingType.TypeQualifier && type != PrototypeParsingType.NameModifier_PartOfType) { start.Next(); type = start.PrototypeParsingType; } // If we're on a name modifier, get it and keep looking for the type if (type == PrototypeParsingType.NameModifier_PartOfType) { extraModifierStart = start; do { start.Next(); type = start.PrototypeParsingType; }while (start < paramEnd && type == PrototypeParsingType.NameModifier_PartOfType); extraModifierEnd = start; // Search for the start again after the extra modifier while (start < paramEnd && type != PrototypeParsingType.Type && type != PrototypeParsingType.TypeModifier && type != PrototypeParsingType.TypeQualifier) { start.Next(); type = start.PrototypeParsingType; } } else { extraModifierStart = paramEnd; extraModifierEnd = paramEnd; } // If we found one, find the end of the type bool foundType = (start < paramEnd); end = start; if (foundType) { int nestLevel = 0; do { if (end.PrototypeParsingType == PrototypeParsingType.OpeningTypeSuffix) { nestLevel++; } else if (end.PrototypeParsingType == PrototypeParsingType.ClosingTypeSuffix) { nestLevel--; } end.Next(); type = end.PrototypeParsingType; }while ((nestLevel > 0 || type == PrototypeParsingType.Type || type == PrototypeParsingType.TypeModifier || type == PrototypeParsingType.TypeQualifier || type == PrototypeParsingType.OpeningTypeSuffix || type == PrototypeParsingType.ClosingTypeSuffix || type == PrototypeParsingType.TypeSuffix || end.FundamentalType == FundamentalType.Whitespace) && end < paramEnd); end.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, start); } // See if we can fill it out with implied types if (impliedTypes) { if (Style == ParameterStyle.C) { // If there's no type for a C parameter, move backwards for "int x, y" if (!foundType) { TokenIterator ignoredA, ignoredB; for (int i = index - 1; i >= 0; i--) { if (GetFullParameterType(i, out start, out end, out ignoredA, out ignoredB, out prefixStart, out prefixEnd, out suffixStart, out suffixEnd, false)) { foundType = true; break; } } } } else // Style == ParameterStyle.Pascal { // If there's no type for a Pascal parameter, move forwards for "x, y: integer" if (!foundType) { TokenIterator ignoredA, ignoredB; for (int i = index + 1; i <= NumberOfParameters; i++) { if (GetFullParameterType(i, out start, out end, out ignoredA, out ignoredB, out prefixStart, out prefixEnd, out suffixStart, out suffixEnd, false)) { foundType = true; break; } } } // If there's no extra modifiers for a Pascal parameter, move backwards for "const x, y: integer" if (extraModifierEnd <= extraModifierStart) { TokenIterator prevParamIterator, prevParamEnd, prevExtraModifierStart, prevExtraModifierEnd; for (int i = index - 1; i >= 0; i--) { // We can't use GetFullParameterType() here because it won't return anything if it doesn't find a type with // implied types off, and turning implied types on means we can't detect when another type occurs which // would mean we wouldn't want to inherit its modifiers ("const x: integer; y: integer"). So we do it manually // with GetParameter(). GetParameter(i, out prevParamIterator, out prevParamEnd); prevExtraModifierStart = prevParamEnd; prevExtraModifierEnd = prevParamEnd; bool foundPrevExtraModifier = false; bool foundPrevType = false; while (prevParamIterator < prevParamEnd) { if (prevParamIterator.PrototypeParsingType == PrototypeParsingType.NameModifier_PartOfType) { if (!foundPrevExtraModifier) { prevExtraModifierStart = prevParamIterator; foundPrevExtraModifier = true; } prevParamIterator.Next(); prevExtraModifierEnd = prevParamIterator; } else if (prevParamIterator.PrototypeParsingType == PrototypeParsingType.Type) { foundPrevType = true; break; } else { prevParamIterator.Next(); } } if (foundPrevType) { break; } else if (foundPrevExtraModifier) { extraModifierStart = prevExtraModifierStart; extraModifierEnd = prevExtraModifierEnd; break; } } } } } // If we found a type, explicit or implied, find the prefix and suffix. We do this after checking for implied types because we // want "int a[12], b" to work. b should be int, not int[12]. In "int *x, y", y should be int, not int*. if (foundType) { prefixStart = paramStart; while (prefixStart < paramEnd && prefixStart.PrototypeParsingType != PrototypeParsingType.NamePrefix_PartOfType) { prefixStart.Next(); } prefixEnd = prefixStart; while (prefixEnd < paramEnd && prefixEnd.PrototypeParsingType == PrototypeParsingType.NamePrefix_PartOfType) { prefixEnd.Next(); } suffixStart = paramStart; while (suffixStart < paramEnd && suffixStart.PrototypeParsingType != PrototypeParsingType.NameSuffix_PartOfType) { suffixStart.Next(); } suffixEnd = suffixStart; while (suffixEnd < paramEnd && suffixEnd.PrototypeParsingType == PrototypeParsingType.NameSuffix_PartOfType) { suffixEnd.Next(); } return(true); } else // didn't find a type { start = paramEnd; end = paramEnd; extraModifierStart = paramEnd; extraModifierEnd = paramEnd; prefixStart = paramEnd; prefixEnd = paramEnd; suffixStart = paramEnd; suffixEnd = paramEnd; return(false); } }
/* Function: GetFullParameterType * * Returns the bounds of the type of the passed parameter, or false if it couldn't find it. This includes modifiers and type * suffixes. Since the type token may not be continuous, it returns separate start and end iterators for type prefixes * (* in "int *x") and suffixes ("[12]" in "int x[12]"). * * If the implied types flag is set this will return "int" for y in "int x, y". If it is not then it will return false for y. */ public bool GetFullParameterType(int index, out TokenIterator start, out TokenIterator end, out TokenIterator prefixStart, out TokenIterator prefixEnd, out TokenIterator suffixStart, out TokenIterator suffixEnd, bool impliedTypes = true) { TokenIterator paramStart, paramEnd; if (!GetParameter(index, out paramStart, out paramEnd)) { start = paramEnd; end = paramEnd; prefixStart = paramEnd; prefixEnd = paramEnd; suffixStart = paramEnd; suffixEnd = paramEnd; return(false); } // Find the beginning of the type by finding the first type token start = paramStart; PrototypeParsingType type = start.PrototypeParsingType; while (start < paramEnd && type != PrototypeParsingType.Type && type != PrototypeParsingType.TypeModifier && type != PrototypeParsingType.TypeQualifier) { start.Next(); type = start.PrototypeParsingType; } // If we found one, find the end of the type bool foundType = (start < paramEnd); end = start; if (foundType) { int nestLevel = 0; do { if (end.PrototypeParsingType == PrototypeParsingType.OpeningTypeSuffix) { nestLevel++; } else if (end.PrototypeParsingType == PrototypeParsingType.ClosingTypeSuffix) { nestLevel--; } end.Next(); type = end.PrototypeParsingType; }while ((nestLevel > 0 || type == PrototypeParsingType.Type || type == PrototypeParsingType.TypeModifier || type == PrototypeParsingType.TypeQualifier || type == PrototypeParsingType.OpeningTypeSuffix || type == PrototypeParsingType.ClosingTypeSuffix || type == PrototypeParsingType.TypeSuffix || end.FundamentalType == FundamentalType.Whitespace) && end < paramEnd); end.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, start); } // If we didn't find a type, see if it's implied else if (impliedTypes) { if (Style == ParameterStyle.C) { // Move backwards for "int x, y" for (int i = index - 1; i >= 0; i--) { if (GetFullParameterType(i, out start, out end, out prefixStart, out prefixEnd, out suffixStart, out suffixEnd, false)) { foundType = true; break; } } } else // Style == ParameterStyle.Pascal { // Move forwards for "x, y: integer" for (int i = index + 1; i <= NumberOfParameters; i++) { if (GetFullParameterType(i, out start, out end, out prefixStart, out prefixEnd, out suffixStart, out suffixEnd, false)) { foundType = true; break; } } } } // If we found a type, explicit or implied, find the prefix and suffix. We do this after checking for implied types because we // want "int a[12], b" to work. b should be int, not int[12]. In "int *x, y", y should be int, not int*. if (foundType) { prefixStart = paramStart; while (prefixStart < paramEnd && prefixStart.PrototypeParsingType != PrototypeParsingType.NamePrefix_PartOfType) { prefixStart.Next(); } prefixEnd = prefixStart; while (prefixEnd < paramEnd && prefixEnd.PrototypeParsingType == PrototypeParsingType.NamePrefix_PartOfType) { prefixEnd.Next(); } suffixStart = paramStart; while (suffixStart < paramEnd && suffixStart.PrototypeParsingType != PrototypeParsingType.NameSuffix_PartOfType) { suffixStart.Next(); } suffixEnd = suffixStart; while (suffixEnd < paramEnd && suffixEnd.PrototypeParsingType == PrototypeParsingType.NameSuffix_PartOfType) { suffixEnd.Next(); } return(true); } else // didn't find a type { start = paramEnd; end = paramEnd; prefixStart = paramEnd; prefixEnd = paramEnd; suffixStart = paramEnd; suffixEnd = paramEnd; return(false); } }
/* Function: MarkParameter * Marks the tokens in the parameter specified by the bounds with <PrototypeParsingTypes>. */ protected void MarkParameter(TokenIterator start, TokenIterator end) { // Pass 1: Count the number of "words" in the parameter prior to the default value and mark the default value // separator. We'll figure out how to interpret the words in the second pass. int words = 0; TokenIterator iterator = start; while (iterator < end) { // Default values if (iterator.Character == '=') { iterator.PrototypeParsingType = PrototypeParsingType.DefaultValueSeparator; iterator.Next(); iterator.NextPastWhitespace(end); TokenIterator endOfDefaultValue = end; TokenIterator lookbehind = endOfDefaultValue; lookbehind.Previous(); while (lookbehind >= iterator && lookbehind.PrototypeParsingType == PrototypeParsingType.ParamSeparator) { endOfDefaultValue = lookbehind; lookbehind.Previous(); } endOfDefaultValue.PreviousPastWhitespace(PreviousPastWhitespaceMode.EndingBounds, iterator); if (iterator < endOfDefaultValue) { iterator.SetPrototypeParsingTypeBetween(endOfDefaultValue, PrototypeParsingType.DefaultValue); } break; } // Param separator else if (iterator.PrototypeParsingType == PrototypeParsingType.ParamSeparator) { break; } // "Words" we're interested in else if (TryToSkipTypeOrVarName(ref iterator, end) || TryToSkipComment(ref iterator) || TryToSkipString(ref iterator) || TryToSkipBlock(ref iterator, true)) { // If there was a comment in the prototype, that means it specifically wasn't filtered out because it was something // significant like a Splint comment or /*out*/. Treat it like a modifier. // Strings don't really make sense in the prototype until the default value, but we need the parser to handle it anyway // just so it doesn't lose its mind if one occurs. // If we come across a block that doesn't immediately follow an identifier, it may be something like a C# property so // treat it as a modifier. words++; } // Whitespace and any unexpected random symbols else { iterator.Next(); } } // Pass 2: Mark the "words" we counted from the first pass. The order of words goes [modifier] [modifier] [type] [name], // starting from the right. Typeless languages that only have one word will have it correctly interpreted as the name. iterator = start; TokenIterator wordStart, wordEnd; while (iterator < end) { wordStart = iterator; bool foundWord = false; bool foundBlock = false; if (iterator.PrototypeParsingType == PrototypeParsingType.DefaultValueSeparator || iterator.PrototypeParsingType == PrototypeParsingType.ParamSeparator) { break; } else if (TryToSkipTypeOrVarName(ref iterator, end)) { foundWord = true; } else if (TryToSkipComment(ref iterator) || TryToSkipString(ref iterator) || TryToSkipBlock(ref iterator, true)) { foundWord = true; foundBlock = true; } else { iterator.Next(); } // Process the word we found if (foundWord) { wordEnd = iterator; if (words >= 3) { if (foundBlock && wordEnd.TokenIndex - wordStart.TokenIndex >= 2) { wordStart.PrototypeParsingType = PrototypeParsingType.OpeningTypeModifier; TokenIterator lookbehind = wordEnd; lookbehind.Previous(); lookbehind.PrototypeParsingType = PrototypeParsingType.ClosingTypeModifier; } else { wordStart.SetPrototypeParsingTypeBetween(wordEnd, PrototypeParsingType.TypeModifier); } } else if (words == 2) { MarkType(wordStart, wordEnd); } else if (words == 1) { MarkName(wordStart, wordEnd); // Change the $ at the beginning of the name from a param modifier to part of the name if (wordStart.Character == '$') { wordStart.PrototypeParsingType = PrototypeParsingType.Name; } } words--; } } }