aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/bookmaker/mdOut.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/bookmaker/mdOut.cpp')
-rw-r--r--tools/bookmaker/mdOut.cpp1286
1 files changed, 990 insertions, 296 deletions
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);
}