aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/bookmaker
diff options
context:
space:
mode:
Diffstat (limited to 'tools/bookmaker')
-rw-r--r--tools/bookmaker/bookmaker.cpp270
-rw-r--r--tools/bookmaker/bookmaker.h274
-rw-r--r--tools/bookmaker/definition.cpp48
-rw-r--r--tools/bookmaker/includeWriter.cpp586
-rw-r--r--tools/bookmaker/mdOut.cpp1286
-rw-r--r--tools/bookmaker/parserCommon.cpp1
-rw-r--r--tools/bookmaker/spellCheck.cpp48
7 files changed, 1808 insertions, 705 deletions
diff --git a/tools/bookmaker/bookmaker.cpp b/tools/bookmaker/bookmaker.cpp
index 49e347e6ea..aaf3781974 100644
--- a/tools/bookmaker/bookmaker.cpp
+++ b/tools/bookmaker/bookmaker.cpp
@@ -29,18 +29,14 @@ DEFINE_string2(spellcheck, s, "", "Spell-check [once, all, mispelling]. (Require
DEFINE_bool2(tokens, t, false, "Write bmh from include. (Requires -b -i)");
DEFINE_bool2(crosscheck, x, false, "Check bmh against includes. (Requires -b -i)");
// v is reserved for verbose
+DEFINE_bool2(validate, V, false, "Validate that all anchor references have definitions. (Requires -r)");
DEFINE_bool2(skip, z, false, "Skip degenerate missed in legacy preprocessor.");
-/* recipe for generating timestamps for existing doxygen comments
-find include/core -type f -name '*.h' -print -exec git blame {} \; > ~/all.blame.txt
+/* todos:
+
+add new markup to associate enum SaveLayerFlagsSet with typedef SaveLayerFlags, if needed.
-todos:
-add new markup to associate typedef SaveLayerFlags with Enum so that, for
- documentation purposes, this enum is named rather than anonymous
-check column 1 of subtopic tables to see that they start lowercase and don't have a trailing period
-space table better for Constants
should Return be on same line as 'Return Value'?
-remove anonymous header, e.g. Enum SkPaint::::anonymous_2
#Member lost all formatting
#List needs '# content ##', formatting
consts like enum members need fully qualfied refs to make a valid link
@@ -53,6 +49,45 @@ deprecated methods should be sorted down in md out, and show include "Deprecated
rewrap text to fit in some number of columns
#Literal is inflexible, making the entire #Code block link-less (see $Literal in SkImageInfo)
would rather keep links for boby above #Literal, and/or make it a block and not a one-liner
+add check to require #Const to contain #Code block if defining const or constexpr (enum consts have
+ #Code blocks inside the #Enum def
+add spelling rule to look for x-bit but allow x bits
+
+There are a number of formatting bugs with ad hoc patches where a substitution doesn't keep
+the space before or after, or the linefeeds before or after. The rules are not very good either.
+Linefeeds in the bmh file are intended to be respected, but #Formula tends to start on a new line
+even if the contents is intended to be inlined. Probably need to require it to be, e.g.:
+
+ array length must be #Formula # (fXCount + 1) * (fYCount + 1) ##.
+
+where there is always a space between prior words and formula (i.e., between "be" and "(fXCount";
+and, an absense of a space after ## denotes no space between "+ 1)" and ".". These rules preserve
+that # commands are always preceded by a whitespace character. Similarly, #PhraseDef/Ref
+need to be inline or create new paragraphs. #phrase_ref# is sufficiently flexible that it can be
+treated as a word without trailing whitespace, adapting the whitespace of its context. It also must
+always have leading whitespace.
+
+It's awkward that phrase param is a child of the phrase def. Since phrase refs may also be children,
+there is special case code to skip phrase def when looking for additional substitutions in the
+phrase def. Could put it in the token list instead I guess, or make a definition subclass used
+by phrase def with an additional slot...
+
+
+
+#Deprecated soon
+##
+should emit the text "To be deprecated soon." (right now you get just "soon")
+
+SkCanvas_ColorBehavior_kLegacy missing </table> in md out
+
+rearrange const out for md so that const / value / short description comes first in a table,
+followed by more elaborate descriptions, examples, seealso. In md.cpp, look to see if #Subtopic
+has #Const children. If so, generate a summary table first.
+Or, only allow #Line and moderate text description in #Const. Put more verbose text, example,
+seealso, in subsequent #SubTopic. Alpha_Type does this and it looks good.
+
+more spelling: x-value y-value
+
see head of selfCheck.cpp for additional todos
*/
@@ -67,6 +102,8 @@ see head of selfCheck.cpp for additional todos
#define M(mt) (1LL << (int) MarkType::k##mt)
#define M_D M(Description)
#define M_CS M(Class) | M(Struct)
+#define M_MD M(Method) | M(Define)
+#define M_MDCM M_MD | M(Const) | M(Member)
#define M_ST M(Subtopic) | M(Topic)
#define M_CSST M_CS | M_ST
#ifdef M_E
@@ -84,35 +121,35 @@ see head of selfCheck.cpp for additional todos
#define E_N Exemplary::kNo
#define E_O Exemplary::kOptional
+// ToDo: add column to denote which marks are one-liners
BmhParser::MarkProps BmhParser::kMarkProps[] = {
// names without formal definitions (e.g. Column) aren't included
-// fill in other names once they're actually used
{ "", MarkType::kNone, R_Y, E_N, 0 }
, { "A", MarkType::kAnchor, R_N, E_N, 0 }
-, { "Alias", MarkType::kAlias, R_N, E_N, 0 }
-, { "Bug", MarkType::kBug, R_N, E_N, 0 }
-, { "Class", MarkType::kClass, R_Y, E_O, M_CSST | M(Root) }
-, { "Code", MarkType::kCode, R_F, E_N, M_CSST | M_E | M(Method) | M(Define) | M(Typedef) }
+, { "Alias", MarkType::kAlias, R_N, E_N, M_ST | M(Const) }
+, { "Bug", MarkType::kBug, R_N, E_N, M_CSST | M_MDCM | M_E
+ | M(Example) | M(NoExample) }
+, { "Class", MarkType::kClass, R_Y, E_O, M_CSST }
+, { "Code", MarkType::kCode, R_F, E_N, M_CSST | M_E | M_MD | M(Typedef) }
, { "", MarkType::kColumn, R_Y, E_N, M(Row) }
, { "", MarkType::kComment, R_N, E_N, 0 }
, { "Const", MarkType::kConst, R_Y, E_O, M_E | M_ST }
, { "Define", MarkType::kDefine, R_O, E_Y, M_ST }
, { "DefinedBy", MarkType::kDefinedBy, R_N, E_N, M(Method) }
-, { "Deprecated", MarkType::kDeprecated, R_Y, E_N, 0 }
+, { "Deprecated", MarkType::kDeprecated, R_Y, E_N, M_CS | M_MDCM | M_E }
, { "Description", MarkType::kDescription, R_Y, E_N, M(Example) | M(NoExample) }
-, { "Doxygen", MarkType::kDoxygen, R_Y, E_N, 0 }
+, { "Details", MarkType::kDetails, R_N, E_N, M(Const) }
, { "Duration", MarkType::kDuration, R_N, E_N, M(Example) | M(NoExample) }
-, { "Enum", MarkType::kEnum, R_Y, E_O, M_CSST | M(Root) }
-, { "EnumClass", MarkType::kEnumClass, R_Y, E_O, M_CSST | M(Root) }
-, { "Example", MarkType::kExample, R_O, E_N, M_CSST | M_E | M(Method) | M(Const) | M(Define) }
-, { "Experimental", MarkType::kExperimental, R_Y, E_N, 0 }
-, { "External", MarkType::kExternal, R_Y, E_N, M(Root) }
-, { "File", MarkType::kFile, R_N, E_N, M(Track) }
-, { "Formula", MarkType::kFormula, R_F, E_N,
- M(Column) | M_E | M_ST | M(Member) | M(Method) | M_D }
+, { "Enum", MarkType::kEnum, R_Y, E_O, M_CSST }
+, { "EnumClass", MarkType::kEnumClass, R_Y, E_O, M_CSST }
+, { "Example", MarkType::kExample, R_O, E_N, M_CSST | M_E | M_MD }
+, { "Experimental", MarkType::kExperimental, R_Y, E_N, M_CS | M_MDCM | M_E }
+, { "External", MarkType::kExternal, R_Y, E_N, 0 }
+, { "Formula", MarkType::kFormula, R_F, E_N, M(Column) | M(Description)
+ | M_E | M_ST | M_MDCM }
, { "Function", MarkType::kFunction, R_O, E_N, M(Example) | M(NoExample) }
, { "Height", MarkType::kHeight, R_N, E_N, M(Example) | M(NoExample) }
-, { "Illustration", MarkType::kIllustration, R_N, E_N, M(Subtopic) }
+, { "Illustration", MarkType::kIllustration, R_N, E_N, M_CSST | M_MD }
, { "Image", MarkType::kImage, R_N, E_N, M(Example) | M(NoExample) }
, { "In", MarkType::kIn, R_N, E_N, M_CSST | M_E | M(Method) | M(Typedef) }
, { "Legend", MarkType::kLegend, R_Y, E_N, M(Table) }
@@ -123,32 +160,31 @@ BmhParser::MarkProps BmhParser::kMarkProps[] = {
, { "", MarkType::kMarkChar, R_N, E_N, 0 }
, { "Member", MarkType::kMember, R_Y, E_N, M_CSST }
, { "Method", MarkType::kMethod, R_Y, E_Y, M_CSST }
-, { "NoExample", MarkType::kNoExample, R_N, E_N, M_CSST | M_E | M(Method) | M(Const) | M(Define) }
+, { "NoExample", MarkType::kNoExample, R_N, E_N, M_CSST | M_E | M_MD }
+, { "NoJustify", MarkType::kNoJustify, R_N, E_N, M(Const) | M(Member) }
, { "Outdent", MarkType::kOutdent, R_N, E_N, M(Code) }
, { "Param", MarkType::kParam, R_Y, E_N, M(Method) | M(Define) }
, { "PhraseDef", MarkType::kPhraseDef, R_Y, E_N, M(Subtopic) }
-, { "", MarkType::kPhraseRef, R_Y, E_N, 0 }
+, { "", MarkType::kPhraseParam, R_Y, E_N, 0 }
+, { "", MarkType::kPhraseRef, R_N, E_N, 0 }
, { "Platform", MarkType::kPlatform, R_N, E_N, M(Example) | M(NoExample) }
, { "Populate", MarkType::kPopulate, R_N, E_N, M(Subtopic) }
-, { "Private", MarkType::kPrivate, R_N, E_N, 0 }
+, { "Private", MarkType::kPrivate, R_Y, E_N, M_CSST | M_MDCM | M_E }
, { "Return", MarkType::kReturn, R_Y, E_N, M(Method) }
-, { "", MarkType::kRoot, R_Y, E_N, 0 }
, { "", MarkType::kRow, R_Y, E_N, M(Table) | M(List) }
-, { "SeeAlso", MarkType::kSeeAlso, R_C, E_N, M_CSST | M_E | M(Method) | M(Define) | M(Typedef) }
+, { "SeeAlso", MarkType::kSeeAlso, R_C, E_N, M_CSST | M_E | M_MD | M(Typedef) }
, { "Set", MarkType::kSet, R_N, E_N, M(Example) | M(NoExample) }
, { "StdOut", MarkType::kStdOut, R_N, E_N, M(Example) | M(NoExample) }
-, { "Struct", MarkType::kStruct, R_Y, E_O, M(Class) | M(Root) | M_ST }
+, { "Struct", MarkType::kStruct, R_Y, E_O, M(Class) | M_ST }
, { "Substitute", MarkType::kSubstitute, R_N, E_N, M_ST }
, { "Subtopic", MarkType::kSubtopic, R_Y, E_Y, M_CSST }
, { "Table", MarkType::kTable, R_Y, E_N, M(Method) | M_CSST | M_E }
-, { "Template", MarkType::kTemplate, R_Y, E_N, 0 }
+, { "Template", MarkType::kTemplate, R_Y, E_N, M_CSST }
, { "", MarkType::kText, R_N, E_N, 0 }
-, { "Time", MarkType::kTime, R_Y, E_N, M(Track) }
, { "ToDo", MarkType::kToDo, R_N, E_N, 0 }
-, { "Topic", MarkType::kTopic, R_Y, E_Y, M_CS | M(Root) | M(Topic) }
-, { "Track", MarkType::kTrack, R_Y, E_N, M_E | M_ST }
-, { "Typedef", MarkType::kTypedef, R_Y, E_N, M(Class) | M_ST }
-, { "", MarkType::kUnion, R_Y, E_N, 0 }
+, { "Topic", MarkType::kTopic, R_Y, E_Y, 0 }
+, { "Typedef", MarkType::kTypedef, R_Y, E_N, M_CSST | M_E }
+, { "Union", MarkType::kUnion, R_Y, E_N, M_CSST }
, { "Volatile", MarkType::kVolatile, R_N, E_N, M(StdOut) }
, { "Width", MarkType::kWidth, R_N, E_N, M(Example) | M(NoExample) }
};
@@ -163,6 +199,7 @@ BmhParser::MarkProps BmhParser::kMarkProps[] = {
#undef M_CSST
#undef M_ST
#undef M_CS
+#undef M_MCD
#undef M_D
#undef M
@@ -438,18 +475,46 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy
fParent->fChildren.push_back(definition);
}
break;
+ } else if (MarkType::kPhraseDef == markType) {
+ bool hasParams = '(' == this->next();
+ fMarkup.emplace_front(markType, defStart, fLineCount, fParent, fMC);
+ definition = &fMarkup.front();
+ definition->fName = typeNameBuilder[0];
+ definition->fFiddle = fParent->fFiddle;
+ definition->fContentStart = fChar;
+ if (hasParams) {
+ char lastChar;
+ do {
+ const char* subEnd = this->anyOf(",)\n");
+ if (!subEnd || '\n' == *subEnd) {
+ return this->reportError<bool>("unexpected phrase list end");
+ }
+ fMarkup.emplace_front(MarkType::kPhraseParam, fChar, fLineCount, fParent,
+ fMC);
+ Definition* phraseParam = &fMarkup.front();
+ phraseParam->fContentStart = fChar;
+ phraseParam->fContentEnd = subEnd;
+ phraseParam->fName = string(fChar, subEnd - fChar);
+ definition->fChildren.push_back(phraseParam);
+ this->skipTo(subEnd);
+ lastChar = this->next();
+ phraseParam->fTerminator = fChar;
+ } while (')' != lastChar);
+ this->skipWhiteSpace();
+ definition->fContentStart = fChar;
+ }
+ this->setAsParent(definition);
+ break;
}
// not one-liners
case MarkType::kCode:
case MarkType::kExample:
- case MarkType::kExperimental:
case MarkType::kFormula:
case MarkType::kFunction:
case MarkType::kLegend:
case MarkType::kList:
case MarkType::kPrivate:
case MarkType::kTable:
- case MarkType::kTrack:
if (hasEnd) {
definition = fParent;
if (markType != fParent->fMarkType) {
@@ -502,21 +567,22 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy
case MarkType::kAnchor:
case MarkType::kBug:
case MarkType::kDeprecated:
+ case MarkType::kDetails:
case MarkType::kDuration:
- case MarkType::kFile:
+ case MarkType::kExperimental:
case MarkType::kHeight:
case MarkType::kIllustration:
case MarkType::kImage:
case MarkType::kIn:
case MarkType::kLine:
case MarkType::kLiteral:
+ case MarkType::kNoJustify:
case MarkType::kOutdent:
case MarkType::kPlatform:
case MarkType::kPopulate:
case MarkType::kSeeAlso:
case MarkType::kSet:
case MarkType::kSubstitute:
- case MarkType::kTime:
case MarkType::kVolatile:
case MarkType::kWidth:
// todo : add check disallowing children?
@@ -569,16 +635,37 @@ bool BmhParser::addDefinition(const char* defStart, bool hasEnd, MarkType markTy
}
fMarkup.emplace_front(MarkType::kText, start, fLineCount, definition, fMC);
Definition* text = &fMarkup.front();
+ if (!islower(start[0]) && (!isdigit(start[0])
+ || MarkType::kConst != definition->fParent->fMarkType)) {
+ return this->reportError<bool>("expect lower case start");
+ }
+ string contents = string(start, end - start);
+ if (string::npos != contents.find('.')) {
+ return this->reportError<bool>("expect phrase, not sentence");
+ }
+ size_t firstSpace = contents.find(' ');
+ if (string::npos == firstSpace || 0 == firstSpace || 's' != start[firstSpace - 1]) {
+ if (MarkType::kMethod == fParent->fMarkType && "experimental" != contents
+ && "incomplete" != contents) {
+ return this->reportError<bool>( "expect phrase in third person present"
+ " tense (1st word should end in 's'");
+ }
+ }
text->fContentStart = start;
text->fContentEnd = end;
text->fTerminator = fChar;
definition->fContentEnd = text->fContentEnd;
definition->fTerminator = fChar;
definition->fChildren.emplace_back(text);
- } else if (MarkType::kDeprecated == markType) {
+ } else if (IncompleteAllowed(markType)) {
this->skipSpace();
fParent->fDeprecated = true;
- fParent->fToBeDeprecated = this->skipExact("soon");
+ fParent->fDetails =
+ this->skipExact("soon") ? Definition::Details::kSoonToBe_Deprecated :
+ this->skipExact("testing") ? Definition::Details::kTestingOnly_Experiment :
+ this->skipExact("do not use") ? Definition::Details::kDoNotUse_Experiement :
+ this->skipExact("not ready") ? Definition::Details::kNotReady_Experiment :
+ Definition::Details::kNone;
this->skipSpace();
if ('\n' != this->peek()) {
return this->reportError<bool>("unexpected text after #Deprecated");
@@ -1210,8 +1297,8 @@ bool BmhParser::findDefinitions() {
const char* defStart = fChar - 1;
MarkType markType = this->getMarkType(MarkLookup::kRequire);
bool hasEnd = this->hasEndToken();
- if (!hasEnd) {
- MarkType parentType = fParent ? fParent->fMarkType : MarkType::kRoot;
+ if (!hasEnd && fParent) {
+ MarkType parentType = fParent->fMarkType;
uint64_t parentMask = kMarkProps[(int) markType].fParentMask;
if (parentMask && !(parentMask & (1LL << (int) parentType))) {
return this->reportError<bool>("invalid parent");
@@ -1298,8 +1385,24 @@ bool BmhParser::findDefinitions() {
const char* phraseNameStart = fChar;
this->skipPhraseName();
string phraseKey = string(phraseNameStart, fChar - phraseNameStart);
- if (fMC != this->next()) {
- return this->reportError<bool>("expect # after phrase-name");
+ char delimiter = this->next();
+ vector<string> params;
+ vector<const char*> paramsLoc;
+ if (fMC != delimiter) {
+ if ('(' != delimiter) {
+ return this->reportError<bool>("expect # after phrase name");
+ }
+ // phrase may take comma delimited parameter list
+ do {
+ const char* subEnd = this->anyOf(",)\n");
+ if (!subEnd || '\n' == *subEnd) {
+ return this->reportError<bool>("unexpected phrase list end");
+ }
+ params.push_back(string(fChar, subEnd - fChar));
+ paramsLoc.push_back(fChar);
+ this->skipTo(subEnd);
+
+ } while (')' != this->next());
}
const char* start = phraseNameStart;
SkASSERT('#' == start[-1]);
@@ -1309,12 +1412,24 @@ bool BmhParser::findDefinitions() {
}
fMarkup.emplace_front(MarkType::kPhraseRef, start, fLineCount, fParent, fMC);
Definition* markChar = &fMarkup.front();
+ this->skipExact("#");
markChar->fContentStart = fChar;
- this->skipToEndBracket('\n');
markChar->fContentEnd = fChar;
markChar->fTerminator = fChar;
markChar->fName = phraseKey;
fParent->fChildren.push_back(markChar);
+ int paramLocIndex = 0;
+ for (auto param : params) {
+ const char* paramLoc = paramsLoc[paramLocIndex++];
+ fMarkup.emplace_front(MarkType::kPhraseParam, paramLoc, fLineCount, fParent,
+ fMC);
+ Definition* phraseParam = &fMarkup.front();
+ phraseParam->fContentStart = paramLoc;
+ phraseParam->fContentEnd = paramLoc + param.length();
+ phraseParam->fTerminator = paramLoc + param.length();
+ phraseParam->fName = param;
+ markChar->fChildren.push_back(phraseParam);
+ }
}
}
char nextChar = this->next();
@@ -1384,13 +1499,14 @@ bool HackParser::hackFiles() {
SkASSERT(!root->fParent);
fStart = root->fStart;
fChar = fStart;
- fClassesAndStructs = nullptr;
+ fClasses = nullptr;
fConstants = nullptr;
fConstructors = nullptr;
fMemberFunctions = nullptr;
fMembers = nullptr;
fOperators = nullptr;
fRelatedFunctions = nullptr;
+ fStructs = nullptr;
this->topicIter(root);
fprintf(fOut, "%.*s", (int) (fEnd - fChar), fChar);
fclose(fOut);
@@ -1442,31 +1558,35 @@ string HackParser::searchTable(const Definition* tableHolder, const Definition*
// returns true if topic has method
void HackParser::topicIter(const Definition* topic) {
- if (string::npos != topic->fName.find(MdOut::kClassesAndStructs)) {
- SkASSERT(!fClassesAndStructs);
- fClassesAndStructs = topic;
+ if (string::npos != topic->fName.find(SubtopicKeys::kClasses)) {
+ SkASSERT(!fClasses);
+ fClasses = topic;
+ }
+ if (string::npos != topic->fName.find(SubtopicKeys::kStructs)) {
+ SkASSERT(!fStructs);
+ fStructs = topic;
}
- if (string::npos != topic->fName.find(MdOut::kConstants)) {
+ if (string::npos != topic->fName.find(SubtopicKeys::kConstants)) {
SkASSERT(!fConstants);
fConstants = topic;
}
- if (string::npos != topic->fName.find(MdOut::kConstructors)) {
+ if (string::npos != topic->fName.find(SubtopicKeys::kConstructors)) {
SkASSERT(!fConstructors);
fConstructors = topic;
}
- if (string::npos != topic->fName.find(MdOut::kMemberFunctions)) {
+ if (string::npos != topic->fName.find(SubtopicKeys::kMemberFunctions)) {
SkASSERT(!fMemberFunctions);
fMemberFunctions = topic;
}
- if (string::npos != topic->fName.find(MdOut::kMembers)) {
+ if (string::npos != topic->fName.find(SubtopicKeys::kMembers)) {
SkASSERT(!fMembers);
fMembers = topic;
}
- if (string::npos != topic->fName.find(MdOut::kOperators)) {
+ if (string::npos != topic->fName.find(SubtopicKeys::kOperators)) {
SkASSERT(!fOperators);
fOperators = topic;
}
- if (string::npos != topic->fName.find(MdOut::kRelatedFunctions)) {
+ if (string::npos != topic->fName.find(SubtopicKeys::kRelatedFunctions)) {
SkASSERT(!fRelatedFunctions);
fRelatedFunctions = topic;
}
@@ -1539,8 +1659,11 @@ void HackParser::topicIter(const Definition* topic) {
this->topicIter(child);
break;
case MarkType::kStruct:
+ this->addOneLiner(fStructs, child, hasLine, false);
+ this->topicIter(child);
+ break;
case MarkType::kClass:
- this->addOneLiner(fClassesAndStructs, child, hasLine, false);
+ this->addOneLiner(fClasses, child, hasLine, false);
this->topicIter(child);
break;
case MarkType::kEnum:
@@ -1822,6 +1945,7 @@ void TextParser::reportError(const char* errorStr) const {
}
void TextParser::reportWarning(const char* errorStr) const {
+ SkASSERT(fLine < fEnd);
TextParser err(fFileName, fLine, fEnd, fLineCount);
size_t lineLen = this->lineLength();
ptrdiff_t spaces = fChar - fLine;
@@ -1892,7 +2016,13 @@ string TextParser::typedefName() {
do {
this->skipToWhiteSpace();
if (fChar < end && isspace(fChar[0])) {
+ const char* whiteStart = fChar;
this->skipWhiteSpace();
+ // FIXME: test should be for fMC
+ if ('#' == fChar[0]) {
+ end = whiteStart;
+ break;
+ }
lastWord = fChar;
} else {
break;
@@ -2051,8 +2181,6 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) {
break;
case MarkType::kCode:
case MarkType::kDescription:
- case MarkType::kDoxygen:
- case MarkType::kExperimental:
case MarkType::kExternal:
case MarkType::kFormula:
case MarkType::kFunction:
@@ -2060,7 +2188,6 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) {
case MarkType::kList:
case MarkType::kNoExample:
case MarkType::kPrivate:
- case MarkType::kTrack:
this->skipNoName();
break;
case MarkType::kLine:
@@ -2071,13 +2198,15 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) {
case MarkType::kBug: // fixme: expect number
case MarkType::kDefinedBy:
case MarkType::kDeprecated:
+ case MarkType::kDetails:
case MarkType::kDuration:
- case MarkType::kFile:
+ case MarkType::kExperimental:
case MarkType::kHeight:
case MarkType::kIllustration:
case MarkType::kImage:
case MarkType::kIn:
case MarkType::kLiteral:
+ case MarkType::kNoJustify:
case MarkType::kOutdent:
case MarkType::kPlatform:
case MarkType::kPopulate:
@@ -2085,7 +2214,6 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) {
case MarkType::kSeeAlso:
case MarkType::kSet:
case MarkType::kSubstitute:
- case MarkType::kTime:
case MarkType::kToDo:
case MarkType::kVolatile:
case MarkType::kWidth:
@@ -2104,12 +2232,22 @@ vector<string> BmhParser::typeName(MarkType markType, bool* checkEnd) {
builder = this->typedefName();
break;
case MarkType::kParam:
- case MarkType::kPhraseDef:
// fixme: expect camelCase for param
builder = this->word("", "");
this->skipSpace();
*checkEnd = false;
break;
+ case MarkType::kPhraseDef: {
+ const char* nameEnd = this->anyOf("(\n");
+ builder = string(fChar, nameEnd - fChar);
+ this->skipLower();
+ if (fChar != nameEnd) {
+ this->reportError("expect lower case only");
+ break;
+ }
+ this->skipTo(nameEnd);
+ *checkEnd = false;
+ } break;
case MarkType::kTable:
this->skipNoName();
break; // unnamed
@@ -2516,6 +2654,7 @@ int main(int argc, char** const argv) {
}
MdOut mdOut(bmhParser);
mdOut.fDebugOut = FLAGS_stdout;
+ mdOut.fValidate = FLAGS_validate;
if (!FLAGS_bmh.isEmpty() && mdOut.buildReferences(includeParser,
FLAGS_bmh[0], FLAGS_ref[0])) {
bmhParser.fWroteOut = true;
@@ -2523,6 +2662,9 @@ int main(int argc, char** const argv) {
if (!FLAGS_status.isEmpty() && mdOut.buildStatus(FLAGS_status[0], FLAGS_ref[0])) {
bmhParser.fWroteOut = true;
}
+ if (FLAGS_validate) {
+ mdOut.checkAnchors();
+ }
}
if (!done && !FLAGS_spellcheck.isEmpty() && FLAGS_examples.isEmpty()) {
if (!FLAGS_bmh.isEmpty()) {
diff --git a/tools/bookmaker/bookmaker.h b/tools/bookmaker/bookmaker.h
index 7e09d41afd..c755beec43 100644
--- a/tools/bookmaker/bookmaker.h
+++ b/tools/bookmaker/bookmaker.h
@@ -95,14 +95,13 @@ enum class MarkType {
kDefinedBy,
kDeprecated,
kDescription,
- kDoxygen,
+ kDetails, // used by #Const to specify #Subtopic details with examples and so on
kDuration,
kEnum,
kEnumClass,
kExample,
kExperimental,
kExternal,
- kFile,
kFormula,
kFunction,
kHeight,
@@ -118,15 +117,16 @@ enum class MarkType {
kMember,
kMethod,
kNoExample,
+ kNoJustify, // don't contribute this #Line to tabular comment measure, even if it fits
kOutdent,
kParam,
kPhraseDef,
+ kPhraseParam,
kPhraseRef,
kPlatform,
kPopulate,
kPrivate,
kReturn,
- kRoot,
kRow,
kSeeAlso,
kSet,
@@ -137,16 +137,18 @@ enum class MarkType {
kTable,
kTemplate,
kText,
- kTime,
kToDo,
kTopic,
- kTrack,
kTypedef,
kUnion,
kVolatile,
kWidth,
};
+static inline bool IncompleteAllowed(MarkType markType) {
+ return MarkType::kDeprecated == markType || MarkType::kExperimental == markType;
+}
+
enum {
Last_MarkType = (int) MarkType::kWidth,
};
@@ -299,21 +301,23 @@ public:
return *loc;
}
+ // either /n/n or /n# will stop parsing a typedef
const char* doubleLF() const {
- int count = 0;
- const char* ptr = fChar;
+ const char* ptr = fChar - 1;
const char* doubleStart = nullptr;
- while (ptr < fEnd) {
- if ('\n' == ptr[0]) {
- if (++count == 1) {
+ while (++ptr < fEnd) {
+ if (!doubleStart) {
+ if ('\n' == ptr[0]) {
doubleStart = ptr;
- } else {
- return doubleStart;
}
- } else if (' ' < ptr[0]) {
- count = 0;
+ continue;
+ }
+ if ('\n' == ptr[0] || '#' == ptr[0]) {
+ return doubleStart;
+ }
+ if (' ' < ptr[0]) {
+ doubleStart = nullptr;
}
- ++ptr;
}
return nullptr;
}
@@ -476,6 +480,12 @@ public:
return true;
}
+ void skipLower() {
+ while (fChar < fEnd && (islower(fChar[0]) || '_' == fChar[0])) {
+ fChar++;
+ }
+ }
+
void skipToNonAlphaNum() {
while (fChar < fEnd && (isalnum(fChar[0]) || '_' == fChar[0])) {
fChar++;
@@ -796,6 +806,19 @@ public:
kOmitReturn,
};
+ enum class Details {
+ kNone,
+ kSoonToBe_Deprecated,
+ kTestingOnly_Experiment,
+ kDoNotUse_Experiement,
+ kNotReady_Experiment,
+ };
+
+ enum class DetailsType {
+ kPhrase,
+ kSentence,
+ };
+
Definition() {}
Definition(const char* start, const char* end, int line, Definition* parent, char mc)
@@ -845,7 +868,6 @@ public:
virtual ~Definition() {}
virtual RootDefinition* asRoot() { SkASSERT(0); return nullptr; }
- virtual const RootDefinition* asRoot() const { SkASSERT(0); return nullptr; }
bool boilerplateIfDef();
bool boilerplateEndIf() {
@@ -857,7 +879,7 @@ public:
bool crossCheck(const Definition& includeToken) const;
bool crossCheckInside(const char* start, const char* end, const Definition& includeToken) const;
- const Definition* csParent() const {
+ Definition* csParent() {
Definition* test = fParent;
while (test) {
if (MarkType::kStruct == test->fMarkType || MarkType::kClass == test->fMarkType) {
@@ -875,12 +897,14 @@ public:
const Definition* hasChild(MarkType markType) const;
bool hasMatch(string name) const;
const Definition* hasParam(string ref) const;
+ string incompleteMessage(DetailsType ) const;
bool isClone() const { return fClone; }
- Definition* iRootParent() {
- Definition* test = fParent;
+ const Definition* iRootParent() const {
+ const Definition* test = fParent;
while (test) {
- if (Type::kKeyWord == test->fType && KeyWord::kClass == test->fKeyWord) {
+ if (Type::kKeyWord == test->fType
+ && (KeyWord::kClass == test->fKeyWord || KeyWord::kStruct == test->fKeyWord)) {
return test;
}
test = test->fParent;
@@ -923,6 +947,17 @@ public:
fParentIndex = fParent ? (int) fParent->fTokens.size() : -1;
}
+ const Definition* subtopicParent() const {
+ Definition* test = fParent;
+ while (test) {
+ if (MarkType::kTopic == test->fMarkType || MarkType::kSubtopic == test->fMarkType) {
+ return test;
+ }
+ test = test->fParent;
+ }
+ return nullptr;
+ }
+
const Definition* topicParent() const {
Definition* test = fParent;
while (test) {
@@ -962,13 +997,29 @@ public:
bool fDeprecated = false;
bool fOperatorConst = false;
bool fPrivate = false;
- bool fShort = false;
- bool fToBeDeprecated = false;
+ Details fDetails = Details::kNone;
bool fMemberStart = false;
bool fAnonymous = false;
mutable bool fVisited = false;
};
+class SubtopicKeys {
+public:
+ static constexpr const char* kClasses = "Class";
+ static constexpr const char* kConstants = "Constant";
+ static constexpr const char* kConstructors = "Constructor";
+ static constexpr const char* kDefines = "Define";
+ static constexpr const char* kMemberFunctions = "Member_Function";
+ static constexpr const char* kMembers = "Member";
+ static constexpr const char* kOperators = "Operator";
+ static constexpr const char* kOverview = "Overview";
+ static constexpr const char* kRelatedFunctions = "Related_Function";
+ static constexpr const char* kStructs = "Struct";
+ static constexpr const char* kTypedefs = "Typedef";
+
+ static const char* kGeneratedSubtopics[];
+};
+
class RootDefinition : public Definition {
public:
enum class AllowParens {
@@ -976,6 +1027,15 @@ public:
kYes,
};
+ struct SubtopicContents {
+ SubtopicContents()
+ : fShowClones(false) {
+ }
+
+ vector<Definition*> fMembers;
+ bool fShowClones;
+ };
+
RootDefinition() {
}
@@ -994,17 +1054,22 @@ public:
}
RootDefinition* asRoot() override { return this; }
- const RootDefinition* asRoot() const override { return this; }
void clearVisited();
bool dumpUnVisited();
- const Definition* find(string ref, AllowParens ) const;
+ Definition* find(string ref, AllowParens );
bool isRoot() const override { return true; }
+
+ SubtopicContents& populator(const char* key) {
+ return fPopulators[key];
+ }
+
RootDefinition* rootParent() override { return fRootParent; }
const RootDefinition* rootParent() const override { return fRootParent; }
void setRootParent(RootDefinition* rootParent) { fRootParent = rootParent; }
unordered_map<string, RootDefinition*> fBranches;
unordered_map<string, Definition> fLeaves;
+ unordered_map<string, SubtopicContents> fPopulators;
private:
RootDefinition* fRootParent = nullptr;
};
@@ -1044,6 +1109,7 @@ public:
ParserCommon() : TextParser()
, fParent(nullptr)
, fDebugOut(false)
+ , fValidate(false)
{
}
@@ -1196,7 +1262,10 @@ public:
int fPendingSpace; // one or two spaces should preceed the next string or block
char fLastChar; // last written
bool fDebugOut; // set true to write to std out
+ bool fValidate; // set true to check anchor defs and refs
bool fOutdentNext; // set at end of embedded struct to prevent premature outdent
+ bool fWroteSomething; // used to detect empty content; an alternative source is preferable
+
private:
typedef TextParser INHERITED;
};
@@ -1238,6 +1307,7 @@ public:
kFormula, // resolve methods as they are used, not as they are prototyped
kLiteral, // output untouched
kClone, // resolved, output, with references to clones as well
+ kSimple, // resolve simple words (used to resolve method declarations)
};
enum class ExampleOptions {
@@ -1283,14 +1353,13 @@ public:
, { nullptr, MarkType::kDefinedBy }
, { nullptr, MarkType::kDeprecated }
, { nullptr, MarkType::kDescription }
- , { nullptr, MarkType::kDoxygen }
+ , { nullptr, MarkType::kDetails }
, { nullptr, MarkType::kDuration }
, { &fEnumMap, MarkType::kEnum }
, { &fClassMap, MarkType::kEnumClass }
, { nullptr, MarkType::kExample }
, { nullptr, MarkType::kExperimental }
, { nullptr, MarkType::kExternal }
- , { nullptr, MarkType::kFile }
, { nullptr, MarkType::kFormula }
, { nullptr, MarkType::kFunction }
, { nullptr, MarkType::kHeight }
@@ -1306,15 +1375,16 @@ public:
, { nullptr, MarkType::kMember }
, { &fMethodMap, MarkType::kMethod }
, { nullptr, MarkType::kNoExample }
+ , { nullptr, MarkType::kNoJustify }
, { nullptr, MarkType::kOutdent }
, { nullptr, MarkType::kParam }
, { nullptr, MarkType::kPhraseDef }
+ , { nullptr, MarkType::kPhraseParam }
, { nullptr, MarkType::kPhraseRef }
, { nullptr, MarkType::kPlatform }
, { nullptr, MarkType::kPopulate }
, { nullptr, MarkType::kPrivate }
, { nullptr, MarkType::kReturn }
- , { nullptr, MarkType::kRoot }
, { nullptr, MarkType::kRow }
, { nullptr, MarkType::kSeeAlso }
, { nullptr, MarkType::kSet }
@@ -1325,10 +1395,8 @@ public:
, { nullptr, MarkType::kTable }
, { nullptr, MarkType::kTemplate }
, { nullptr, MarkType::kText }
- , { nullptr, MarkType::kTime }
, { nullptr, MarkType::kToDo }
, { nullptr, MarkType::kTopic }
- , { nullptr, MarkType::kTrack }
, { &fTypedefMap, MarkType::kTypedef }
, { nullptr, MarkType::kUnion }
, { nullptr, MarkType::kVolatile }
@@ -1484,14 +1552,13 @@ public:
, { nullptr, MarkType::kDefinedBy }
, { nullptr, MarkType::kDeprecated }
, { nullptr, MarkType::kDescription }
- , { nullptr, MarkType::kDoxygen }
+ , { nullptr, MarkType::kDetails }
, { nullptr, MarkType::kDuration }
, { &fIEnumMap, MarkType::kEnum }
, { &fIEnumMap, MarkType::kEnumClass }
, { nullptr, MarkType::kExample }
, { nullptr, MarkType::kExperimental }
, { nullptr, MarkType::kExternal }
- , { nullptr, MarkType::kFile }
, { nullptr, MarkType::kFormula }
, { nullptr, MarkType::kFunction }
, { nullptr, MarkType::kHeight }
@@ -1507,15 +1574,16 @@ public:
, { nullptr, MarkType::kMember }
, { nullptr, MarkType::kMethod }
, { nullptr, MarkType::kNoExample }
+ , { nullptr, MarkType::kNoJustify }
, { nullptr, MarkType::kOutdent }
, { nullptr, MarkType::kParam }
, { nullptr, MarkType::kPhraseDef }
+ , { nullptr, MarkType::kPhraseParam }
, { nullptr, MarkType::kPhraseRef }
, { nullptr, MarkType::kPlatform }
, { nullptr, MarkType::kPopulate }
, { nullptr, MarkType::kPrivate }
, { nullptr, MarkType::kReturn }
- , { nullptr, MarkType::kRoot }
, { nullptr, MarkType::kRow }
, { nullptr, MarkType::kSeeAlso }
, { nullptr, MarkType::kSet }
@@ -1526,10 +1594,8 @@ public:
, { nullptr, MarkType::kTable }
, { &fITemplateMap, MarkType::kTemplate }
, { nullptr, MarkType::kText }
- , { nullptr, MarkType::kTime }
, { nullptr, MarkType::kToDo }
, { nullptr, MarkType::kTopic }
- , { nullptr, MarkType::kTrack }
, { &fITypedefMap, MarkType::kTypedef }
, { &fIUnionMap, MarkType::kUnion }
, { nullptr, MarkType::kVolatile }
@@ -1905,6 +1971,13 @@ public:
kOut,
};
+ enum class ItemState {
+ kNone,
+ kName,
+ kValue,
+ kComment,
+ };
+
struct IterState {
IterState (list<Definition>::iterator tIter, list<Definition>::iterator tIterEnd)
: fDefIter(tIter)
@@ -1937,6 +2010,23 @@ public:
bool fWord;
};
+ struct Item {
+ string fName;
+ string fValue;
+ };
+
+ struct LastItem {
+ const char* fStart;
+ const char* fEnd;
+ };
+
+ struct ItemLength {
+ int fCurName;
+ int fCurValue;
+ int fLongestName;
+ int fLongestValue;
+ };
+
IncludeWriter() : IncludeParser() {
this->reset();
}
@@ -1954,11 +2044,19 @@ public:
return 0 == size;
}
+ bool checkChildCommentLength(const Definition* parent, MarkType childType) const;
+ void checkEnumLengths(const Definition& child, string enumName, ItemLength* length) const;
void constOut(const Definition* memberStart, const Definition& child,
- const Definition* bmhConst);
+ const Definition* bmhConst);
void descriptionOut(const Definition* def, SkipFirstLine , Phrase );
- void enumHeaderOut(const RootDefinition* root, const Definition& child);
- void enumMembersOut(const RootDefinition* root, Definition& child);
+ void enumHeaderOut(RootDefinition* root, const Definition& child);
+ string enumMemberComment(const Definition* currentEnumItem, const Definition& child) const;
+ const Definition* enumMemberForComment(const Definition* currentEnumItem) const;
+ ItemState enumMemberName(const Definition& child,
+ const Definition* token, Item* , LastItem* , const Definition** enumItem);
+ void enumMemberOut(const Definition* currentEnumItem, const Definition& child,
+ const Item& , Preprocessor& );
+ void enumMembersOut(Definition& child);
bool enumPreprocessor(Definition* token, MemberPass pass,
vector<IterState>& iterStack, IterState** iterState, Preprocessor* );
void enumSizeItems(const Definition& child);
@@ -1970,7 +2068,8 @@ public:
int lookupReference(const PunctuationState punctuation, const Word word,
const int start, const int run, int lastWrite, const char last,
const char* data);
- void methodOut(const Definition* method, const Definition& child);
+ const Definition* matchMemberName(string matchName, const Definition& child) const;
+ void methodOut(Definition* method, const Definition& child);
bool populate(Definition* def, ParentPair* parentPair, RootDefinition* root);
bool populate(BmhParser& bmhParser);
@@ -1993,7 +2092,6 @@ public:
Definition* structMemberOut(const Definition* memberStart, const Definition& child);
void structOut(const Definition* root, const Definition& child,
const char* commentStart, const char* commentEnd);
- void structSetMembersShort(const vector<Definition*>& bmhChildren);
void structSizeMembers(const Definition& child);
private:
BmhParser* fBmhParser;
@@ -2002,7 +2100,7 @@ private:
const Definition* fBmhMethod;
const Definition* fEnumDef;
const Definition* fMethodDef;
- const Definition* fBmhStructDef;
+ Definition* fBmhStructDef;
const char* fContinuation; // used to construct paren-qualified method name
int fAnonymousEnumCount;
int fEnumItemValueTab;
@@ -2120,13 +2218,14 @@ public:
private:
const BmhParser& fBmhParser;
- const Definition* fClassesAndStructs;
+ const Definition* fClasses;
const Definition* fConstants;
const Definition* fConstructors;
const Definition* fMemberFunctions;
const Definition* fMembers;
const Definition* fOperators;
const Definition* fRelatedFunctions;
+ const Definition* fStructs;
bool hackFiles();
typedef ParserCommon INHERITED;
@@ -2134,25 +2233,21 @@ private:
class MdOut : public ParserCommon {
public:
- MdOut(const BmhParser& bmh) : ParserCommon()
+ struct SubtopicDescriptions {
+ string fName;
+ string fOneLiner;
+ string fDetails;
+ };
+
+ MdOut(BmhParser& bmh) : ParserCommon()
, fBmhParser(bmh) {
this->reset();
+ this->addPopulators();
}
bool buildReferences(const IncludeParser& , const char* docDir, const char* mdOutDirOrFile);
bool buildStatus(const char* docDir, const char* mdOutDir);
-
- static constexpr const char* kClassesAndStructs = "Class_or_Struct";
- static constexpr const char* kConstants = "Constant";
- static constexpr const char* kConstructors = "Constructor";
- static constexpr const char* kDefines = "Define";
- static constexpr const char* kMemberFunctions = "Member_Function";
- static constexpr const char* kMembers = "Member";
- static constexpr const char* kOperators = "Operator";
- static constexpr const char* kOverview = "Overview";
- static constexpr const char* kRelatedFunctions = "Related_Function";
- static constexpr const char* kSubtopics = "Overview_Subtopic";
- static constexpr const char* kTypedefs = "Typedef";
+ void checkAnchors();
private:
enum class TableState {
@@ -2161,34 +2256,39 @@ private:
kColumn,
};
- struct TableContents {
- TableContents()
- : fShowClones(false) {
- }
-
- string fDescription;
- vector<const Definition*> fMembers;
- bool fShowClones;
+ struct AnchorDef {
+ string fDef;
+ MarkType fMarkType;
};
+ void addPopulators();
string addReferences(const char* start, const char* end, BmhParser::Resolvable );
+ string anchorDef(string def, string name);
+ string anchorLocalRef(string ref, string name);
+ string anchorRef(string def, string name);
+
bool buildRefFromFile(const char* fileName, const char* outDir);
bool checkParamReturnBody(const Definition* def);
- void childrenOut(const Definition* def, const char* contentStart);
- const Definition* csParent() const;
+ Definition* checkParentsForMatch(Definition* test, string ref) const;
+ void childrenOut(Definition* def, const char* contentStart);
+ Definition* csParent();
const Definition* findParamType();
+ string getMemberTypeName(const Definition* def, string* memberType);
+ static bool HasDetails(const Definition* def);
+ void htmlOut(string );
const Definition* isDefined(const TextParser& , string ref, BmhParser::Resolvable );
+ const Definition* isDefinedByParent(RootDefinition* root, string ref);
string linkName(const Definition* ) const;
- string linkRef(string leadingSpaces, const Definition*, string ref,
- BmhParser::Resolvable ) const;
- void markTypeOut(Definition* );
+ string linkRef(string leadingSpaces, const Definition*, string ref, BmhParser::Resolvable );
+ void markTypeOut(Definition* , const Definition** prior);
void mdHeaderOut(int depth) { mdHeaderOutLF(depth, 2); }
void mdHeaderOutLF(int depth, int lf);
- void overviewOut();
bool parseFromFile(const char* path) override { return true; }
- void populateTables(const Definition* def);
+ void populateOne(Definition* def,
+ unordered_map<string, RootDefinition::SubtopicContents>& populator);
+ void populateTables(const Definition* def, RootDefinition* );
- TableContents& populator(const char* key) {
+ SubtopicDescriptions& populator(string key) {
auto entry = fPopulators.find(key);
// FIXME: this should have been detected earlier
SkASSERT(fPopulators.end() != entry);
@@ -2200,6 +2300,7 @@ private:
fEnumClass = nullptr;
fMethod = nullptr;
fRoot = nullptr;
+ fSubtopic = nullptr;
fLastParam = nullptr;
fTableState = TableState::kNone;
fAddRefFailed = false;
@@ -2208,6 +2309,7 @@ private:
fInList = false;
fResolveAndIndent = false;
fLiteralAndIndent = false;
+ fLastDef = nullptr;
}
BmhParser::Resolvable resolvable(const Definition* definition) const {
@@ -2227,19 +2329,32 @@ private:
}
void resolveOut(const char* start, const char* end, BmhParser::Resolvable );
- void rowOut(const char * name, string description);
- void subtopicOut(const TableContents& tableContents);
- void subtopicsOut();
+ void rowOut(const char * name, string description, bool literalName);
+
+ void subtopicOut(string name);
+ void subtopicsOut(Definition* def);
+ void summaryOut(const Definition* def, MarkType , string name);
+ string tableDataCodeDef(const Definition* def);
+ string tableDataCodeDef(string def, string name);
+ string tableDataCodeLocalRef(string name);
+ string tableDataCodeLocalRef(string ref, string name);
+ string tableDataCodeRef(const Definition* ref);
+ string tableDataCodeRef(string ref, string name);
- unordered_map<string, TableContents> fPopulators;
vector<const Definition*> fClassStack;
+ unordered_map<string, vector<AnchorDef> > fAllAnchorDefs;
+ unordered_map<string, vector<string> > fAllAnchorRefs;
- const BmhParser& fBmhParser;
+ BmhParser& fBmhParser;
const Definition* fEnumClass;
+ const Definition* fLastDef;
Definition* fMethod;
- const RootDefinition* fRoot;
+ RootDefinition* fRoot; // used in generating populated tables; always struct or class
+ RootDefinition* fSubtopic; // used in resolving symbols
const Definition* fLastParam;
TableState fTableState;
+ unordered_map<string, SubtopicDescriptions> fPopulators;
+ unordered_map<string, string> fPhraseParams;
bool fAddRefFailed;
bool fHasFiddle;
bool fInDescription; // FIXME: for now, ignore unfound camelCase in description since it may
@@ -2247,6 +2362,8 @@ private:
bool fInList;
bool fLiteralAndIndent;
bool fResolveAndIndent;
+ bool fOddRow;
+ bool fHasDetails;
typedef ParserCommon INHERITED;
};
@@ -2272,7 +2389,7 @@ public:
}
}
- void skipToMethodEnd() {
+ void skipToMethodEnd(BmhParser::Resolvable resolvable) {
if (this->eof()) {
return;
}
@@ -2284,7 +2401,8 @@ public:
return;
}
}
- if (this->startsWith(fClassName.c_str()) || this->startsWith("operator")) {
+ if (BmhParser::Resolvable::kSimple != resolvable
+ && (this->startsWith(fClassName.c_str()) || this->startsWith("operator"))) {
const char* ptr = this->anyOf("\n (");
if (ptr && '(' == *ptr) {
this->skipToEndBracket(')');
diff --git a/tools/bookmaker/definition.cpp b/tools/bookmaker/definition.cpp
index 06db96487e..9a6d0f6d49 100644
--- a/tools/bookmaker/definition.cpp
+++ b/tools/bookmaker/definition.cpp
@@ -338,6 +338,9 @@ bool Definition::boilerplateIfDef() {
// fixme: this will need to be more complicated to handle all of Skia
// for now, just handle paint -- maybe fiddle will loosen naming restrictions
void Definition::setCanonicalFiddle() {
+ if (string::npos != fName.find("SkCanvas::SaveLayerRec")) {
+ SkDebugf("");
+ }
fMethodType = Definition::MethodType::kNone;
size_t doubleColons = fName.find("::", 0);
SkASSERT(string::npos != doubleColons);
@@ -372,6 +375,15 @@ void Definition::setCanonicalFiddle() {
size_t openParen = fName.find('(', doubleColons);
if (string::npos == openParen) {
result += fName.substr(doubleColons);
+ // see if it is a constructor -- if second to last delimited name equals last
+ size_t nextColons = fName.find("::", doubleColons);
+ if (string::npos != nextColons) {
+ nextColons += 2;
+ if (!strncmp(&fName[doubleColons], &fName[nextColons],
+ nextColons - doubleColons - 2)) {
+ fMethodType = Definition::MethodType::kConstructor;
+ }
+ }
} else {
size_t comma = fName.find(',', doubleColons);
if (string::npos == comma) {
@@ -677,7 +689,7 @@ string Definition::formatFunction(Format format) const {
lastStart = saveStart;
lastEnd = methodParser.fChar;
indent = SkTMin(indent, (size_t) (limit - maxLine));
- // write string wtih trimmmed indent
+ // write string with trimmmed indent
string methodStr;
int written = 0;
do {
@@ -804,6 +816,32 @@ bool Definition::hasMatch(string name) const {
return false;
}
+string Definition::incompleteMessage(DetailsType detailsType) const {
+ if (!IncompleteAllowed(fMarkType)) {
+ auto iter = std::find_if(fChildren.begin(), fChildren.end(),
+ [](const Definition* test) { return IncompleteAllowed(test->fMarkType); });
+ SkASSERT(fChildren.end() != iter);
+ return (*iter)->incompleteMessage(detailsType);
+ }
+ string message = MarkType::kExperimental == fMarkType ?
+ "Experimental." : "Deprecated.";
+ if (Definition::Details::kDoNotUse_Experiement == fDetails) {
+ message += " Do not use.";
+ } else if (Definition::Details::kNotReady_Experiment == fDetails) {
+ message += " Not ready for general use.";
+ } else if (Definition::Details::kSoonToBe_Deprecated == fDetails) {
+ message += " Soon to be deprecated.";
+ } else if (Definition::Details::kTestingOnly_Experiment == fDetails) {
+ message += " For testing only.";
+ }
+ if (DetailsType::kPhrase == detailsType) {
+ message = message.substr(0, message.length() - 1); // remove trailing period
+ std::replace(message.begin(), message.end(), '.', ':');
+ std::transform(message.begin(), message.end(), message.begin(), ::tolower);
+ }
+ return message;
+}
+
bool Definition::isStructOrClass() const {
if (MarkType::kStruct != fMarkType && MarkType::kClass != fMarkType) {
return false;
@@ -1084,7 +1122,7 @@ bool RootDefinition::dumpUnVisited() {
return success;
}
-const Definition* RootDefinition::find(string ref, AllowParens allowParens) const {
+Definition* RootDefinition::find(string ref, AllowParens allowParens) {
const auto leafIter = fLeaves.find(ref);
if (leafIter != fLeaves.end()) {
return &leafIter->second;
@@ -1098,12 +1136,12 @@ const Definition* RootDefinition::find(string ref, AllowParens allowParens) cons
}
const auto branchIter = fBranches.find(ref);
if (branchIter != fBranches.end()) {
- const RootDefinition* rootDef = branchIter->second;
+ RootDefinition* rootDef = branchIter->second;
return rootDef;
}
- const Definition* result = nullptr;
+ Definition* result = nullptr;
for (const auto& branch : fBranches) {
- const RootDefinition* rootDef = branch.second;
+ RootDefinition* rootDef = branch.second;
result = rootDef->find(ref, allowParens);
if (result) {
break;
diff --git a/tools/bookmaker/includeWriter.cpp b/tools/bookmaker/includeWriter.cpp
index e2ba6c67b8..c1a98cf786 100644
--- a/tools/bookmaker/includeWriter.cpp
+++ b/tools/bookmaker/includeWriter.cpp
@@ -7,6 +7,57 @@
#include "bookmaker.h"
+bool IncludeWriter::checkChildCommentLength(const Definition* parent, MarkType childType) const {
+ bool oneMember = false;
+ for (auto& item : parent->fChildren) {
+ if (childType != item->fMarkType) {
+ continue;
+ }
+ oneMember = true;
+ int lineLen = 0;
+ for (auto& itemChild : item->fChildren) {
+ if (MarkType::kExperimental == itemChild->fMarkType) {
+ lineLen = sizeof("experimental") - 1;
+ break;
+ }
+ if (MarkType::kDeprecated == itemChild->fMarkType) {
+ lineLen = sizeof("deprecated") - 1;
+ // todo: look for 'soon'
+ break;
+ }
+ if (MarkType::kLine == itemChild->fMarkType) {
+ lineLen = itemChild->length();
+ break;
+ }
+ }
+ if (!lineLen) {
+ item->reportError<void>("missing #Line");
+ }
+ if (fEnumItemCommentTab + lineLen >= 100) {
+// if too long, remove spaces until it fits, or wrap
+// item->reportError<void>("#Line comment too long");
+ }
+ }
+ return oneMember;
+}
+
+void IncludeWriter::checkEnumLengths(const Definition& child, string enumName, ItemLength* length) const {
+ const Definition* enumItem = this->matchMemberName(enumName, child);
+ if (std::any_of(enumItem->fChildren.begin(), enumItem->fChildren.end(),
+ [](Definition* child){return MarkType::kNoJustify == child->fMarkType;})) {
+ return;
+ }
+ string comment = this->enumMemberComment(enumItem, child);
+ int lineLimit = 100 - fIndent - 7; // 7: , space //!< space
+ if (length->fCurValue) {
+ lineLimit -= 3; // space = space
+ }
+ if (length->fCurName + length->fCurValue + (int) comment.length() < lineLimit) {
+ length->fLongestName = SkTMax(length->fLongestName, length->fCurName);
+ length->fLongestValue = SkTMax(length->fLongestValue, length->fCurValue);
+ }
+}
+
void IncludeWriter::constOut(const Definition* memberStart, const Definition& child,
const Definition* bmhConst) {
const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 :
@@ -33,7 +84,8 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs
bool breakOut = false;
SkDEBUGCODE(bool wroteCode = false);
if (def->fDeprecated) {
- this->writeString(def->fToBeDeprecated ? "To be deprecated soon." : "Deprecated.");
+ string message = def->incompleteMessage(Definition::DetailsType::kSentence);
+ this->writeString(message);
this->lfcr();
}
for (auto prop : def->fChildren) {
@@ -92,9 +144,10 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs
}
}
commentStart = prop->fContentStart;
- if (def->fToBeDeprecated) {
- commentStart += 4; // skip over "soon" // FIXME: this is awkward
- } else if (MarkType::kBug == prop->fMarkType) {
+ if (' ' < commentStart[0]) {
+ commentStart = strchr(commentStart, '\n');
+ }
+ if (MarkType::kBug == prop->fMarkType) {
commentStart = prop->fContentEnd;
}
commentLen = (int) (prop->fContentEnd - commentStart);
@@ -115,6 +168,9 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs
this->writeString("EXPERIMENTAL:");
this->writeSpace();
commentStart = prop->fContentStart;
+ if (' ' < commentStart[0]) {
+ commentStart = strchr(commentStart, '\n');
+ }
commentLen = (int) (prop->fContentEnd - commentStart);
if (commentLen > 0) {
if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) {
@@ -202,7 +258,41 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs
return this->reportError<void>("missing phrase definition");
}
Definition* phraseDef = iter->second;
- this->rewriteBlock(phraseDef->length(), phraseDef->fContentStart, Phrase::kYes);
+ // TODO: given TextParser(commentStart, prop->fStart + up to #) return if
+ // it ends with two of more linefeeds, ignoring other whitespace
+ Phrase defIsPhrase = '\n' == prop->fStart[0] && '\n' == prop->fStart[-1] ?
+ Phrase::kNo : Phrase::kYes;
+ if (Phrase::kNo == defIsPhrase) {
+ this->lf(2);
+ }
+ const char* start = phraseDef->fContentStart;
+ int length = phraseDef->length();
+ auto propParams = prop->fChildren.begin();
+ // can this share code or logic with mdout somehow?
+ for (auto child : phraseDef->fChildren) {
+ if (MarkType::kPhraseParam == child->fMarkType) {
+ continue;
+ }
+ int localLength = child->fStart - start;
+ this->rewriteBlock(localLength, start, defIsPhrase);
+ start += localLength;
+ length -= localLength;
+ SkASSERT(propParams != prop->fChildren.end());
+ if (fColumn > 0) {
+ this->writeSpace();
+ }
+ this->writeString((*propParams)->fName);
+ localLength = child->fContentEnd - child->fStart;
+ start += localLength;
+ length -= localLength;
+ if (isspace(start[0])) {
+ this->writeSpace();
+ }
+ defIsPhrase = Phrase::kYes;
+ }
+ if (length > 0) {
+ this->rewriteBlock(length, start, defIsPhrase);
+ }
commentStart = prop->fContentStart;
commentLen = (int) (def->fContentEnd - commentStart);
} break;
@@ -220,8 +310,7 @@ void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirs
}
}
-void IncludeWriter::enumHeaderOut(const RootDefinition* root,
- const Definition& child) {
+void IncludeWriter::enumHeaderOut(RootDefinition* root, const Definition& child) {
const Definition* enumDef = nullptr;
const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 :
child.fContentStart;
@@ -281,8 +370,7 @@ void IncludeWriter::enumHeaderOut(const RootDefinition* root,
bool lastAnchor = false;
SkDEBUGCODE(bool foundConst = false);
for (auto test : enumDef->fChildren) {
- if (MarkType::kCode == test->fMarkType) {
- SkASSERT(!codeBlock); // FIXME: check enum for correct order earlier
+ if (MarkType::kCode == test->fMarkType && !codeBlock) {
codeBlock = test;
commentStart = codeBlock->fTerminator;
continue;
@@ -316,17 +404,23 @@ void IncludeWriter::enumHeaderOut(const RootDefinition* root,
lastAnchor = false;
}
this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo);
- if (MarkType::kAnchor == test->fMarkType) {
+ if (MarkType::kAnchor == test->fMarkType || MarkType::kCode == test->fMarkType) {
bool newLine = commentEnd - commentStart > 1 &&
'\n' == commentEnd[-1] && '\n' == commentEnd[-2];
commentStart = test->fContentStart;
- commentEnd = test->fChildren[0]->fStart;
+ commentEnd = MarkType::kAnchor == test->fMarkType ? test->fChildren[0]->fStart :
+ test->fContentEnd;
if (newLine) {
this->lf(2);
} else {
this->writeSpace();
}
- this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo);
+ if (MarkType::kAnchor == test->fMarkType) {
+ this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo);
+ } else {
+ this->writeBlock((int) (commentEnd - commentStart), commentStart);
+ this->lf(2);
+ }
lastAnchor = true; // this->writeSpace();
}
commentStart = test->fTerminator;
@@ -358,176 +452,189 @@ void IncludeWriter::enumHeaderOut(const RootDefinition* root,
fEnumDef = enumDef;
}
-void IncludeWriter::enumMembersOut(const RootDefinition* root, Definition& child) {
- // iterate through include tokens and find how much remains for 1 line comments
- // put ones that fit on same line, ones that are too big on preceding line?
- const Definition* currentEnumItem = nullptr;
- const char* commentStart = nullptr;
- const char* lastEnd = nullptr;
- int commentLen = 0;
- enum class State {
- kNoItem,
- kItemName,
- kItemValue,
- kItemComment,
- };
- State state = State::kNoItem;
+const Definition* IncludeWriter::enumMemberForComment(const Definition* currentEnumItem) const {
+ for (auto constItem : currentEnumItem->fChildren) {
+ if (MarkType::kLine == constItem->fMarkType
+ || MarkType::kExperimental == constItem->fMarkType
+ || MarkType::kDeprecated == constItem->fMarkType) {
+ return constItem;
+ }
+ }
+ SkASSERT(0);
+ return nullptr;
+}
+
+string IncludeWriter::enumMemberComment(const Definition* currentEnumItem,
+ const Definition& child) const {
+ // #Const should always be followed by #Line, so description follows that
+ string shortComment;
+ for (auto constItem : currentEnumItem->fChildren) {
+ if (MarkType::kLine == constItem->fMarkType) {
+ shortComment = string(constItem->fContentStart, constItem->length());
+ break;
+ }
+ if (IncompleteAllowed(constItem->fMarkType)) {
+ shortComment = constItem->incompleteMessage(Definition::DetailsType::kPhrase);
+ }
+ }
+ if (!shortComment.length()) {
+ currentEnumItem->reportError<void>("missing #Line or #Deprecated or #Experimental");
+ }
+ return shortComment;
+}
+
+IncludeWriter::ItemState IncludeWriter::enumMemberName(
+ const Definition& child, const Definition* token, Item* item, LastItem* last,
+ const Definition** currentEnumItem) {
+ TextParser parser(fFileName, last->fStart, last->fEnd, fLineCount);
+ parser.skipWhiteSpace();
+ item->fName = string(parser.fChar, (int) (last->fEnd - parser.fChar));
+ *currentEnumItem = this->matchMemberName(item->fName, child);
+ if (token) {
+ fStart = token->fContentEnd;
+ TextParser enumLine(token->fFileName, last->fEnd, token->fContentStart, token->fLineCount);
+ const char* end = enumLine.anyOf(",}=");
+ SkASSERT(end);
+ if ('=' == *end) { // write enum value
+ last->fEnd = token->fContentEnd;
+ item->fValue = string(token->fContentStart, (int) (last->fEnd - token->fContentStart));
+ return ItemState::kValue;
+ }
+ }
+ return ItemState::kComment;
+}
+
+void IncludeWriter::enumMemberOut(const Definition* currentEnumItem, const Definition& child,
+ const Item& item, Preprocessor& preprocessor) {
+ SkASSERT(currentEnumItem);
+ string shortComment = this->enumMemberComment(currentEnumItem, child);
+ int enumItemValueTab =
+ SkTMax((int) item.fName.length() + fIndent + 1, fEnumItemValueTab); // 1: ,
+ int valueLength = item.fValue.length();
+ int assignLength = valueLength ? valueLength + 3 : 0; // 3: space = space
+ int enumItemCommentTab = SkTMax(enumItemValueTab + assignLength, fEnumItemCommentTab);
+ int trimNeeded = enumItemCommentTab + shortComment.length() - (100 - (sizeof("//!< ") - 1));
+ bool crAfterName = false;
+ if (trimNeeded > 0) {
+ if (item.fValue.length()) {
+ int valueSpare = SkTMin(trimNeeded, // 3 below: space = space
+ (int) (enumItemCommentTab - enumItemValueTab - item.fValue.length() - 3));
+ SkASSERT(valueSpare >= 0);
+ trimNeeded -= valueSpare;
+ enumItemCommentTab -= valueSpare;
+ }
+ if (trimNeeded > 0) {
+ int nameSpare = SkTMin(trimNeeded, (int) (enumItemValueTab - item.fName.length()
+ - fIndent - 1)); // 1: ,
+ SkASSERT(nameSpare >= 0);
+ trimNeeded -= nameSpare;
+ enumItemValueTab -= nameSpare;
+ enumItemCommentTab -= nameSpare;
+ }
+ if (trimNeeded > 0) {
+ crAfterName = true;
+ if (!valueLength) {
+ this->enumMemberForComment(currentEnumItem)->reportError<void>("comment too long");
+ } else if (valueLength + fIndent + 8 + shortComment.length() > // 8: addtional indent
+ 100 - (sizeof(", //!< ") - 1)) { // -1: zero-terminated string
+ this->enumMemberForComment(currentEnumItem)->reportError<void>("comment 2 long");
+ } // 2: = space
+ enumItemValueTab = fEnumItemValueTab + 2 // 2: , space
+ - SkTMax(0, fEnumItemValueTab + 2 + valueLength + 2 - fEnumItemCommentTab);
+ enumItemCommentTab = SkTMax(enumItemValueTab + valueLength + 2, fEnumItemCommentTab);
+ }
+ }
+ this->lfcr();
+ this->writeString(item.fName);
+ int saveIndent = fIndent;
+ if (item.fValue.length()) {
+ if (!crAfterName) {
+ this->indentToColumn(enumItemValueTab);
+ } else {
+ this->writeSpace();
+ }
+ this->writeString("=");
+ if (crAfterName) {
+ this->lfcr();
+ fIndent = enumItemValueTab;
+ } else {
+ this->writeSpace();
+ }
+ this->writeString(item.fValue);
+ }
+ this->writeString(",");
+ this->indentToColumn(enumItemCommentTab);
+ this->writeString("//!<");
+ this->writeSpace();
+ this->rewriteBlock(shortComment.length(), shortComment.c_str(), Phrase::kYes);
+ this->lfcr();
+ fIndent = saveIndent;
+ if (preprocessor.fStart) {
+ SkASSERT(preprocessor.fEnd);
+ int saveIndent = fIndent;
+ fIndent = SkTMax(0, fIndent - 8);
+ this->lf(2);
+ this->writeBlock(
+ (int) (preprocessor.fEnd - preprocessor.fStart), preprocessor.fStart);
+ this->lfcr();
+ fIndent = saveIndent;
+ preprocessor.reset();
+ }
+}
+
+// iterate through include tokens and find how much remains for 1 line comments
+// put ones that fit on same line, ones that are too big wrap
+void IncludeWriter::enumMembersOut(Definition& child) {
+ ItemState state = ItemState::kNone;
+ const Definition* currentEnumItem;
+ LastItem last = { nullptr, nullptr };
+ auto brace = child.fChildren[0];
+ if (KeyWord::kClass == brace->fKeyWord) {
+ brace = brace->fChildren[0];
+ }
+ SkASSERT(Bracket::kBrace == brace->fBracket);
vector<IterState> iterStack;
- iterStack.emplace_back(child.fTokens.begin(), child.fTokens.end());
+ iterStack.emplace_back(brace->fTokens.begin(), brace->fTokens.end());
IterState* iterState = &iterStack[0];
Preprocessor preprocessor;
- for (int onePast = 0; onePast < 2; onePast += iterState->fDefIter == iterState->fDefEnd) {
- Definition* token = onePast ? nullptr : &*iterState->fDefIter++;
- if (this->enumPreprocessor(token, MemberPass::kOut, iterStack, &iterState,
+ Item item;
+ while (iterState->fDefIter != iterState->fDefEnd) {
+ auto& token = *iterState->fDefIter++;
+ if (this->enumPreprocessor(&token, MemberPass::kOut, iterStack, &iterState,
&preprocessor)) {
continue;
}
- if (token && State::kItemName == state) {
- TextParser enumLine(token->fFileName, lastEnd,
- token->fContentStart, token->fLineCount);
- const char* end = enumLine.anyOf(",}=");
- SkASSERT(end);
- state = '=' == *end ? State::kItemValue : State::kItemComment;
- if (State::kItemValue == state) { // write enum value
- this->indentToColumn(fEnumItemValueTab);
- this->writeString("=");
- this->writeSpace();
- lastEnd = token->fContentEnd;
- this->writeBlock((int) (lastEnd - token->fContentStart),
- token->fContentStart); // write const value if any
- continue;
- }
+ if (ItemState::kName == state) {
+ state = this->enumMemberName(child, &token, &item, &last, &currentEnumItem);
}
- if (token && State::kItemValue == state) {
- TextParser valueEnd(token->fFileName, lastEnd,
- token->fContentStart, token->fLineCount);
+ if (ItemState::kValue == state) {
+ TextParser valueEnd(token.fFileName, last.fEnd, token.fContentStart, token.fLineCount);
const char* end = valueEnd.anyOf(",}");
if (!end) { // write expression continuation
- if (' ' == lastEnd[0]) {
- this->writeSpace();
- }
- this->writeBlock((int) (token->fContentEnd - lastEnd), lastEnd);
- continue;
- }
- }
- if (State::kNoItem != state) {
- this->writeString(",");
- SkASSERT(currentEnumItem);
- if (currentEnumItem->fShort) {
- this->indentToColumn(fEnumItemCommentTab);
- if (commentLen || currentEnumItem->fDeprecated) {
- this->writeString("//!<");
- this->writeSpace();
- if (currentEnumItem->fDeprecated) {
- this->writeString(child.fToBeDeprecated ? "to be deprecated soon"
- : "deprecated");
- } else {
- this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
- }
- }
- }
- if (onePast) {
- fIndent -= 4;
- }
- this->lfcr();
- if (preprocessor.fStart) {
- SkASSERT(preprocessor.fEnd);
- int saveIndent = fIndent;
- fIndent = SkTMax(0, fIndent - 8);
- this->lf(2);
- this->writeBlock(
- (int) (preprocessor.fEnd - preprocessor.fStart), preprocessor.fStart);
- this->lfcr();
- fIndent = saveIndent;
- preprocessor.reset();
- }
- if (token && State::kItemValue == state) {
- fStart = token->fContentStart;
- }
- state = State::kNoItem;
- }
- SkASSERT(State::kNoItem == state);
- if (onePast) {
- break;
- }
- SkASSERT(token);
- string itemName;
- if (!fEnumDef->isRoot()) {
- itemName = root->fName + "::";
- if (KeyWord::kClass == child.fParent->fKeyWord) {
- itemName += child.fParent->fName + "::";
- }
- }
- itemName += string(token->fContentStart, (int) (token->fContentEnd - token->fContentStart));
- for (auto& enumItem : fEnumDef->fChildren) {
- if (MarkType::kConst != enumItem->fMarkType) {
- continue;
- }
- if (itemName != enumItem->fName) {
+ item.fValue += string(last.fEnd, (int) (token.fContentEnd - last.fEnd));
continue;
}
- currentEnumItem = enumItem;
- break;
- }
- SkASSERT(currentEnumItem);
- // if description fits, it goes after item
- commentStart = currentEnumItem->fContentStart;
- const char* commentEnd;
- if (currentEnumItem->fChildren.size() > 0) {
- commentEnd = currentEnumItem->fChildren[0]->fStart;
- } else {
- commentEnd = currentEnumItem->fContentEnd;
- }
- TextParser enumComment(fFileName, commentStart, commentEnd, currentEnumItem->fLineCount);
- bool isDeprecated = false;
- if (enumComment.skipToLineStart()) { // skip const value
- commentStart = enumComment.fChar;
- commentLen = (int) (commentEnd - commentStart);
- } else {
- const Definition* childDef = currentEnumItem->fChildren[0];
- isDeprecated = MarkType::kDeprecated == childDef->fMarkType;
- if (MarkType::kPrivate == childDef->fMarkType || isDeprecated) {
- commentStart = childDef->fContentStart;
- if (currentEnumItem->fToBeDeprecated) {
- SkASSERT(isDeprecated);
- commentStart += 4; // skip over "soon" // FIXME: this is awkward
- }
- commentLen = (int) (childDef->fContentEnd - commentStart);
- }
}
- // FIXME: may assert here if there's no const value
- // should have detected and errored on that earlier when enum fContentStart was set
- SkASSERT((commentLen > 0 && commentLen < 1000) || isDeprecated);
- if (!currentEnumItem->fShort) {
- this->writeCommentHeader();
- fIndent += 4;
- bool wroteLineFeed = false;
- if (isDeprecated) {
- this->writeString(currentEnumItem->fToBeDeprecated
- ? "To be deprecated soon." : "Deprecated.");
- }
- TextParserSave save(this);
- this->setForErrorReporting(currentEnumItem, commentStart);
- wroteLineFeed = Wrote::kLF ==
- this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
- save.restore();
- fIndent -= 4;
- if (wroteLineFeed || fColumn > 100 - 3 /* space * / */ ) {
- this->lfcr();
- } else {
- this->writeSpace();
- }
- this->writeCommentTrailer();
+ if (ItemState::kNone != state) {
+ this->enumMemberOut(currentEnumItem, child, item, preprocessor);
+ fStart = token.fContentStart;
+ state = ItemState::kNone;
+ last.fStart = nullptr;
}
- lastEnd = token->fContentEnd;
- this->lfcr();
- if (',' == fStart[0]) {
- ++fStart;
+ SkASSERT(ItemState::kNone == state);
+ if (!last.fStart) {
+ last.fStart = fStart;
}
- this->writeBlock((int) (lastEnd - fStart), fStart); // enum item name
- fStart = token->fContentEnd;
- state = State::kItemName;
+ last.fEnd = token.fContentEnd;
+ state = ItemState::kName;
+ }
+ if (ItemState::kName == state) {
+ state = this->enumMemberName(child, nullptr, &item, &last, &currentEnumItem);
+ }
+ if (ItemState::kValue == state || ItemState::kComment == state) {
+ this->enumMemberOut(currentEnumItem, child, item, preprocessor);
}
+ fIndent -= 4;
}
bool IncludeWriter::enumPreprocessor(Definition* token, MemberPass pass,
@@ -590,18 +697,9 @@ bool IncludeWriter::enumPreprocessor(Definition* token, MemberPass pass,
}
void IncludeWriter::enumSizeItems(const Definition& child) {
- enum class State {
- kNoItem,
- kItemName,
- kItemValue,
- kItemComment,
- };
- State state = State::kNoItem;
- int longestName = 0;
- int longestValue = 0;
- int valueLen = 0;
+ ItemState state = ItemState::kNone;
+ ItemLength lengths = { 0, 0, 0, 0 };
const char* lastEnd = nullptr;
-// SkASSERT(child.fChildren.size() == 1 || child.fChildren.size() == 2);
auto brace = child.fChildren[0];
if (KeyWord::kClass == brace->fKeyWord) {
brace = brace->fChildren[0];
@@ -611,70 +709,85 @@ void IncludeWriter::enumSizeItems(const Definition& child) {
iterStack.emplace_back(brace->fTokens.begin(), brace->fTokens.end());
IterState* iterState = &iterStack[0];
Preprocessor preprocessor;
+ string enumName;
while (iterState->fDefIter != iterState->fDefEnd) {
auto& token = *iterState->fDefIter++;
if (this->enumPreprocessor(&token, MemberPass::kCount, iterStack, &iterState,
&preprocessor)) {
continue;
}
- if (State::kItemName == state) {
- TextParser enumLine(token.fFileName, lastEnd,
- token.fContentStart, token.fLineCount);
+ if (ItemState::kName == state) {
+ TextParser enumLine(token.fFileName, lastEnd, token.fContentStart, token.fLineCount);
const char* end = enumLine.anyOf(",}=");
SkASSERT(end);
- state = '=' == *end ? State::kItemValue : State::kItemComment;
- if (State::kItemValue == state) {
- valueLen = (int) (token.fContentEnd - token.fContentStart);
+ state = '=' == *end ? ItemState::kValue : ItemState::kComment;
+ if (ItemState::kValue == state) {
lastEnd = token.fContentEnd;
+ lengths.fCurValue = (int) (lastEnd - token.fContentStart);
continue;
}
}
- if (State::kItemValue == state) {
- TextParser valueEnd(token.fFileName, lastEnd,
- token.fContentStart, token.fLineCount);
+ if (ItemState::kValue == state) {
+ TextParser valueEnd(token.fFileName, lastEnd, token.fContentStart, token.fLineCount);
const char* end = valueEnd.anyOf(",}");
if (!end) { // write expression continuation
- valueLen += (int) (token.fContentEnd - lastEnd);
+ lengths.fCurValue += (int) (token.fContentEnd - lastEnd);
continue;
}
}
- if (State::kNoItem != state) {
- longestValue = SkTMax(longestValue, valueLen);
- state = State::kNoItem;
+ if (ItemState::kNone != state) {
+ this->checkEnumLengths(child, enumName, &lengths);
+ lengths.fCurValue = 0;
+ state = ItemState::kNone;
}
- SkASSERT(State::kNoItem == state);
+ SkASSERT(ItemState::kNone == state);
lastEnd = token.fContentEnd;
- longestName = SkTMax(longestName, (int) (lastEnd - token.fContentStart));
- state = State::kItemName;
+ lengths.fCurName = (int) (lastEnd - token.fContentStart);
+ enumName = string(token.fContentStart, lengths.fCurName);
+ state = ItemState::kName;
}
- if (State::kItemValue == state) {
- longestValue = SkTMax(longestValue, valueLen);
+ if (ItemState::kNone != state) {
+ this->checkEnumLengths(child, enumName, &lengths);
}
- fEnumItemValueTab = longestName + fIndent + 1 /* space before = */ ;
- if (longestValue) {
- longestValue += 3; /* = space , */
+ fEnumItemValueTab = lengths.fLongestName + fIndent + 1 /* 1: , */ ;
+ if (lengths.fLongestValue) {
+ lengths.fLongestValue += 3; // 3: space = space
}
- fEnumItemCommentTab = fEnumItemValueTab + longestValue + 1 /* space before //!< */ ;
+ fEnumItemCommentTab = fEnumItemValueTab + lengths.fLongestValue + 1 ; // 1: space before //!<
// iterate through bmh children and see which comments fit on include lines
- for (auto& enumItem : fEnumDef->fChildren) {
- if (MarkType::kConst != enumItem->fMarkType) {
+ if (!this->checkChildCommentLength(fEnumDef, MarkType::kConst)) {
+ fEnumDef->reportError<void>("expected at least one #Const in #Enum");
+ }
+}
+
+const Definition* IncludeWriter::matchMemberName(string matchName, const Definition& child) const {
+ const Definition* parent = &child;
+ if (KeyWord::kEnum == child.fKeyWord && child.fChildren.size() > 0
+ && KeyWord::kClass == child.fChildren[0]->fKeyWord) {
+ matchName = child.fChildren[0]->fName + "::" + matchName;
+ }
+ do {
+ if (KeyWord::kStruct == parent->fKeyWord || KeyWord::kClass == parent->fKeyWord) {
+ matchName = parent->fName + "::" + matchName;
+ }
+ } while ((parent = parent->fParent));
+ const Definition* enumItem = nullptr;
+ for (auto testItem : fEnumDef->fChildren) {
+ if (MarkType::kConst != testItem->fMarkType) {
continue;
}
- TextParser enumLine(enumItem);
- enumLine.trimEnd();
- enumLine.skipToLineStart(); // skip const value
- const char* commentStart = enumLine.fChar;
- enumLine.skipLine();
- ptrdiff_t lineLen = enumLine.fChar - commentStart + 5 /* //!< space */ ;
- if (!enumLine.eof()) {
- enumLine.skipWhiteSpace();
+ if (matchName != testItem->fName) {
+ continue;
}
- enumItem->fShort = enumLine.eof() && fEnumItemCommentTab + lineLen < 100;
+ enumItem = testItem;
+ break;
}
+ SkASSERT(enumItem);
+ return enumItem;
}
// walk children and output complete method doxygen description
-void IncludeWriter::methodOut(const Definition* method, const Definition& child) {
+void IncludeWriter::methodOut(Definition* method, const Definition& child) {
if (fPendingMethod) {
fIndent -= 4;
fPendingMethod = false;
@@ -683,7 +796,7 @@ void IncludeWriter::methodOut(const Definition* method, const Definition& child)
fMethodDef = &child;
fContinuation = nullptr;
fDeferComment = nullptr;
- const Definition* csParent = method->csParent();
+ Definition* csParent = method->csParent();
if (csParent && (0 == fIndent || fIndentNext)) {
fIndent += 4;
fIndentNext = false;
@@ -745,7 +858,7 @@ void IncludeWriter::structOut(const Definition* root, const Definition& child,
fIndent += 4;
this->lfcr();
if (child.fDeprecated) {
- this->writeString(child.fToBeDeprecated ? "to be deprecated soon" : "deprecated");
+ this->writeString(child.incompleteMessage(Definition::DetailsType::kSentence));
} else {
this->rewriteBlock((int)(commentEnd - commentStart), commentStart, Phrase::kNo);
}
@@ -805,6 +918,7 @@ Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const
if (!commentBlock) {
return memberStart->reportError<Definition*>("member missing comment block");
}
+#if 0
if (!commentBlock->fShort) {
const char* commentStart = commentBlock->fContentStart;
ptrdiff_t commentLen = commentBlock->fContentEnd - commentStart;
@@ -831,6 +945,7 @@ Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const
}
this->writeCommentTrailer();
}
+#endif
this->lfcr();
this->writeBlock((int) (child.fStart - memberStart->fContentStart),
memberStart->fContentStart);
@@ -852,7 +967,7 @@ Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const
valueStart->fContentStart);
}
this->writeString(";");
- if (commentBlock->fShort) {
+ /* if (commentBlock->fShort) */ {
this->indentToColumn(fStructCommentTab);
this->writeString("//!<");
this->writeSpace();
@@ -863,29 +978,6 @@ Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const
return valueEnd;
}
-// iterate through bmh children and see which comments fit on include lines
-void IncludeWriter::structSetMembersShort(const vector<Definition*>& bmhChildren) {
- for (auto memberDef : bmhChildren) {
- if (MarkType::kMember != memberDef->fMarkType) {
- continue;
- }
- string extract = fBmhParser->extractText(memberDef, BmhParser::TrimExtract::kYes);
- bool multiline = string::npos != extract.find('\n');
- if (multiline) {
- memberDef->fShort = false;
- } else {
- ptrdiff_t lineLen = extract.length() + 5 /* //!< space */ ;
- memberDef->fShort = fStructCommentTab + lineLen < 100;
- }
- }
- for (auto memberDef : bmhChildren) {
- if (MarkType::kSubtopic != memberDef->fMarkType && MarkType::kTopic != memberDef->fMarkType) {
- continue;
- }
- this->structSetMembersShort(memberDef->fChildren);
- }
-}
-
void IncludeWriter::structSizeMembers(const Definition& child) {
int longestType = 0;
Definition* typeStart = nullptr;
@@ -996,8 +1088,9 @@ void IncludeWriter::structSizeMembers(const Definition& child) {
fStructCommentTab += longestValue + 3 /* space = space */ ;
fStructValueTab -= 1 /* ; */ ;
}
- // iterate through bmh children and see which comments fit on include lines
- this->structSetMembersShort(fBmhStructDef->fChildren);
+ // iterate through struct to ensure that members' comments fit on line
+ // struct or class may not have any members
+ (void) this->checkChildCommentLength(fBmhStructDef, MarkType::kMember);
}
static bool find_start(const Definition* startDef, const char* start) {
@@ -1026,8 +1119,8 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti
// skip include comment
// if there is a series of same named methods, write one set of comments, then write all methods
string methodName;
- const Definition* method = nullptr;
- const Definition* clonedMethod = nullptr;
+ Definition* method = nullptr;
+ Definition* clonedMethod = nullptr;
const Definition* memberStart = nullptr;
const Definition* memberEnd = nullptr;
fContinuation = nullptr;
@@ -1285,8 +1378,8 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti
if (fInStruct) {
// try child; root+child; root->parent+child; etc.
int trial = 0;
- const RootDefinition* search = root;
- const Definition* parent = search->fParent;
+ RootDefinition* search = root;
+ Definition* parent = search->fParent;
do {
string name;
if (0 == trial) {
@@ -1301,7 +1394,7 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti
}
fBmhStructDef = search->find(name, RootDefinition::AllowParens::kNo);
} while (!fBmhStructDef && ++trial);
- root = const_cast<RootDefinition*>(fBmhStructDef->asRoot());
+ root = fBmhStructDef->asRoot();
SkASSERT(root);
fIndent += 4;
this->structSizeMembers(child);
@@ -1472,7 +1565,7 @@ bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefiniti
(KeyWord::kClass == child.fParent->fKeyWord && child.fParent->fParent &&
KeyWord::kEnum == child.fParent->fParent->fKeyWord)) {
SkASSERT(Bracket::kBrace == child.fBracket);
- this->enumMembersOut(root, child);
+ this->enumMembersOut(*child.fParent);
this->writeString("};");
this->lf(2);
startDef = child.fParent;
@@ -2095,6 +2188,7 @@ IncludeWriter::Wrote IncludeWriter::rewriteBlock(int size, const char* data, Phr
case 'z':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
+ case '%': // to do : ensure that preceding is a number
case '-':
switch (word) {
case Word::kStart:
diff --git a/tools/bookmaker/mdOut.cpp b/tools/bookmaker/mdOut.cpp
index db6a8cf7d6..7f07bc2d3e 100644
--- a/tools/bookmaker/mdOut.cpp
+++ b/tools/bookmaker/mdOut.cpp
@@ -16,6 +16,155 @@
} \
fprintf(fOut, __VA_ARGS__)
+const char* SubtopicKeys::kGeneratedSubtopics[] = {
+ kClasses, kConstants, kConstructors, kDefines,
+ kMemberFunctions, kMembers, kOperators, kRelatedFunctions, kStructs, kTypedefs,
+};
+
+const char* kConstTableStyle =
+"<style>" "\n"
+ ".td_const td, th { border: 2px solid #dddddd; text-align: left; padding: 8px; }" "\n"
+ ".tr_const tr:nth-child(even) { background-color: #f0f0f0; }" "\n"
+ ".td2_const td:first-child + td { text-align: center; }" "\n"
+"</style>" "\n";
+
+const char* kTableDeclaration = "<table style='border-collapse: collapse; width: 62.5em'>";
+
+#define kTD_Base "border: 2px solid #dddddd; padding: 8px; "
+#define kTH_Left "<th style='text-align: left; " kTD_Base "'>"
+#define kTH_Center "<th style='text-align: center; " kTD_Base "'>"
+
+string kTD_Left = " <td style='text-align: left; " kTD_Base "'>";
+string kTD_Center = " <td style='text-align: center; " kTD_Base "'>";
+string kTR_Dark = " <tr style='background-color: #f0f0f0; '>";
+
+const char* kAllConstTableHeader = " <tr>" kTH_Left "Const</th>" "\n"
+ kTH_Center "Value</th>" "\n"
+ kTH_Left "Description</th>" "</tr>";
+const char* kSubConstTableHeader = " <tr>" kTH_Left "Const</th>" "\n"
+ kTH_Center "Value</th>" "\n"
+ kTH_Left "Details</th>" "\n"
+ kTH_Left "Description</th>" "</tr>";
+const char* kAllMemberTableHeader = " <tr>" kTH_Left "Type</th>" "\n"
+ kTH_Left "Name</th>" "\n"
+ kTH_Left "Description</th>" "</tr>";
+const char* kSubMemberTableHeader = " <tr>" kTH_Left "Type</th>" "\n"
+ kTH_Left "Name</th>" "\n"
+ kTH_Left "Details</th>" "\n"
+ kTH_Left "Description</th>" "</tr>";
+const char* kTopicsTableHeader = " <tr>" kTH_Left "Topic</th>" "\n"
+ kTH_Left "Description</th>" "</tr>";
+
+static string html_file_name(string bmhFileName) {
+ SkASSERT("docs" == bmhFileName.substr(0, 4));
+ SkASSERT('\\' == bmhFileName[4] || '/' == bmhFileName[4]);
+ SkASSERT(".bmh" == bmhFileName.substr(bmhFileName.length() - 4));
+ string result = bmhFileName.substr(5, bmhFileName.length() - 4 - 5);
+ return result;
+}
+
+string MdOut::anchorDef(string str, string name) {
+ if (fValidate) {
+ string htmlName = html_file_name(fFileName);
+ vector<AnchorDef>& allDefs = fAllAnchorDefs[htmlName];
+ if (!std::any_of(allDefs.begin(), allDefs.end(),
+ [str](AnchorDef compare) { return compare.fDef == str; } )) {
+ MarkType markType = fLastDef->fMarkType;
+ if (MarkType::kMethod == markType
+ && std::any_of(fLastDef->fChildren.begin(), fLastDef->fChildren.end(),
+ [](const Definition* compare) {
+ return IncompleteAllowed(compare->fMarkType); } )) {
+ markType = MarkType::kDeprecated;
+ }
+ if (MarkType::kMethod == markType && fLastDef->fClone) {
+ markType = MarkType::kDeprecated; // TODO: hack to allow missing reference
+ }
+ allDefs.push_back( { str, markType } );
+ }
+ }
+ return "<a name='" + str + "'>" + name + "</a>";
+}
+
+string MdOut::anchorRef(string ref, string name) {
+ if (fValidate) {
+ string htmlName;
+ size_t hashIndex = ref.find('#');
+ if (string::npos != hashIndex && "https://" != ref.substr(0, 8)) {
+ if (0 == hashIndex) {
+ htmlName = html_file_name(fFileName);
+ } else {
+ htmlName = ref.substr(0, hashIndex);
+ }
+ vector<string>& allRefs = fAllAnchorRefs[htmlName];
+ string refPart = ref.substr(hashIndex + 1);
+ if (allRefs.end() == std::find(allRefs.begin(), allRefs.end(), refPart)) {
+ allRefs.push_back(refPart);
+ }
+ }
+ }
+ SkASSERT(string::npos != ref.find('#') || string::npos != ref.find("https://"));
+ return "<a href='" + ref + "'>" + name + "</a>";
+}
+
+string MdOut::anchorLocalRef(string ref, string name) {
+ return this->anchorRef("#" + ref, name);
+}
+
+string MdOut::tableDataCodeRef(string ref, string name) {
+ return kTD_Left + this->anchorRef(ref, "<code>" + name + "</code>") + "</td>";
+}
+
+string MdOut::tableDataCodeLocalRef(string ref, string name) {
+ return this->tableDataCodeRef("#" + ref, name);
+}
+
+string MdOut::tableDataCodeLocalRef(string name) {
+ return this->tableDataCodeLocalRef(name, name);
+}
+
+string MdOut::tableDataCodeRef(const Definition* ref) {
+ return this->tableDataCodeLocalRef(ref->fFiddle, ref->fName);
+}
+
+string MdOut::tableDataCodeDef(string def, string name) {
+ return kTD_Left + this->anchorDef(def, "<code>" + name + "</code>") + "</td>";
+}
+
+string MdOut::tableDataCodeDef(const Definition* def) {
+ return this->tableDataCodeDef(def->fFiddle, def->fName);
+}
+
+static string table_data_const(const Definition* def, const char** textStartPtr) {
+ TextParser parser(def);
+ SkAssertResult(parser.skipToEndBracket('\n'));
+ string constant = string(def->fContentStart, (int) (parser.fChar - def->fContentStart));
+ if (textStartPtr) {
+ *textStartPtr = parser.fChar;
+ }
+ return kTD_Center + constant + "</td>";
+}
+
+static string out_table_data_description_start() {
+ return kTD_Left;
+}
+
+static string out_table_data_description(string str) {
+ return kTD_Left + str + "</td>";
+}
+
+static string out_table_data_description(const Definition* def) {
+ return out_table_data_description(string(def->fContentStart,
+ (int) (def->fContentEnd - def->fContentStart)));
+}
+
+static string out_table_data_details(string details) {
+ return kTD_Left + details + "</td>";
+}
+
+#undef kConstTDBase
+#undef kTH_Left
+#undef kTH_Center
+
static void add_ref(string leadingSpaces, string ref, string* result) {
*result += leadingSpaces + ref;
}
@@ -43,6 +192,92 @@ static bool all_lower(string ref) {
return true;
}
+// from https://stackoverflow.com/questions/3418231/replace-part-of-a-string-with-another-string
+void replace_all(string& str, const string& from, const string& to) {
+ SkASSERT(!from.empty());
+ size_t start_pos = 0;
+ while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
+ str.replace(start_pos, from.length(), to);
+ start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
+ }
+}
+
+// detail strings are preceded by an example comment to check readability
+void MdOut::addPopulators() {
+ fPopulators[SubtopicKeys::kClasses].fName = "Class Declarations";
+ fPopulators[SubtopicKeys::kClasses].fOneLiner = "embedded class members";
+ fPopulators[SubtopicKeys::kClasses].fDetails =
+ /* SkImageInfo */ "uses C++ classes to declare the public data"
+ " structures and interfaces.";
+ fPopulators[SubtopicKeys::kConstants].fName = "Constants";
+ fPopulators[SubtopicKeys::kConstants].fOneLiner = "enum and enum class, and their const values";
+ fPopulators[SubtopicKeys::kConstants].fDetails =
+ /* SkImageInfo */ "related constants are defined by <code>enum</code>,"
+ " <code>enum class</code>, <code>#define</code>, <code>const</code>,"
+ " and <code>constexpr</code>.";
+ fPopulators[SubtopicKeys::kConstructors].fName = "Constructors";
+ fPopulators[SubtopicKeys::kConstructors].fOneLiner = "functions that construct";
+ fPopulators[SubtopicKeys::kConstructors].fDetails =
+ /* SkImageInfo */ "can be constructed or initialized by these functions,"
+ " including C++ class constructors.";
+ fPopulators[SubtopicKeys::kDefines].fName = "Defines";
+ fPopulators[SubtopicKeys::kDefines].fOneLiner = "preprocessor definitions of functions, values";
+ fPopulators[SubtopicKeys::kDefines].fDetails =
+ /* SkImageInfo */ "uses preprocessor definitions to inline code and constants,"
+ " and to abstract platform-specific functionality.";
+ fPopulators[SubtopicKeys::kMemberFunctions].fName = "Functions";
+ fPopulators[SubtopicKeys::kMemberFunctions].fOneLiner = "global and class member functions";
+ fPopulators[SubtopicKeys::kMemberFunctions].fDetails =
+ /* SkImageInfo */ "member functions read and modify the structure properties.";
+ fPopulators[SubtopicKeys::kMembers].fName = "Members";
+ fPopulators[SubtopicKeys::kMembers].fOneLiner = "member values";
+ fPopulators[SubtopicKeys::kMembers].fDetails =
+ /* SkImageInfo */ "members may be read and written directly without using"
+ " a member function.";
+ fPopulators[SubtopicKeys::kOperators].fName = "Operators";
+ fPopulators[SubtopicKeys::kOperators].fOneLiner = "operator overloading methods";
+ fPopulators[SubtopicKeys::kOperators].fDetails =
+ /* SkImageInfo */ "operators inline class member functions with arithmetic"
+ " equivalents.";
+ fPopulators[SubtopicKeys::kRelatedFunctions].fName = "Related Functions";
+ fPopulators[SubtopicKeys::kRelatedFunctions].fOneLiner =
+ "similar member functions grouped together";
+ fPopulators[SubtopicKeys::kRelatedFunctions].fDetails =
+ /* SkImageInfo */ "global, <code>struct</code>, and <code>class</code> related member"
+ " functions share a topic.";
+ fPopulators[SubtopicKeys::kStructs].fName = "Struct Declarations";
+ fPopulators[SubtopicKeys::kStructs].fOneLiner = "embedded struct members";
+ fPopulators[SubtopicKeys::kStructs].fDetails =
+ /* SkImageInfo */ "uses C++ structs to declare the public data"
+ " structures and interfaces.";
+ fPopulators[SubtopicKeys::kTypedefs].fName = "Typedef Declarations";
+ fPopulators[SubtopicKeys::kTypedefs].fOneLiner = "types defined by other types";
+ fPopulators[SubtopicKeys::kTypedefs].fDetails =
+ /* SkImageInfo */ " <code>typedef</code> define a data type.";
+}
+
+Definition* MdOut::checkParentsForMatch(Definition* test, string ref) const {
+ bool isSubtopic = MarkType::kSubtopic == test->fMarkType
+ || MarkType::kTopic == test->fMarkType;
+ do {
+ if (!test->isRoot()) {
+ continue;
+ }
+ bool localTopic = MarkType::kSubtopic == test->fMarkType
+ || MarkType::kTopic == test->fMarkType;
+ if (localTopic != isSubtopic) {
+ continue;
+ }
+ string prefix(isSubtopic ? "_" : "::");
+ RootDefinition* root = test->asRoot();
+ string prefixed = root->fName + prefix + ref;
+ if (Definition* def = root->find(prefixed, RootDefinition::AllowParens::kYes)) {
+ return def;
+ }
+ } while ((test = test->fParent));
+ return nullptr;
+}
+
// FIXME: preserve inter-line spaces and don't add new ones
string MdOut::addReferences(const char* refStart, const char* refEnd,
BmhParser::Resolvable resolvable) {
@@ -84,7 +319,7 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
} else {
leadingSpaces = string(base, wordStart - base);
}
- t.skipToMethodEnd();
+ t.skipToMethodEnd(resolvable);
if (base == t.fChar) {
if (!t.eof() && '~' == base[0] && !isalnum(base[1])) {
t.next();
@@ -99,13 +334,18 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
continue;
}
ref = string(start, t.fChar - start);
+ if (fMethod && "SkSurface::MakeRenderTarget" == fMethod->fName && "is" == ref) {
+ SkDebugf("");
+ }
if (const Definition* def = this->isDefined(t, ref, resolvable)) {
if (MarkType::kExternal == def->fMarkType) {
+ (void) this->anchorRef("undocumented#" + ref, ""); // for anchor validate
add_ref(leadingSpaces, ref, &result);
continue;
}
SkASSERT(def->fFiddle.length());
- if (!t.eof() && '(' == t.peek() && t.strnchr(')', t.fEnd)) {
+ if (BmhParser::Resolvable::kSimple != resolvable
+ && !t.eof() && '(' == t.peek() && t.strnchr(')', t.fEnd)) {
if (!t.skipToEndBracket(')')) {
t.reportError("missing close paren");
fAddRefFailed = true;
@@ -226,8 +466,9 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
}
}
}
- if (BmhParser::Resolvable::kOut != resolvable &&
- BmhParser::Resolvable::kFormula != resolvable) {
+ if (BmhParser::Resolvable::kSimple != resolvable
+ && BmhParser::Resolvable::kOut != resolvable
+ && BmhParser::Resolvable::kFormula != resolvable) {
t.reportError("missed camelCase");
fAddRefFailed = true;
return result;
@@ -259,37 +500,25 @@ string MdOut::addReferences(const char* refStart, const char* refEnd,
continue;
}
}
- const Definition* test = fRoot;
- do {
- if (!test->isRoot()) {
- continue;
- }
- for (string prefix : { "_", "::" } ) {
- const RootDefinition* root = test->asRoot();
- string prefixed = root->fName + prefix + ref;
- if (const Definition* def = root->find(prefixed,
- RootDefinition::AllowParens::kYes)) {
- result += linkRef(leadingSpaces, def, ref, resolvable);
- goto found;
- }
- }
- } while ((test = test->fParent));
- found:
- if (!test) {
- if (BmhParser::Resolvable::kOut != resolvable &&
- BmhParser::Resolvable::kFormula != resolvable) {
- t.reportError("undefined reference");
- fAddRefFailed = true;
- } else {
- add_ref(leadingSpaces, ref, &result);
- }
+ Definition* def = this->checkParentsForMatch(fSubtopic, ref);
+ if (!def) {
+ def = this->checkParentsForMatch(fRoot, ref);
+ }
+ if (def) {
+ result += this->linkRef(leadingSpaces, def, ref, resolvable);
+ continue;
+ }
+ if (BmhParser::Resolvable::kOut != resolvable &&
+ BmhParser::Resolvable::kFormula != resolvable) {
+ t.reportError("undefined reference");
+ fAddRefFailed = true;
+ } else {
+ add_ref(leadingSpaces, ref, &result);
}
} while (!t.eof());
return result;
}
-
-
bool MdOut::buildReferences(const IncludeParser& includeParser, const char* docDir,
const char* mdFileOrPath) {
if (!sk_isdir(mdFileOrPath)) {
@@ -362,12 +591,7 @@ bool MdOut::buildRefFromFile(const char* name, const char* outDir) {
if (topicDef->fParent) {
continue;
}
- if (!topicDef->isRoot()) {
- fAddRefFailed = true;
- return this->reportError<bool>("expected root topic");
- }
- fRoot = topicDef->asRoot();
- if (string::npos == fRoot->fFileName.rfind(match)) {
+ if (string::npos == topicDef->fFileName.rfind(match)) {
continue;
}
if (!fOut) {
@@ -381,6 +605,9 @@ bool MdOut::buildRefFromFile(const char* name, const char* outDir) {
SkDebugf("could not open output file %s\n", fullName.c_str());
return false;
}
+ if (false) { // try inlining the style
+ FPRINTF("%s", kConstTableStyle);
+ }
size_t underscorePos = header.find('_');
if (string::npos != underscorePos) {
header.replace(underscorePos, 1, " ");
@@ -390,19 +617,8 @@ bool MdOut::buildRefFromFile(const char* name, const char* outDir) {
this->lfAlways(1);
FPRINTF("===");
}
- fPopulators.clear();
- fPopulators[kClassesAndStructs].fDescription = "embedded struct and class members";
- fPopulators[kConstants].fDescription = "enum and enum class, const values";
- fPopulators[kConstructors].fDescription = "functions that construct";
- fPopulators[kDefines].fDescription = "preprocessor definitions of functions, values";
- fPopulators[kMemberFunctions].fDescription = "static functions and member methods";
- fPopulators[kMembers].fDescription = "member values";
- fPopulators[kOperators].fDescription = "operator overloading methods";
- fPopulators[kRelatedFunctions].fDescription = "similar methods grouped together";
- fPopulators[kSubtopics].fDescription = "";
- fPopulators[kTypedefs].fDescription = "types defined by other types";
- this->populateTables(fRoot);
- this->markTypeOut(topicDef);
+ const Definition* prior = nullptr;
+ this->markTypeOut(topicDef, &prior);
}
if (fOut) {
this->writePending();
@@ -423,6 +639,91 @@ bool MdOut::buildRefFromFile(const char* name, const char* outDir) {
return !fAddRefFailed;
}
+static bool contains_referenced_child(const Definition* found, const vector<string>& refs) {
+ for (auto child : found->fChildren) {
+ if (refs.end() != std::find_if(refs.begin(), refs.end(),
+ [child](string def) { return child->fName == def; } )) {
+ return true;
+ }
+ if (contains_referenced_child(child, refs)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void MdOut::checkAnchors() {
+ int missing = 0;
+ for (auto bmhFile : fAllAnchorRefs) {
+ auto defIter = fAllAnchorDefs.find(bmhFile.first);
+ SkASSERT(fAllAnchorDefs.end() != defIter);
+ vector<AnchorDef>& allDefs = defIter->second;
+ std::sort(allDefs.begin(), allDefs.end(),
+ [](const AnchorDef& a, const AnchorDef& b) { return a.fDef < b.fDef; } );
+ std::sort(bmhFile.second.begin(), bmhFile.second.end());
+ auto allDefsIter = allDefs.begin();
+ auto allRefsIter = bmhFile.second.begin();
+ for (;;) {
+ bool allDefsEnded = allDefsIter == allDefs.end();
+ bool allRefsEnded = allRefsIter == bmhFile.second.end();
+ if (allDefsEnded && allRefsEnded) {
+ break;
+ }
+ if (allRefsEnded || (!allDefsEnded && allDefsIter->fDef < *allRefsIter)) {
+ if (MarkType::kParam != allDefsIter->fMarkType
+ && !IncompleteAllowed(allDefsIter->fMarkType)) {
+ // If undocumented but parent or child is referred to: good enough for now
+ bool goodEnough = false;
+ if ("undocumented" == defIter->first) {
+ auto iter = fBmhParser.fTopicMap.find(allDefsIter->fDef);
+ if (fBmhParser.fTopicMap.end() != iter) {
+ const Definition* found = iter->second;
+ if (string::npos != found->fFileName.find("undocumented")) {
+ const Definition* parent = found;
+ while ((parent = parent->fParent)) {
+ if (bmhFile.second.end() != std::find_if(bmhFile.second.begin(),
+ bmhFile.second.end(),
+ [parent](string def) {
+ return parent->fName == def; } )) {
+ goodEnough = true;
+ break;
+ }
+ }
+ if (!goodEnough) {
+ goodEnough = contains_referenced_child(found, bmhFile.second);
+ }
+ }
+ }
+ }
+ if (!goodEnough) {
+ SkDebugf("missing ref %s %s\n", defIter->first.c_str(),
+ allDefsIter->fDef.c_str());
+ missing++;
+ }
+ }
+ allDefsIter++;
+ } else if (allDefsEnded || (!allRefsEnded && allDefsIter->fDef > *allRefsIter)) {
+ if (fBmhParser.fExternals.end() == std::find_if(fBmhParser.fExternals.begin(),
+ fBmhParser.fExternals.end(), [allRefsIter](const RootDefinition& root) {
+ return *allRefsIter != root.fName; } )) {
+ SkDebugf("missing def %s %s\n", bmhFile.first.c_str(), allRefsIter->c_str());
+ missing++;
+ }
+ allRefsIter++;
+ } else {
+ SkASSERT(!allDefsEnded);
+ SkASSERT(!allRefsEnded);
+ SkASSERT(allDefsIter->fDef == *allRefsIter);
+ allDefsIter++;
+ allRefsIter++;
+ }
+ if (missing >= 10) {
+ missing = 0;
+ }
+ }
+ }
+}
+
bool MdOut::checkParamReturnBody(const Definition* def) {
TextParser paramBody(def);
const char* descriptionStart = paramBody.fChar;
@@ -445,21 +746,23 @@ bool MdOut::checkParamReturnBody(const Definition* def) {
return true;
}
-void MdOut::childrenOut(const Definition* def, const char* start) {
+void MdOut::childrenOut(Definition* def, const char* start) {
const char* end;
fLineCount = def->fLineCount;
- if (def->isRoot()) {
- fRoot = const_cast<RootDefinition*>(def->asRoot());
- } else if (MarkType::kEnumClass == def->fMarkType) {
+ if (MarkType::kEnumClass == def->fMarkType) {
fEnumClass = def;
}
BmhParser::Resolvable resolvable = this->resolvable(def);
+ const Definition* prior = nullptr;
for (auto& child : def->fChildren) {
+ if (MarkType::kPhraseParam == child->fMarkType) {
+ continue;
+ }
end = child->fStart;
if (BmhParser::Resolvable::kNo != resolvable) {
this->resolveOut(start, end, resolvable);
}
- this->markTypeOut(child);
+ this->markTypeOut(child, &prior);
start = child->fTerminator;
}
if (BmhParser::Resolvable::kNo != resolvable) {
@@ -471,8 +774,59 @@ void MdOut::childrenOut(const Definition* def, const char* start) {
}
}
-const Definition* MdOut::csParent() const {
- const Definition* csParent = fRoot->csParent();
+// output header for subtopic for all consts: name, value, short descriptions (#Line)
+// output link to in context #Const with moderate description
+void MdOut::summaryOut(const Definition* def, MarkType markType, string name) {
+ this->writePending();
+ SkASSERT(TableState::kNone == fTableState);
+ this->mdHeaderOut(3);
+ FPRINTF("%s", name.c_str());
+ this->lfAlways(2);
+ FPRINTF("%s", kTableDeclaration); // <table> with style info
+ this->lfAlways(1);
+ FPRINTF("%s", MarkType::kConst == markType ? kAllConstTableHeader : kAllMemberTableHeader);
+ this->lfAlways(1);
+ bool odd = true;
+ for (auto child : def->fChildren) {
+ if (markType != child->fMarkType) {
+ continue;
+ }
+ auto oneLiner = std::find_if(child->fChildren.begin(), child->fChildren.end(),
+ [](const Definition* test){ return MarkType::kLine == test->fMarkType; } );
+ if (child->fChildren.end() == oneLiner) {
+ child->reportError<void>("missing #Line");
+ continue;
+ }
+ FPRINTF("%s", odd ? kTR_Dark.c_str() : " <tr>");
+ this->lfAlways(1);
+ if (MarkType::kConst == markType) {
+ FPRINTF("%s", tableDataCodeRef(def).c_str());
+ this->lfAlways(1);
+ FPRINTF("%s", table_data_const(def, nullptr).c_str());
+ } else {
+ string memberType;
+ string memberName = this->getMemberTypeName(child, &memberType);
+ SkASSERT(MarkType::kMember == markType);
+ FPRINTF("%s", out_table_data_description(memberType).c_str());
+ this->lfAlways(1);
+ FPRINTF("%s", tableDataCodeLocalRef(memberName).c_str());
+ }
+ this->lfAlways(1);
+ FPRINTF("%s", out_table_data_description(*oneLiner).c_str());
+ this->lfAlways(1);
+ FPRINTF("%s", " </tr>");
+ this->lfAlways(1);
+ odd = !odd;
+ }
+ FPRINTF("</table>");
+ this->lfAlways(1);
+}
+
+Definition* MdOut::csParent() {
+ if (!fRoot) {
+ return nullptr;
+ }
+ Definition* csParent = fRoot->csParent();
if (!csParent) {
const Definition* topic = fRoot;
while (topic && MarkType::kTopic != topic->fMarkType) {
@@ -515,6 +869,106 @@ const Definition* MdOut::findParamType() {
return nullptr;
}
+string MdOut::getMemberTypeName(const Definition* def, string* memberType) {
+ TextParser parser(def->fFileName, def->fStart, def->fContentStart,
+ def->fLineCount);
+ parser.skipExact("#Member");
+ parser.skipWhiteSpace();
+ const char* typeStart = parser.fChar;
+ const char* typeEnd = nullptr;
+ const char* nameStart = nullptr;
+ const char* nameEnd = nullptr;
+ do {
+ parser.skipToWhiteSpace();
+ if (nameStart) {
+ nameEnd = parser.fChar;
+ }
+ if (parser.eof()) {
+ break;
+ }
+ const char* spaceLoc = parser.fChar;
+ if (parser.skipWhiteSpace()) {
+ typeEnd = spaceLoc;
+ nameStart = parser.fChar;
+ }
+ } while (!parser.eof());
+ SkASSERT(typeEnd);
+ *memberType = string(typeStart, (int) (typeEnd - typeStart));
+ replace_all(*memberType, " ", "&nbsp;");
+ SkASSERT(nameStart);
+ SkASSERT(nameEnd);
+ return string(nameStart, (int) (nameEnd - nameStart));
+}
+
+bool MdOut::HasDetails(const Definition* def) {
+ for (auto child : def->fChildren) {
+ if (MarkType::kDetails == child->fMarkType) {
+ return true;
+ }
+ if (MdOut::HasDetails(child)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void MdOut::htmlOut(string s) {
+ SkASSERT(string::npos != s.find('<'));
+ FPRINTF("%s", s.c_str());
+}
+
+const Definition* MdOut::isDefinedByParent(RootDefinition* root, string ref) {
+ if (ref == root->fName) {
+ return root;
+ }
+ if (const Definition* definition = root->find(ref, RootDefinition::AllowParens::kYes)) {
+ return definition;
+ }
+ Definition* test = root;
+ bool isSubtopic = MarkType::kSubtopic == root->fMarkType
+ || MarkType::kTopic == root->fMarkType;
+ do {
+ if (!test->isRoot()) {
+ continue;
+ }
+ bool testIsSubtopic = MarkType::kSubtopic == test->fMarkType
+ || MarkType::kTopic == test->fMarkType;
+ if (isSubtopic != testIsSubtopic) {
+ continue;
+ }
+ RootDefinition* root = test->asRoot();
+ for (auto& leaf : root->fBranches) {
+ if (ref == leaf.first) {
+ return leaf.second;
+ }
+ const Definition* definition = leaf.second->find(ref,
+ RootDefinition::AllowParens::kYes);
+ if (definition) {
+ return definition;
+ }
+ }
+ string prefix = isSubtopic ? "_" : "::";
+ string prefixed = root->fName + prefix + ref;
+ if (Definition* definition = root->find(prefixed, RootDefinition::AllowParens::kYes)) {
+ return definition;
+ }
+ if (isSubtopic && isupper(prefixed[0])) {
+ auto topicIter = fBmhParser.fTopicMap.find(prefixed);
+ if (topicIter != fBmhParser.fTopicMap.end()) {
+ return topicIter->second;
+ }
+ }
+ if (isSubtopic) {
+ string fiddlePrefixed = root->fFiddle + "_" + ref;
+ auto topicIter = fBmhParser.fTopicMap.find(fiddlePrefixed);
+ if (topicIter != fBmhParser.fTopicMap.end()) {
+ return topicIter->second;
+ }
+ }
+ } while ((test = test->fParent));
+ return nullptr;
+}
+
const Definition* MdOut::isDefined(const TextParser& parser, string ref,
BmhParser::Resolvable resolvable) {
auto rootIter = fBmhParser.fClassMap.find(ref);
@@ -550,55 +1004,18 @@ const Definition* MdOut::isDefined(const TextParser& parser, string ref,
return &external;
}
}
- if (fRoot) {
- if (ref == fRoot->fName) {
- return fRoot;
- }
- if (const Definition* definition = fRoot->find(ref, RootDefinition::AllowParens::kYes)) {
- return definition;
- }
- const Definition* test = fRoot;
- do {
- if (!test->isRoot()) {
- continue;
- }
- const RootDefinition* root = test->asRoot();
- for (auto& leaf : root->fBranches) {
- if (ref == leaf.first) {
- return leaf.second;
- }
- const Definition* definition = leaf.second->find(ref,
- RootDefinition::AllowParens::kYes);
- if (definition) {
- return definition;
- }
- }
- for (string prefix : { "::", "_" } ) {
- string prefixed = root->fName + prefix + ref;
- if (const Definition* definition = root->find(prefixed,
- RootDefinition::AllowParens::kYes)) {
- return definition;
- }
- if (isupper(prefixed[0])) {
- auto topicIter = fBmhParser.fTopicMap.find(prefixed);
- if (topicIter != fBmhParser.fTopicMap.end()) {
- return topicIter->second;
- }
- }
- }
- string fiddlePrefixed = root->fFiddle + "_" + ref;
- auto topicIter = fBmhParser.fTopicMap.find(fiddlePrefixed);
- if (topicIter != fBmhParser.fTopicMap.end()) {
- return topicIter->second;
- }
- } while ((test = test->fParent));
+ if (const Definition* definition = this->isDefinedByParent(fRoot, ref)) {
+ return definition;
+ }
+ if (const Definition* definition = this->isDefinedByParent(fSubtopic, ref)) {
+ return definition;
}
size_t doubleColon = ref.find("::");
if (string::npos != doubleColon) {
string className = ref.substr(0, doubleColon);
auto classIter = fBmhParser.fClassMap.find(className);
if (classIter != fBmhParser.fClassMap.end()) {
- const RootDefinition& classDef = classIter->second;
+ RootDefinition& classDef = classIter->second;
const Definition* result = classDef.find(ref, RootDefinition::AllowParens::kYes);
if (result) {
return result;
@@ -611,7 +1028,7 @@ const Definition* MdOut::isDefined(const TextParser& parser, string ref,
ref.length() > 1 && isupper(ref[1]))) {
// try with a prefix
if ('k' == ref[0]) {
- for (auto const& iter : fBmhParser.fEnumMap) {
+ for (auto& iter : fBmhParser.fEnumMap) {
auto def = iter.second.find(ref, RootDefinition::AllowParens::kYes);
if (def) {
return def;
@@ -692,13 +1109,14 @@ string MdOut::linkName(const Definition* ref) const {
result = namePart;
}
}
+ replace_all(result, "::", "_");
return result;
}
// for now, hard-code to html links
// def should not include SkXXX_
string MdOut::linkRef(string leadingSpaces, const Definition* def,
- string ref, BmhParser::Resolvable resolvable) const {
+ string ref, BmhParser::Resolvable resolvable) {
string buildup;
string refName;
const string* str = &def->fFiddle;
@@ -708,7 +1126,9 @@ string MdOut::linkRef(string leadingSpaces, const Definition* def,
if (MarkType::kAlias == def->fMarkType) {
def = def->fParent;
SkASSERT(def);
- SkASSERT(MarkType::kSubtopic == def->fMarkType ||MarkType::kTopic == def->fMarkType);
+ SkASSERT(MarkType::kSubtopic == def->fMarkType
+ || MarkType::kTopic == def->fMarkType
+ || MarkType::kConst == def->fMarkType);
}
if (MarkType::kSubtopic == def->fMarkType) {
const Definition* topic = def->topicParent();
@@ -756,7 +1176,7 @@ string MdOut::linkRef(string leadingSpaces, const Definition* def,
if (ref.length() > 2 && islower(ref[0]) && "()" == ref.substr(ref.length() - 2)) {
refOut = refOut.substr(0, refOut.length() - 2);
}
- string result = leadingSpaces + "<a href=\"" + buildup + "\">" + refOut + "</a>";
+ string result = leadingSpaces + this->anchorRef(buildup, refOut);
if (BmhParser::Resolvable::kClone == resolvable && MarkType::kMethod == def->fMarkType &&
def->fCloned && !def->fClone) {
bool found = false;
@@ -771,7 +1191,8 @@ string MdOut::linkRef(string leadingSpaces, const Definition* def,
string clone = match + num;
const auto& leafIter = classIter->second.fLeaves.find(clone);
if (leafIter != classIter->second.fLeaves.end()) {
- result += "<sup><a href=\"" + buildup + "_" + num + "\">[" + num + "]</a></sup>";
+ result += "<sup>" + this->anchorRef(buildup + "_" + num,
+ string("[") + num + "]") + "</sup>";
found = true;
}
}
@@ -783,19 +1204,27 @@ string MdOut::linkRef(string leadingSpaces, const Definition* def,
return result;
}
-void MdOut::markTypeOut(Definition* def) {
+static bool writeTableEnd(MarkType markType, Definition* def, const Definition** prior) {
+ return markType != def->fMarkType && *prior && markType == (*prior)->fMarkType;
+}
+
+void MdOut::markTypeOut(Definition* def, const Definition** prior) {
string printable = def->printableName();
const char* textStart = def->fContentStart;
- if (MarkType::kParam != def->fMarkType && MarkType::kConst != def->fMarkType &&
- (!def->fParent || MarkType::kConst != def->fParent->fMarkType) &&
- TableState::kNone != fTableState &&
- (MarkType::kPhraseRef != def->fMarkType || !def->fParent ||
- MarkType::kParam != def->fParent->fMarkType)) {
+ bool lookForOneLiner = false;
+ // #Param and #Const don't have markers to say when the last is seen, so detect that by looking
+ // for a change in type.
+ if (MarkType::kStruct == def->fMarkType) {
+ SkDebugf("");
+ }
+ if (writeTableEnd(MarkType::kParam, def, prior) || writeTableEnd(MarkType::kConst, def, prior)
+ || writeTableEnd(MarkType::kMember, def, prior)) {
this->writePending();
FPRINTF("</table>");
this->lf(2);
fTableState = TableState::kNone;
}
+ fLastDef = def;
switch (def->fMarkType) {
case MarkType::kAlias:
break;
@@ -806,20 +1235,52 @@ void MdOut::markTypeOut(Definition* def) {
this->writePending();
TextParser parser(def);
const char* start = parser.fChar;
- parser.skipToEndBracket(" # ");
+ parser.skipToEndBracket((string(" ") + def->fMC + " ").c_str());
string anchorText(start, parser.fChar - start);
- parser.skipExact(" # ");
+ parser.skipExact((string(" ") + def->fMC + " ").c_str());
string anchorLink(parser.fChar, parser.fEnd - parser.fChar);
- FPRINTF("<a href=\"%s\">%s", anchorLink.c_str(), anchorText.c_str());
+ this->htmlOut(anchorRef(anchorLink, anchorText));
} break;
case MarkType::kBug:
break;
case MarkType::kClass:
+ case MarkType::kStruct: {
+ fRoot = def->asRoot();
this->mdHeaderOut(1);
- FPRINTF("<a name=\"%s\"></a> Class %s", this->linkName(def).c_str(),
- def->fName.c_str());
+ if (MarkType::kStruct == def->fMarkType) {
+ this->htmlOut(anchorDef(def->fFiddle, "Struct " + def->fName));
+ } else {
+ this->htmlOut(anchorDef(this->linkName(def), "Class " + def->fName));
+ }
this->lf(1);
- break;
+ if (string::npos != fRoot->fFileName.find("undocumented")) {
+ break;
+ }
+ // if class or struct contains constants, and doesn't contain subtopic kConstant, add it
+ // and add a child populate
+ const Definition* subtopic = def->subtopicParent();
+ const Definition* topic = def->topicParent();
+ for (auto item : SubtopicKeys::kGeneratedSubtopics) {
+ string subname;
+ if (subtopic != topic) {
+ subname = subtopic->fName + '_';
+ }
+ subname += item;
+ if (fRoot->populator(item).fMembers.size()
+ && !std::any_of(fRoot->fChildren.begin(), fRoot->fChildren.end(),
+ [subname](const Definition* child) {
+ return MarkType::kSubtopic == child->fMarkType
+ && subname == child->fName;
+ } )) {
+ // generate subtopic
+ this->mdHeaderOut(2);
+ this->htmlOut(anchorDef(subname, item));
+ this->lf(2);
+ // generate populate
+ this->subtopicOut(item);
+ }
+ }
+ } break;
case MarkType::kCode:
this->lfAlways(2);
FPRINTF("<pre style=\"padding: 1em 1em 1em 1em;"
@@ -837,56 +1298,105 @@ void MdOut::markTypeOut(Definition* def) {
break;
case MarkType::kComment:
break;
+ case MarkType::kMember:
case MarkType::kConst: {
+ bool isConst = MarkType::kConst == def->fMarkType;
+ lookForOneLiner = false;
+ fWroteSomething = false;
+ // output consts for one parent with moderate descriptions
+ // optional link to subtopic with longer descriptions, examples
if (TableState::kNone == fTableState) {
+ SkASSERT(!*prior || (isConst && MarkType::kConst != (*prior)->fMarkType)
+ || (!isConst && MarkType::kMember != (*prior)->fMarkType));
this->mdHeaderOut(3);
- FPRINTF("Constants\n"
- "\n"
- "<table>");
+ FPRINTF("%s", this->fPopulators[isConst ? SubtopicKeys::kConstants :
+ SubtopicKeys::kMembers].fName.c_str());
+ this->lfAlways(2);
+ FPRINTF("%s", kTableDeclaration);
fTableState = TableState::kRow;
- this->lf(1);
+ fOddRow = true;
+ this->lfAlways(1);
+ // look ahead to see if the details column has data or not
+ fHasDetails = MdOut::HasDetails(def->fParent);
+ FPRINTF("%s", fHasDetails ? \
+ (isConst ? kSubConstTableHeader : kSubMemberTableHeader) : \
+ (isConst ? kAllConstTableHeader : kAllMemberTableHeader));
+ this->lfAlways(1);
}
if (TableState::kRow == fTableState) {
this->writePending();
- FPRINTF(" <tr>");
- this->lf(1);
+ FPRINTF("%s", fOddRow ? kTR_Dark.c_str() : " <tr>");
+ fOddRow = !fOddRow;
+ this->lfAlways(1);
fTableState = TableState::kColumn;
}
this->writePending();
- FPRINTF(" <td><a name=\"%s\"> <code><strong>%s </strong></code> </a></td>",
- def->fFiddle.c_str(), def->fName.c_str());
- const char* lineEnd = strchr(textStart, '\n');
- SkASSERT(lineEnd < def->fTerminator);
- SkASSERT(lineEnd > textStart);
- SkASSERT((int) (lineEnd - textStart) == lineEnd - textStart);
- FPRINTF("<td>%.*s</td>", (int) (lineEnd - textStart), textStart);
- FPRINTF("<td>");
- textStart = lineEnd;
+ if (isConst) {
+ // TODO: if fHasDetails is true, could defer def and issue a ref instead
+ // unclear if this is a good idea or not
+ FPRINTF("%s", this->tableDataCodeDef(def).c_str());
+ this->lfAlways(1);
+ FPRINTF("%s", table_data_const(def, &textStart).c_str());
+ } else {
+ string memberType;
+ string memberName = this->getMemberTypeName(def, &memberType);
+ FPRINTF("%s", out_table_data_description(memberType).c_str());
+ this->lfAlways(1);
+ FPRINTF("%s", tableDataCodeDef(def->fFiddle, memberName).c_str());
+ }
+ this->lfAlways(1);
+ if (fHasDetails) {
+ string details;
+ auto subtopic = std::find_if(def->fChildren.begin(), def->fChildren.end(),
+ [](const Definition* test){
+ return MarkType::kDetails == test->fMarkType; } );
+ if (def->fChildren.end() != subtopic) {
+ string subtopicName = string((*subtopic)->fContentStart,
+ (int) ((*subtopic)->fContentEnd - (*subtopic)->fContentStart));
+ const Definition* parentSubtopic = def->subtopicParent();
+ SkASSERT(parentSubtopic);
+ string fullName = parentSubtopic->fFiddle + '_' + subtopicName;
+ if (fBmhParser.fTopicMap.end() == fBmhParser.fTopicMap.find(fullName)) {
+ (*subtopic)->reportError<void>("missing #Details subtopic");
+ }
+ subtopicName = parentSubtopic->fName + '_' + subtopicName;
+ string noUnderscores = subtopicName;
+ replace_all(noUnderscores, "_", "&nbsp;");
+ details = this->anchorLocalRef(subtopicName, noUnderscores) + "&nbsp;";
+ }
+ FPRINTF("%s", out_table_data_details(details).c_str());
+ this->lfAlways(1);
+ }
+ lookForOneLiner = true; // if description is empty, use oneLiner data
+ FPRINTF("%s", out_table_data_description_start().c_str()); // start of Description
+ this->lfAlways(1);
} break;
case MarkType::kDefine:
break;
case MarkType::kDefinedBy:
break;
case MarkType::kDeprecated:
+ this->writeString("Deprecated.");
+ this->lf(2);
break;
case MarkType::kDescription:
fInDescription = true;
this->writePending();
- FPRINTF("<div>");
+ FPRINTF("%s", "<div>");
break;
- case MarkType::kDoxygen:
+ case MarkType::kDetails:
break;
case MarkType::kDuration:
break;
case MarkType::kEnum:
case MarkType::kEnumClass:
this->mdHeaderOut(2);
- FPRINTF("<a name=\"%s\"></a> Enum %s", def->fFiddle.c_str(), def->fName.c_str());
+ this->htmlOut(anchorDef(def->fFiddle, "Enum " + def->fName));
this->lf(2);
break;
case MarkType::kExample: {
this->mdHeaderOut(3);
- FPRINTF("Example\n"
+ FPRINTF("%s", "Example\n"
"\n");
fHasFiddle = true;
bool showGpu = false;
@@ -904,15 +1414,15 @@ void MdOut::markTypeOut(Definition* def) {
SkASSERT(def->fHash.length() > 0);
FPRINTF("<div><fiddle-embed name=\"%s\"", def->fHash.c_str());
if (showGpu) {
- FPRINTF(" gpu=\"true\"");
+ FPRINTF("%s", " gpu=\"true\"");
if (gpuAndCpu) {
- FPRINTF(" cpu=\"true\"");
+ FPRINTF("%s", " cpu=\"true\"");
}
}
- FPRINTF(">");
+ FPRINTF("%s", ">");
} else {
SkASSERT(def->fHash.length() == 0);
- FPRINTF("<pre style=\"padding: 1em 1em 1em 1em; font-size: 13px"
+ FPRINTF("%s", "<pre style=\"padding: 1em 1em 1em 1em; font-size: 13px"
" width: 62.5em; background-color: #f0f0f0\">");
this->lfAlways(1);
if (def->fWrapper.length() > 0) {
@@ -922,10 +1432,11 @@ void MdOut::markTypeOut(Definition* def) {
}
} break;
case MarkType::kExperimental:
+ writeString("Experimental.");
+ this->lf(2);
break;
case MarkType::kExternal:
- break;
- case MarkType::kFile:
+ SkDebugf("");
break;
case MarkType::kFormula:
break;
@@ -962,8 +1473,9 @@ void MdOut::markTypeOut(Definition* def) {
break;
case MarkType::kList:
fInList = true;
+ fTableState = TableState::kRow;
this->lfAlways(2);
- FPRINTF("<table>");
+ FPRINTF("%s", "<table>");
this->lf(1);
break;
case MarkType::kLiteral:
@@ -971,22 +1483,11 @@ void MdOut::markTypeOut(Definition* def) {
case MarkType::kMarkChar:
fBmhParser.fMC = def->fContentStart[0];
break;
- case MarkType::kMember: {
- TextParser tp(def->fFileName, def->fStart, def->fContentStart, def->fLineCount);
- tp.skipExact("#Member");
- tp.skipWhiteSpace();
- const char* end = tp.trimmedBracketEnd('\n');
- this->lfAlways(2);
- FPRINTF("<a name=\"%s\"> <code><strong>%.*s</strong></code> </a>",
- def->fFiddle.c_str(), (int) (end - tp.fChar), tp.fChar);
- this->lf(2);
- } break;
case MarkType::kMethod: {
string method_name = def->methodName();
string formattedStr = def->formatFunction(Definition::Format::kIncludeReturn);
-
this->lfAlways(2);
- FPRINTF("<a name=\"%s\"></a>", def->fFiddle.c_str());
+ this->htmlOut(anchorDef(def->fFiddle, ""));
if (!def->isClone()) {
this->mdHeaderOutLF(2, 1);
FPRINTF("%s", method_name.c_str());
@@ -997,22 +1498,26 @@ void MdOut::markTypeOut(Definition* def) {
// TODO: 50em below should match limit = 80 in formatFunction()
this->writePending();
string preformattedStr = preformat(formattedStr);
- FPRINTF("<pre style=\"padding: 1em 1em 1em 1em;"
- "width: 62.5em; background-color: #f0f0f0\">\n"
- "%s\n"
- "</pre>", preformattedStr.c_str());
+ string references = this->addReferences(&preformattedStr.front(),
+ &preformattedStr.back() + 1, BmhParser::Resolvable::kSimple);
+ preformattedStr = references;
+ this->htmlOut("<pre style=\"padding: 1em 1em 1em 1em; width: 62.5em;"
+ "background-color: #f0f0f0\">\n" + preformattedStr + "\n" + "</pre>");
this->lf(2);
fTableState = TableState::kNone;
fMethod = def;
} break;
case MarkType::kNoExample:
break;
+ case MarkType::kNoJustify:
+ break;
case MarkType::kOutdent:
break;
case MarkType::kParam: {
if (TableState::kNone == fTableState) {
+ SkASSERT(!*prior || MarkType::kParam != (*prior)->fMarkType);
this->mdHeaderOut(3);
- fprintf(fOut,
+ this->htmlOut(
"Parameters\n"
"\n"
"<table>"
@@ -1036,22 +1541,84 @@ void MdOut::markTypeOut(Definition* def) {
paramParser.skipToSpace();
string paramNameStr(paramName, (int) (paramParser.fChar - paramName));
if (!this->checkParamReturnBody(def)) {
+ *prior = def;
return;
}
string refNameStr = def->fParent->fFiddle + "_" + paramNameStr;
- fprintf(fOut,
- " <td><a name=\"%s\"> <code><strong>%s </strong></code> </a></td> <td>",
- refNameStr.c_str(), paramNameStr.c_str());
+ this->htmlOut(" <td>" + anchorDef(refNameStr,
+ "<code><strong>" + paramNameStr + "</strong></code>") + "</td>");
+ this->lfAlways(1);
+ FPRINTF(" <td>");
} break;
+ case MarkType::kPhraseDef:
+ // skip text and children
+ *prior = def;
+ return;
+ case MarkType::kPhraseParam:
+ SkDebugf(""); // convenient place to set a breakpoint
+ break;
+ case MarkType::kPhraseRef:
+ if (fPhraseParams.end() != fPhraseParams.find(def->fName)) {
+ if (fColumn > 0) {
+ this->writeSpace();
+ }
+ this->writeString(fPhraseParams[def->fName]);
+ if (isspace(def->fContentStart[0])) {
+ this->writeSpace();
+ }
+ } else if (fBmhParser.fPhraseMap.end() == fBmhParser.fPhraseMap.find(def->fName)) {
+ def->reportError<void>("missing phrase definition");
+ fAddRefFailed = true;
+ } else {
+ if (fColumn) {
+ SkASSERT(' ' >= def->fStart[0]);
+ this->writeSpace();
+ }
+ Definition* phraseRef = fBmhParser.fPhraseMap.find(def->fName)->second;
+ // def->fChildren are parameters to substitute phraseRef->fChildren,
+ // phraseRef->fChildren has both param defines and references
+ // def->fChildren must have the same number of entries as phaseRef->fChildren
+ // which are kPhraseParam, and substitute one for one
+ // Then, each kPhraseRef in phaseRef looks up the key and value
+ fPhraseParams.clear();
+ auto refKidsIter = phraseRef->fChildren.begin();
+ for (auto child : def->fChildren) {
+ if (MarkType::kPhraseParam != child->fMarkType) {
+ // more work to do to support other types
+ this->reportError("phrase ref child must be param");
+ }
+ do {
+ if (refKidsIter == phraseRef->fChildren.end()) {
+ this->reportError("phrase def missing param");
+ break;
+ }
+ if (MarkType::kPhraseRef == (*refKidsIter)->fMarkType) {
+ continue;
+ }
+ if (MarkType::kPhraseParam != (*refKidsIter)->fMarkType) {
+ this->reportError("unexpected type in phrase def children");
+ break;
+ }
+ fPhraseParams[(*refKidsIter)->fName] = child->fName;
+ break;
+ } while (true);
+ }
+ this->childrenOut(phraseRef, phraseRef->fContentStart);
+ fPhraseParams.clear();
+ if (' ' >= def->fContentStart[0] && !fPendingLF) {
+ this->writeSpace();
+ }
+ }
+ break;
case MarkType::kPlatform:
break;
case MarkType::kPopulate: {
SkASSERT(MarkType::kSubtopic == def->fParent->fMarkType);
string name = def->fParent->fName;
- if (kSubtopics == name) {
- this->subtopicsOut();
+ if (string::npos != name.find(SubtopicKeys::kOverview)) {
+ this->subtopicsOut(def->fParent);
} else {
- this->subtopicOut(this->populator(name.c_str()));
+ this->subtopicOut(name);
}
} break;
case MarkType::kPrivate:
@@ -1060,6 +1627,7 @@ void MdOut::markTypeOut(Definition* def) {
this->mdHeaderOut(3);
FPRINTF("Return Value");
if (!this->checkParamReturnBody(def)) {
+ *prior = def;
return;
}
this->lf(2);
@@ -1080,7 +1648,7 @@ void MdOut::markTypeOut(Definition* def) {
case MarkType::kStdOut: {
TextParser code(def);
this->mdHeaderOut(4);
- fprintf(fOut,
+ FPRINTF(
"Example Output\n"
"\n"
"~~~~");
@@ -1094,18 +1662,27 @@ void MdOut::markTypeOut(Definition* def) {
FPRINTF("~~~~");
this->lf(2);
} break;
- case MarkType::kStruct:
- fRoot = def->asRoot();
- this->mdHeaderOut(1);
- FPRINTF("<a name=\"%s\"></a> Struct %s", def->fFiddle.c_str(), def->fName.c_str());
- this->lf(1);
- break;
case MarkType::kSubstitute:
break;
case MarkType::kSubtopic:
+ fSubtopic = def->asRoot();
this->mdHeaderOut(2);
- FPRINTF("<a name=\"%s\"></a> %s", def->fName.c_str(), printable.c_str());
+ if (SubtopicKeys::kOverview == def->fName) {
+ this->writeString(def->fName);
+ } else {
+ this->htmlOut(anchorDef(def->fName, printable));
+ }
this->lf(2);
+ // if a subtopic child is const, generate short table of const name, value, line desc
+ if (std::any_of(def->fChildren.begin(), def->fChildren.end(),
+ [](Definition* child){return MarkType::kConst == child->fMarkType;})) {
+ this->summaryOut(def, MarkType::kConst, fPopulators[SubtopicKeys::kConstants].fName);
+ }
+ // if a subtopic child is member, generate short table of const name, value, line desc
+ if (std::any_of(def->fChildren.begin(), def->fChildren.end(),
+ [](Definition* child){return MarkType::kMember == child->fMarkType;})) {
+ this->summaryOut(def, MarkType::kMember, fPopulators[SubtopicKeys::kMembers].fName);
+ }
break;
case MarkType::kTable:
this->lf(2);
@@ -1114,20 +1691,26 @@ void MdOut::markTypeOut(Definition* def) {
break;
case MarkType::kText:
break;
- case MarkType::kTime:
- break;
case MarkType::kToDo:
break;
- case MarkType::kTopic:
+ case MarkType::kTopic: {
+ auto found = std::find_if(def->fChildren.begin(), def->fChildren.end(),
+ [](Definition* test) { return test->isStructOrClass(); } );
+ bool hasClassOrStruct = def->fChildren.end() != found;
+ fRoot = hasClassOrStruct ? (*found)->asRoot() : def->asRoot();
+ fSubtopic = def->asRoot();
+ bool isUndocumented = string::npos != def->fFileName.find("undocumented");
+ if (!isUndocumented) {
+ this->populateTables(def, fRoot);
+ }
this->mdHeaderOut(1);
- FPRINTF("<a name=\"%s\"></a> %s", this->linkName(def).c_str(),
- printable.c_str());
+ this->htmlOut(anchorDef(this->linkName(def), printable));
this->lf(1);
- break;
- case MarkType::kTrack:
- // don't output children
- return;
+ } break;
case MarkType::kTypedef:
+ this->mdHeaderOut(1);
+ this->htmlOut(anchorDef(def->fFiddle, "Typedef " + def->fName));
+ this->lf(1);
break;
case MarkType::kUnion:
break;
@@ -1135,39 +1718,36 @@ void MdOut::markTypeOut(Definition* def) {
break;
case MarkType::kWidth:
break;
- case MarkType::kPhraseDef:
- // skip text and children
- return;
- case MarkType::kPhraseRef:
- if (fBmhParser.fPhraseMap.end() == fBmhParser.fPhraseMap.find(def->fName)) {
- def->reportError<void>("missing phrase definition");
- fAddRefFailed = true;
- } else {
- if (fColumn && ' ' >= def->fStart[0]) {
- this->writeSpace();
- }
- Definition* phraseRef = fBmhParser.fPhraseMap.find(def->fName)->second;
- this->childrenOut(phraseRef, phraseRef->fContentStart);
- if (' ' >= def->fContentStart[0]) {
- this->writeSpace();
- }
- }
- break;
default:
SkDebugf("fatal error: MarkType::k%s unhandled in %s()\n",
BmhParser::kMarkProps[(int) def->fMarkType].fName, __func__);
SkASSERT(0); // handle everything
break;
}
- TableState saveState = fTableState;
+ // TableState saveState = fTableState;
+ if (def->fLineCount >= 533 && string::npos != def->fFileName.find("SkSurface")) {
+ SkDebugf("");
+ }
this->childrenOut(def, textStart);
- fTableState = saveState;
+ // fTableState = saveState;
switch (def->fMarkType) { // post child work, at least for tables
case MarkType::kAnchor:
if (fColumn > 0) {
this->writeSpace();
}
break;
+ case MarkType::kClass:
+ case MarkType::kStruct:
+ if (TableState::kNone != fTableState) {
+ this->writePending();
+ FPRINTF("</table>");
+ this->lf(2);
+ fTableState = TableState::kNone;
+ }
+ if (def->csParent()) {
+ fRoot = def->csParent()->asRoot();
+ }
+ break;
case MarkType::kCode:
fIndent = 0;
this->lf(1);
@@ -1180,7 +1760,7 @@ void MdOut::markTypeOut(Definition* def) {
if (fInList) {
this->writePending();
FPRINTF("</td>");
- this->lf(1);
+ this->lfAlways(1);
} else {
FPRINTF(" ");
}
@@ -1192,7 +1772,12 @@ void MdOut::markTypeOut(Definition* def) {
break;
case MarkType::kEnum:
case MarkType::kEnumClass:
- this->lfAlways(2);
+ if (TableState::kNone != fTableState) {
+ this->writePending();
+ FPRINTF("</table>");
+ this->lf(2);
+ fTableState = TableState::kNone;
+ }
break;
case MarkType::kExample:
this->writePending();
@@ -1216,8 +1801,10 @@ void MdOut::markTypeOut(Definition* def) {
case MarkType::kList:
fInList = false;
this->writePending();
+ SkASSERT(TableState::kNone != fTableState);
FPRINTF("</table>");
this->lf(2);
+ fTableState = TableState::kNone;
break;
case MarkType::kLegend: {
SkASSERT(def->fChildren.size() == 1);
@@ -1239,13 +1826,26 @@ void MdOut::markTypeOut(Definition* def) {
this->lf(2);
break;
case MarkType::kConst:
+ case MarkType::kMember:
+ if (lookForOneLiner && !fWroteSomething) {
+ auto oneLiner = std::find_if(def->fChildren.begin(), def->fChildren.end(),
+ [](const Definition* test){ return MarkType::kLine == test->fMarkType; } );
+ if (def->fChildren.end() != oneLiner) {
+ TextParser parser(*oneLiner);
+ parser.skipWhiteSpace();
+ parser.trimEnd();
+ FPRINTF("%.*s", (int) (parser.fEnd - parser.fChar), parser.fChar);
+ }
+ lookForOneLiner = false;
+ }
case MarkType::kParam:
SkASSERT(TableState::kColumn == fTableState);
fTableState = TableState::kRow;
this->writePending();
- FPRINTF("</td>\n");
+ FPRINTF("</td>");
+ this->lfAlways(1);
FPRINTF(" </tr>");
- this->lf(1);
+ this->lfAlways(1);
break;
case MarkType::kReturn:
case MarkType::kSeeAlso:
@@ -1259,9 +1859,6 @@ void MdOut::markTypeOut(Definition* def) {
}
this->lf(1);
break;
- case MarkType::kStruct:
- fRoot = fRoot->rootParent();
- break;
case MarkType::kTable:
this->lf(2);
break;
@@ -1269,9 +1866,22 @@ void MdOut::markTypeOut(Definition* def) {
break;
case MarkType::kPrivate:
break;
+ case MarkType::kSubtopic:
+ SkASSERT(def);
+ do {
+ def = def->fParent;
+ } while (def && MarkType::kTopic != def->fMarkType
+ && MarkType::kSubtopic != def->fMarkType);
+ SkASSERT(def);
+ fSubtopic = def->asRoot();
+ break;
+ case MarkType::kTopic:
+ fSubtopic = nullptr;
+ break;
default:
break;
}
+ *prior = def;
}
void MdOut::mdHeaderOutLF(int depth, int lf) {
@@ -1282,76 +1892,103 @@ void MdOut::mdHeaderOutLF(int depth, int lf) {
FPRINTF(" ");
}
-void MdOut::populateTables(const Definition* def) {
- const Definition* csParent = this->csParent();
- if (!csParent) {
+void MdOut::populateOne(Definition* def,
+ unordered_map<string, RootDefinition::SubtopicContents>& populator) {
+ if (MarkType::kConst == def->fMarkType) {
+ populator[SubtopicKeys::kConstants].fMembers.push_back(def);
return;
}
+ if (MarkType::kEnum == def->fMarkType || MarkType::kEnumClass == def->fMarkType) {
+ populator[SubtopicKeys::kConstants].fMembers.push_back(def);
+ return;
+ }
+ if (MarkType::kDefine == def->fMarkType) {
+ populator[SubtopicKeys::kDefines].fMembers.push_back(def);
+ return;
+ }
+ if (MarkType::kMember == def->fMarkType) {
+ populator[SubtopicKeys::kMembers].fMembers.push_back(def);
+ return;
+ }
+ if (MarkType::kTypedef == def->fMarkType) {
+ populator[SubtopicKeys::kTypedefs].fMembers.push_back(def);
+ return;
+ }
+ if (MarkType::kMethod != def->fMarkType) {
+ return;
+ }
+ if (def->fClone) {
+ return;
+ }
+ if (Definition::MethodType::kConstructor == def->fMethodType
+ || Definition::MethodType::kDestructor == def->fMethodType) {
+ populator[SubtopicKeys::kConstructors].fMembers.push_back(def);
+ return;
+ }
+ if (Definition::MethodType::kOperator == def->fMethodType) {
+ populator[SubtopicKeys::kOperators].fMembers.push_back(def);
+ return;
+ }
+ populator[SubtopicKeys::kMemberFunctions].fMembers.push_back(def);
+ const Definition* csParent = this->csParent();
+ if (csParent) {
+ if (0 == def->fName.find(csParent->fName + "::Make")
+ || 0 == def->fName.find(csParent->fName + "::make")) {
+ populator[SubtopicKeys::kConstructors].fMembers.push_back(def);
+ return;
+ }
+ }
+ for (auto item : def->fChildren) {
+ if (MarkType::kIn == item->fMarkType) {
+ string name(item->fContentStart, item->fContentEnd - item->fContentStart);
+ populator[name].fMembers.push_back(def);
+ populator[name].fShowClones = true;
+ break;
+ }
+ }
+}
+
+void MdOut::populateTables(const Definition* def, RootDefinition* root) {
for (auto child : def->fChildren) {
- if (MarkType::kTopic == child->fMarkType || MarkType::kSubtopic == child->fMarkType) {
+ if (MarkType::kSubtopic == child->fMarkType) {
string name = child->fName;
- bool builtInTopic = name == kClassesAndStructs || name == kConstants
- || name == kConstructors || name == kDefines || name == kMemberFunctions
- || name == kMembers || name == kOperators || name == kOverview
- || name == kRelatedFunctions || name == kSubtopics || name == kTypedefs;
- if (!builtInTopic && child->fName != kOverview) {
- this->populator(kRelatedFunctions).fMembers.push_back(child);
- }
- this->populateTables(child);
+ bool builtInTopic = name == SubtopicKeys::kOverview;
+ for (auto item : SubtopicKeys::kGeneratedSubtopics) {
+ builtInTopic |= name == item;
+ }
+ if (!builtInTopic) {
+ string subname;
+ const Definition* subtopic = child->subtopicParent();
+ if (subtopic) {
+ subname = subtopic->fName + '_';
+ }
+ builtInTopic = name == subname + SubtopicKeys::kOverview;
+ for (auto item : SubtopicKeys::kGeneratedSubtopics) {
+ builtInTopic |= name == subname + item;
+ }
+ if (!builtInTopic) {
+ root->populator(SubtopicKeys::kRelatedFunctions).fMembers.push_back(child);
+ } else {
+ SkDebugf("");
+ }
+ }
+ this->populateTables(child, root);
continue;
}
if (child->isStructOrClass()) {
if (fClassStack.size() > 0) {
- this->populator(kClassesAndStructs).fMembers.push_back(child);
+ root->populator(MarkType::kStruct != child->fMarkType ? SubtopicKeys::kClasses :
+ SubtopicKeys::kStructs).fMembers.push_back(child);
}
fClassStack.push_back(child);
- this->populateTables(child);
+ this->populateTables(child, child->asRoot());
fClassStack.pop_back();
continue;
}
if (MarkType::kEnum == child->fMarkType || MarkType::kEnumClass == child->fMarkType) {
- this->populator(kConstants).fMembers.push_back(child);
- continue;
- }
- if (MarkType::kDefine == child->fMarkType) {
- this->populator(kDefines).fMembers.push_back(child);
- }
- if (MarkType::kMember == child->fMarkType) {
- this->populator(kMembers).fMembers.push_back(child);
- continue;
- }
- if (MarkType::kTypedef == child->fMarkType) {
- this->populator(kTypedefs).fMembers.push_back(child);
- }
- if (MarkType::kMethod != child->fMarkType) {
- continue;
- }
- if (child->fClone) {
- continue;
- }
- if (Definition::MethodType::kConstructor == child->fMethodType
- || Definition::MethodType::kDestructor == child->fMethodType) {
- this->populator(kConstructors).fMembers.push_back(child);
- continue;
- }
- if (Definition::MethodType::kOperator == child->fMethodType) {
- this->populator(kOperators).fMembers.push_back(child);
- continue;
- }
- this->populator(kMemberFunctions).fMembers.push_back(child);
- if (csParent && (0 == child->fName.find(csParent->fName + "::Make")
- || 0 == child->fName.find(csParent->fName + "::make"))) {
- this->populator(kConstructors).fMembers.push_back(child);
- continue;
- }
- for (auto item : child->fChildren) {
- if (MarkType::kIn == item->fMarkType) {
- string name(item->fContentStart, item->fContentEnd - item->fContentStart);
- fPopulators[name].fMembers.push_back(child);
- fPopulators[name].fShowClones = true;
- break;
- }
+ this->populateTables(child, root);
}
+ this->populateOne(child, root->fPopulators);
}
}
@@ -1416,6 +2053,7 @@ void MdOut::resolveOut(const char* start, const char* end, BmhParser::Resolvable
}
string str(contentStart, lineLength);
this->writeString(str.c_str());
+ fWroteSomething = !!lineLength;
}
if (paragraph.eof()) {
break;
@@ -1431,51 +2069,96 @@ void MdOut::resolveOut(const char* start, const char* end, BmhParser::Resolvable
}
}
-void MdOut::rowOut(const char* name, string description) {
+void MdOut::rowOut(const char* name, string description, bool literalName) {
+ FPRINTF("%s", fOddRow ? kTR_Dark.c_str() : " <tr>");
this->lfAlways(1);
- FPRINTF("| ");
- this->resolveOut(name, name + strlen(name), BmhParser::Resolvable::kYes);
- FPRINTF(" | ");
+ FPRINTF("%s", kTD_Left.c_str());
+ if (literalName) {
+ if (strlen(name)) {
+ this->writeString(name);
+ }
+ } else {
+ this->resolveOut(name, name + strlen(name), BmhParser::Resolvable::kYes);
+ }
+ FPRINTF("</td>");
+ this->lfAlways(1);
+ FPRINTF("%s", kTD_Left.c_str());
this->resolveOut(&description.front(), &description.back() + 1, BmhParser::Resolvable::kYes);
- FPRINTF(" |");
- this->lf(1);
+ FPRINTF("</td>");
+ this->lfAlways(1);
+ FPRINTF(" </tr>");
+ this->lfAlways(1);
+ fOddRow = !fOddRow;
}
-void MdOut::subtopicsOut() {
- const Definition* csParent = this->csParent();
- SkASSERT(csParent);
- this->rowOut("name", "description");
- this->rowOut("---", "---");
- for (auto item : { kClassesAndStructs, kConstants, kConstructors, kDefines,
- kMemberFunctions, kMembers, kOperators, kRelatedFunctions, kTypedefs } ) {
- for (auto entry : this->populator(item).fMembers) {
- if (entry->csParent() == csParent) {
- string description = fPopulators.find(item)->second.fDescription;
- if (kConstructors == item) {
- description += " " + csParent->fName;
+void MdOut::subtopicsOut(Definition* def) {
+ Definition* csParent = def->csParent();
+ const Definition* subtopicParent = def->subtopicParent();
+ const Definition* topicParent = def->topicParent();
+ SkASSERT(subtopicParent);
+ this->lfAlways(1);
+ FPRINTF("%s", kTableDeclaration);
+ this->lfAlways(1);
+ FPRINTF("%s", kTopicsTableHeader);
+ this->lfAlways(1);
+ fOddRow = true;
+ for (auto item : SubtopicKeys::kGeneratedSubtopics) {
+ for (auto entry : fRoot->populator(item).fMembers) {
+ if ((csParent && entry->csParent() == csParent)
+ || entry->subtopicParent() == subtopicParent) {
+ auto popItem = fPopulators.find(item);
+ string description = popItem->second.fOneLiner;
+ if (SubtopicKeys::kConstructors == item) {
+ description += " " + fRoot->fName;
+ }
+ string subtopic;
+ if (subtopicParent != topicParent) {
+ subtopic = subtopicParent->fName + '_';
}
- this->rowOut(item, description);
+ string link = this->anchorLocalRef(subtopic + item, popItem->second.fName);
+ this->rowOut(link.c_str(), description, true);
break;
}
}
}
+ FPRINTF("</table>");
+ this->lfAlways(1);
}
-void MdOut::subtopicOut(const TableContents& tableContents) {
- const auto& data = tableContents.fMembers;
- const Definition* csParent = this->csParent();
+void MdOut::subtopicOut(string name) {
+ Definition* csParent = this->csParent();
SkASSERT(csParent);
- fRoot = csParent->asRoot();
- this->rowOut("name", "description");
- this->rowOut("---", "---");
+ const Definition* topicParent = fSubtopic ? fSubtopic->topicParent() : nullptr;
+ this->lfAlways(1);
+ if (fPopulators.end() != fPopulators.find(name)) {
+ const SubtopicDescriptions& tableDescriptions = this->populator(name);
+ this->anchorDef(name, tableDescriptions.fName);
+ this->lfAlways(1);
+ if (tableDescriptions.fDetails.length()) {
+ string details = csParent->fName;
+ details += " " + tableDescriptions.fDetails;
+ this->writeString(details);
+ this->lfAlways(1);
+ }
+ } else {
+ this->anchorDef(name, name);
+ this->lfAlways(1);
+ }
+ FPRINTF("%s", kTableDeclaration);
+ this->lfAlways(1);
+ FPRINTF("%s", kTopicsTableHeader);
+ this->lfAlways(1);
+ fOddRow = true;
std::map<string, const Definition*> items;
+ const RootDefinition::SubtopicContents& tableContents = fRoot->populator(name.c_str());
+ auto& data = tableContents.fMembers;
for (auto entry : data) {
- if (entry->csParent() != csParent) {
+ if (entry->csParent() != csParent && entry->topicParent() != topicParent) {
continue;
}
size_t start = entry->fName.find_last_of("::");
- string name = entry->fName.substr(string::npos == start ? 0 : start + 1);
- items[name] = entry;
+ string entryName = entry->fName.substr(string::npos == start ? 0 : start + 1);
+ items[entryName] = entry;
}
for (auto entry : items) {
if (entry.second->fDeprecated) {
@@ -1494,8 +2177,16 @@ void MdOut::subtopicOut(const TableContents& tableContents) {
parser.reportError("missing #Line");
continue;
}
- this->rowOut(entry.first.c_str(), string(oneLiner->fContentStart,
- oneLiner->fContentEnd - oneLiner->fContentStart));
+ string keyName = entry.first;
+ TextParser dummy(entry.second); // for reporting errors, which we won't do
+ if (!this->isDefined(dummy, keyName, BmhParser::Resolvable::kOut)) {
+ keyName = entry.second->fName;
+ size_t doubleColon = keyName.find("::");
+ SkASSERT(string::npos != doubleColon);
+ keyName = keyName.substr(doubleColon + 2);
+ }
+ this->rowOut(keyName.c_str(), string(oneLiner->fContentStart,
+ oneLiner->fContentEnd - oneLiner->fContentStart), false);
if (tableContents.fShowClones && entry.second->fCloned) {
int cloneNo = 2;
string builder = entry.second->fName;
@@ -1504,15 +2195,18 @@ void MdOut::subtopicOut(const TableContents& tableContents) {
}
builder += '_';
this->rowOut("",
- preformat(entry.second->formatFunction(Definition::Format::kOmitReturn)));
+ preformat(entry.second->formatFunction(Definition::Format::kOmitReturn)), true);
do {
string match = builder + to_string(cloneNo);
auto child = csParent->findClone(match);
if (!child) {
break;
}
- this->rowOut("", preformat(child->formatFunction(Definition::Format::kOmitReturn)));
+ this->rowOut("",
+ preformat(child->formatFunction(Definition::Format::kOmitReturn)), true);
} while (++cloneNo);
}
}
+ FPRINTF("</table>");
+ this->lfAlways(1);
}
diff --git a/tools/bookmaker/parserCommon.cpp b/tools/bookmaker/parserCommon.cpp
index 072c996df3..0ce45dc1ff 100644
--- a/tools/bookmaker/parserCommon.cpp
+++ b/tools/bookmaker/parserCommon.cpp
@@ -154,6 +154,7 @@ bool ParserCommon::writeBlockTrim(int size, const char* data) {
debug_out(size, data);
}
fprintf(fOut, "%.*s", size, data);
+ fWroteSomething = true;
int added = 0;
fLastChar = data[size - 1];
while (size > 0 && '\n' != data[--size]) {
diff --git a/tools/bookmaker/spellCheck.cpp b/tools/bookmaker/spellCheck.cpp
index 488ceacc7d..be17855f9b 100644
--- a/tools/bookmaker/spellCheck.cpp
+++ b/tools/bookmaker/spellCheck.cpp
@@ -42,12 +42,17 @@ private:
kColumn,
};
+ enum class PrintCheck {
+ kWordsOnly,
+ kAllowNumbers,
+ };
+
bool check(Definition* );
bool checkable(MarkType markType);
- void childCheck(const Definition* def, const char* start);
+ void childCheck(Definition* def, const char* start);
void leafCheck(const char* start, const char* end);
bool parseFromFile(const char* path) override { return true; }
- void printCheck(string str);
+ void printCheck(string str, PrintCheck);
void reset() override {
INHERITED::resetCommon();
@@ -168,7 +173,7 @@ bool SpellCheck::check(Definition* def) {
case MarkType::kDescription:
fInDescription = true;
break;
- case MarkType::kDoxygen:
+ case MarkType::kDetails:
break;
case MarkType::kDuration:
break;
@@ -182,8 +187,6 @@ bool SpellCheck::check(Definition* def) {
break;
case MarkType::kExternal:
break;
- case MarkType::kFile:
- break;
case MarkType::kFormula:
fInFormula = true;
break;
@@ -223,6 +226,8 @@ bool SpellCheck::check(Definition* def) {
} break;
case MarkType::kNoExample:
break;
+ case MarkType::kNoJustify:
+ break;
case MarkType::kOutdent:
break;
case MarkType::kParam: {
@@ -238,7 +243,13 @@ bool SpellCheck::check(Definition* def) {
fInCode = true;
this->wordCheck(paramParser.fChar - paramName, paramName);
fInCode = false;
- } break;
+ } break;
+ case MarkType::kPhraseDef:
+ break;
+ case MarkType::kPhraseParam:
+ break;
+ case MarkType::kPhraseRef:
+ break;
case MarkType::kPlatform:
break;
case MarkType::kPopulate:
@@ -271,7 +282,10 @@ bool SpellCheck::check(Definition* def) {
case MarkType::kSubstitute:
break;
case MarkType::kSubtopic:
- this->printCheck(printable);
+ // TODO: add a tag that allows subtopic labels in illustrations to skip spellcheck?
+ if (string::npos == fFileName.find("illustrations.bmh")) {
+ this->printCheck(printable, PrintCheck::kAllowNumbers);
+ }
break;
case MarkType::kTable:
break;
@@ -279,16 +293,11 @@ bool SpellCheck::check(Definition* def) {
break;
case MarkType::kText:
break;
- case MarkType::kTime:
- break;
case MarkType::kToDo:
break;
case MarkType::kTopic:
- this->printCheck(printable);
+ this->printCheck(printable, PrintCheck::kWordsOnly);
break;
- case MarkType::kTrack:
- // don't output children
- return true;
case MarkType::kTypedef:
break;
case MarkType::kUnion:
@@ -348,11 +357,11 @@ bool SpellCheck::checkable(MarkType markType) {
return BmhParser::Resolvable::kYes == fBmhParser.kMarkProps[(int) markType].fResolve;
}
-void SpellCheck::childCheck(const Definition* def, const char* start) {
+void SpellCheck::childCheck(Definition* def, const char* start) {
const char* end;
fLineCount = def->fLineCount;
if (def->isRoot()) {
- fRoot = const_cast<RootDefinition*>(def->asRoot());
+ fRoot = def->asRoot();
}
for (auto& child : def->fChildren) {
end = child->fStart;
@@ -459,9 +468,16 @@ void SpellCheck::leafCheck(const char* start, const char* end) {
} while (++chPtr <= end);
}
-void SpellCheck::printCheck(string str) {
+void SpellCheck::printCheck(string str, PrintCheck allowed) {
string word;
for (std::stringstream stream(str); stream >> word; ) {
+ if (PrintCheck::kAllowNumbers == allowed && (std::isdigit(word.back()) || 'x' == word.back())) {
+ // allow ###x for RGB_888x
+ if ((size_t) std::count_if(word.begin(), word.end() - 1,
+ [](unsigned char c){ return std::isdigit(c); } ) == word.length() - 1) {
+ continue;
+ }
+ }
wordCheck(word);
}
}