streamline web documentation

This is the next step in reducing the amount of typing
in bookmaker, and presenting a web-base interface that
more closely matches the include documentation.

Moving towards making xxx_Reference solely contain
use documentation, and xxx_Overview (unpublished)
contain theory of operation documentation.

Fixed quite a few bugs along the way, and made
function and member documentation tighter.

TBR=caryclark@google.com
Docs-Preview: https://skia.org/?cl=160640
Change-Id: I763df4d59baa5dfd0177f0523294b5316434c4b0
Reviewed-on: https://skia-review.googlesource.com/c/160640
Reviewed-by: Cary Clark <caryclark@skia.org>
Commit-Queue: Cary Clark <caryclark@skia.org>
Auto-Submit: Cary Clark <caryclark@skia.org>
diff --git a/tools/bookmaker/bookmaker.cpp b/tools/bookmaker/bookmaker.cpp
index 08577b9..a4968ef 100644
--- a/tools/bookmaker/bookmaker.cpp
+++ b/tools/bookmaker/bookmaker.cpp
@@ -34,6 +34,9 @@
 
 /* todos:
 
+if #Subtopic contains #SeeAlso or #Example generate horizontal rule at end
+constexpr populated with filter inside subtopic does not have definition body
+
 #List needs '# content ##', formatting
 rewrap text to fit in some number of columns
 #Literal is inflexible, making the entire #Code block link-less (see $Literal in SkImageInfo)
@@ -120,13 +123,14 @@
 , { "Experimental", MarkType::kExperimental, R_Y, E_N, M_CS | M_MDCM | M_E }
 , { "External",     MarkType::kExternal,     R_Y, E_N, 0 }
 , { "File",         MarkType::kFile,         R_Y, E_N, M(Topic) }
+, { "Filter",       MarkType::kFilter,       R_N, E_N, M(Subtopic) | M(Code) }
 , { "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_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) }
+, { "In",           MarkType::kIn,           R_N, E_N, M_CSST | M_E | M(Method) | M(Typedef) | M(Code) }
 , { "Legend",       MarkType::kLegend,       R_Y, E_N, M(Table) }
 , { "Line",         MarkType::kLine,         R_N, E_N, M_CSST | M_E | M(Method) | M(Typedef) }
 , { "",             MarkType::kLink,         R_N, E_N, M(Anchor) }
@@ -143,7 +147,7 @@
 , { "",             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_CS | M(Code) }
+, { "Populate",     MarkType::kPopulate,     R_N, E_N, M(Code) }
 , { "Private",      MarkType::kPrivate,      R_N, E_N, M_CSST | M_MDCM | M_E }
 , { "Return",       MarkType::kReturn,       R_Y, E_N, M(Method) }
 , { "",             MarkType::kRow,          R_Y, E_N, M(Table) | M(List) }
@@ -562,6 +566,7 @@
         case MarkType::kDetails:
         case MarkType::kDuration:
         case MarkType::kExperimental:
+        case MarkType::kFilter:
         case MarkType::kHeight:
         case MarkType::kIllustration:
         case MarkType::kImage:
@@ -2312,6 +2317,7 @@
         case MarkType::kDuration:
         case MarkType::kExperimental:
         case MarkType::kFile:
+        case MarkType::kFilter:
         case MarkType::kHeight:
         case MarkType::kIllustration:
         case MarkType::kImage:
@@ -2767,7 +2773,7 @@
                 ParserCommon::OneFile::kYes)) {
             return -1;
         }
-        includeParser.writeCodeBlock(bmhParser);
+        includeParser.writeCodeBlock();
         MdOut mdOut(bmhParser, includeParser);
         mdOut.fDebugOut = FLAGS_stdout;
         mdOut.fValidate = FLAGS_validate;
diff --git a/tools/bookmaker/bookmaker.h b/tools/bookmaker/bookmaker.h
index d8b2042..160b715 100644
--- a/tools/bookmaker/bookmaker.h
+++ b/tools/bookmaker/bookmaker.h
@@ -106,6 +106,7 @@
     kExperimental,
     kExternal,
     kFile,
+    kFilter,
     kFormula,
     kFunction,
     kHeight,
@@ -1009,6 +1010,11 @@
         fParentIndex = fParent ? (int) fParent->fTokens.size() : -1;
     }
 
+    string simpleName() {
+        size_t doubleColon = fName.rfind("::");
+        return string::npos == doubleColon ? fName : fName.substr(doubleColon + 2);
+    }
+
     const Definition* subtopicParent() const {
         Definition* test = fParent;
         while (test) {
@@ -1711,11 +1717,26 @@
             SkASSERT(fIClassMap.end() != map || inProgress);
             return fIClassMap.end() != map ? map->second.fCode : "";
         }
+        if (MarkType::kConst == markType) {
+            auto map = fIConstMap.find(name);
+            SkASSERT(fIConstMap.end() != map);
+            return map->second->fCode;
+        }
+        if (MarkType::kDefine == markType) {
+            auto map = fIDefineMap.find(name);
+            SkASSERT(fIDefineMap.end() != map);
+            return map->second->fCode;
+        }
         if (MarkType::kEnum == markType || MarkType::kEnumClass == markType) {
             auto map = fIEnumMap.find(name);
             SkASSERT(fIEnumMap.end() != map);
             return map->second->fCode;
         }
+        if (MarkType::kTypedef == markType) {
+            auto map = fITypedefMap.find(name);
+            SkASSERT(fITypedefMap.end() != map);
+            return map->second->fCode;
+        }
         SkASSERT(0);
         return "";
     }
@@ -1739,6 +1760,7 @@
     void dumpTypedef(const Definition& , string className);
 
     string elidedCodeBlock(const Definition& );
+    string filteredBlock(string inContents, string filterContents);
     bool findComments(const Definition& includeDef, Definition* markupDef);
     Definition* findIncludeObject(const Definition& includeDef, MarkType markType,
                                   string typeName);
@@ -1810,7 +1832,6 @@
         fLastObject = nullptr;
         fPriorEnum = nullptr;
         fPriorObject = nullptr;
-        fAttrDeprecated = nullptr;
         fPrev = '\0';
         fInChar = false;
         fInCharCommentString = false;
@@ -1849,7 +1870,7 @@
     }
 
     void validate() const;
-    void writeCodeBlock(const BmhParser& );
+    void writeCodeBlock();
     string writeCodeBlock(const Definition&, MarkType );
     string writeCodeBlock(TextParser& i, MarkType , int indent);
 
@@ -1990,9 +2011,6 @@
         MarkType fMarkType;
     };
 
-    static const char gAttrDeprecated[];
-    static const size_t kAttrDeprecatedLen;
-
     vector<DefinitionMap> fMaps;
     unordered_map<string, Definition> fIncludeMap;
     list<Definition> fGlobals;
@@ -2012,7 +2030,6 @@
     Definition* fLastObject;
     Definition* fPriorEnum;
     Definition* fPriorObject;
-    const Definition* fAttrDeprecated;
     int fPriorIndex;
     const char* fIncludeWord;
     Elided fElided;
diff --git a/tools/bookmaker/includeParser.cpp b/tools/bookmaker/includeParser.cpp
index 50ba723..ad77119 100644
--- a/tools/bookmaker/includeParser.cpp
+++ b/tools/bookmaker/includeParser.cpp
@@ -9,9 +9,6 @@
 #include "SkOSFile.h"
 #include "SkOSPath.h"
 
-const char IncludeParser::gAttrDeprecated[] = "SK_ATTR_DEPRECATED";
-const size_t IncludeParser::kAttrDeprecatedLen = sizeof(gAttrDeprecated) - 1;
-
 const IncludeKey kKeyWords[] = {
     { "",           KeyWord::kNone,         KeyProperty::kNone           },
     { "SK_API",     KeyWord::kSK_API,       KeyProperty::kModifier       },
@@ -410,7 +407,11 @@
     int lastIndent = 0;
     bool lastDoubleMeUp = false;
     fCheck.reset();
-    this->codeBlockSpaces(result, startIndent);
+    if (MarkType::kDefine == markType) {
+        result = "#define ";
+    } else {
+        this->codeBlockSpaces(result, startIndent);
+    }
     do {
         if (!this->advanceInclude(i)) {
             continue;
@@ -610,19 +611,21 @@
     return result;
 }
 
-void IncludeParser::writeCodeBlock(const BmhParser& bmhParser) {
+void IncludeParser::writeCodeBlock() {
     for (auto& classMapper : fIClassMap) {
-        string className = classMapper.first;
-        auto finder = bmhParser.fClassMap.find(className);
-        if (bmhParser.fClassMap.end() != finder) {
-            classMapper.second.fCode = this->writeCodeBlock(classMapper.second, MarkType::kClass);
-            continue;
-        }
-        SkASSERT(string::npos != className.find("::"));
+        classMapper.second.fCode = this->writeCodeBlock(classMapper.second, MarkType::kClass);
     }
     for (auto& enumMapper : fIEnumMap) {
-            enumMapper.second->fCode = this->writeCodeBlock(*enumMapper.second,
-                    enumMapper.second->fMarkType);
+        enumMapper.second->fCode = this->writeCodeBlock(*enumMapper.second,
+                enumMapper.second->fMarkType);
+    }
+    for (auto& typedefMapper : fITypedefMap) {
+        typedefMapper.second->fCode = this->writeCodeBlock(*typedefMapper.second,
+                typedefMapper.second->fMarkType);
+    }
+    for (auto& defineMapper : fIDefineMap) {
+        defineMapper.second->fCode = this->writeCodeBlock(*defineMapper.second,
+                defineMapper.second->fMarkType);
     }
 }
 
@@ -754,10 +757,6 @@
                         }
                     }
                     if (!def) {
-                        if (gAttrDeprecated == token.fName) {
-                            fAttrDeprecated = &token;
-                            break;
-                        }
                         if (0 == token.fName.find("SkDEBUGCODE")) {
                             break;
                         }
@@ -1756,6 +1755,25 @@
     return this->writeCodeBlock(i, markType, 0);
 }
 
+ string IncludeParser::filteredBlock(string inContents, string filterContents) {
+    string result;
+    const unordered_map<string, Definition*>* mapPtr = nullptr;
+    MarkType markType = MarkType::kNone;
+    if ("Constant" == inContents) {
+        mapPtr = &fIConstMap;
+        markType = MarkType::kConst;
+    } else {
+        SkASSERT(0); // only Constant supported for now
+    }
+    for (auto entry : *mapPtr) {
+        if (string::npos == entry.first.find(filterContents)) {
+            continue;
+        }
+        result += this->writeCodeBlock(*entry.second, markType);
+    }
+    return result;
+}
+
 bool IncludeParser::findComments(const Definition& includeDef, Definition* markupDef) {
     // add comment preceding class, if any
     const Definition* parent = includeDef.fParent;
@@ -2071,6 +2089,7 @@
     markupChild->fTerminator = markupChild->fContentEnd;
     IClassDefinition& classDef = fIClassMap[markupDef->fName];
     classDef.fConsts[child->fName] = markupChild;
+    fIConstMap[child->fName] = markupChild;
     return true;
 }
 
@@ -2155,6 +2174,7 @@
         return false;
     }
     classDef.fDefines[nameStr] = markupChild;
+    fIDefineMap[nameStr] = markupChild;
     return true;
 }
 
@@ -2612,26 +2632,11 @@
         case Definition::Type::kBracket:
             switch (child->fBracket) {
                 case Bracket::kParen:
-                    if (fLastObject) {
-                        TextParser checkDeprecated(child->fFileName, fLastObject->fTerminator + 1,
-                                child->fStart, fLastObject->fLineCount);
-                        if (!checkDeprecated.eof()) {
-                            checkDeprecated.skipWhiteSpace();
-                            if (checkDeprecated.startsWith(gAttrDeprecated)) {
-                                fAttrDeprecated = child;
-                                break;
-                            }
-                        }
-                    }
                     {
                         auto tokenIter = child->fParent->fTokens.begin();
                         std::advance(tokenIter, child->fParentIndex);
                         tokenIter = std::prev(tokenIter);
                         TextParser previousToken(&*tokenIter);
-                        if (previousToken.startsWith(gAttrDeprecated)) {
-                            fAttrDeprecated = &*tokenIter;
-                            break;
-                        }
                         if (this->isMember(*tokenIter)) {
                             break;
                         }
@@ -2653,11 +2658,6 @@
                     if (!this->parseMethod(child, markupDef)) {
                         return child->reportError<bool>("failed to parse method");
                     }
-                    if (fAttrDeprecated) {
-                        Definition* lastMethod = &markupDef->fTokens.back();
-                        lastMethod->fDeprecated = true;
-                        fAttrDeprecated = nullptr;
-                    }
                 break;
                 case Bracket::kSlashSlash:
                 case Bracket::kSlashStar:
@@ -2763,6 +2763,7 @@
     IClassDefinition& classDef = fIClassMap[markupDef->fName];
     classDef.fTypedefs[nameStr] = markupChild;
     child->fName = markupDef->fName + "::" + nameStr;
+    fITypedefMap[child->fName] = markupChild;
     return true;
 }
 
@@ -3337,6 +3338,9 @@
     if (fIEnumMap.end() != fIEnumMap.find(root)) {
         return true;
     }
+    if (fITypedefMap.end() != fITypedefMap.find(root)) {
+        return true;
+    }
     if (fIFunctionMap.end() != fIFunctionMap.find(root)) {
         return true;
     }
diff --git a/tools/bookmaker/includeWriter.cpp b/tools/bookmaker/includeWriter.cpp
index 713535f..383a6a5 100644
--- a/tools/bookmaker/includeWriter.cpp
+++ b/tools/bookmaker/includeWriter.cpp
@@ -1536,7 +1536,6 @@
                 continue;
             }
             const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 :
-                    fAttrDeprecated ? fAttrDeprecated->fContentStart - 1 :
                     child.fContentStart;
             if (Definition::Type::kBracket == def->fType && Bracket::kDebugCode == def->fBracket) {
                 auto tokenIter = def->fParent->fTokens.begin();
@@ -1581,11 +1580,6 @@
             }
             this->methodOut(method, child);
             sawConst = false;
-            if (fAttrDeprecated) {
-                startDef = fAttrDeprecated;
-                this->setStartBack(fAttrDeprecated->fContentStart, fAttrDeprecated);
-                fAttrDeprecated = nullptr;
-            }
             continue;
         }
         if (Definition::Type::kKeyWord == child.fType) {
@@ -1891,9 +1885,6 @@
                 }
                 continue;
             }
-            if (fAttrDeprecated) {
-                continue;
-            }
             if (KeyWord::kDefine == child.fKeyWord && this->defineOut(child)) {
                 fDeferComment = nullptr;
                 continue;
@@ -1980,10 +1971,6 @@
                 memberStart = &child;
                 staticOnly = false;
             }
-            if (kAttrDeprecatedLen == (size_t) (child.fContentEnd - child.fContentStart) &&
-                    !strncmp(gAttrDeprecated, child.fStart, kAttrDeprecatedLen)) {
-                fAttrDeprecated = &child;
-            }
             continue;
         }
         if (Definition::Type::kPunctuation == child.fType) {
diff --git a/tools/bookmaker/mdOut.cpp b/tools/bookmaker/mdOut.cpp
index 8361e96..0f322b0 100644
--- a/tools/bookmaker/mdOut.cpp
+++ b/tools/bookmaker/mdOut.cpp
@@ -1494,17 +1494,18 @@
         case MarkType::kBug:
             break;
         case MarkType::kClass:
-        case MarkType::kStruct: {
+        case MarkType::kStruct:
             fRoot = def->asRoot();
-        //    this->mdHeaderOut(1);
-            this->lfAlways(1);
+            this->lfAlways(2);
             if (MarkType::kStruct == def->fMarkType) {
                 this->htmlOut(anchorDef(def->fFiddle, ""));
             } else {
                 this->htmlOut(anchorDef(this->linkName(def), ""));
             }
-        //    this->lf(1);
-            } break;
+            this->lfAlways(2);
+            FPRINTF("---");
+            this->lf(2);
+            break;
         case MarkType::kCode:
             this->lfAlways(2);
             FPRINTF("<pre style=\"padding: 1em 1em 1em 1em;"
@@ -1596,11 +1597,6 @@
             FPRINTF("%s", out_table_data_description_start().c_str()); // start of Description
             this->lfAlways(1);
         } break;
-        case MarkType::kDefine:
-            this->mdHeaderOut(2);
-            this->htmlOut(anchorDef(def->fFiddle, "Define " + def->fName));
-            this->lf(2);
-            break;
         case MarkType::kDeprecated:
             this->writeString(def->fParent->incompleteMessage(
                     Definition::DetailsType::kSentence).c_str());
@@ -1615,10 +1611,13 @@
             break;
         case MarkType::kDuration:
             break;
+        case MarkType::kDefine:
         case MarkType::kEnum:
         case MarkType::kEnumClass:
-            this->mdHeaderOut(2);
-            this->htmlOut(anchorDef(def->fFiddle, "Enum " + def->fName));
+            this->lfAlways(2);
+            this->htmlOut(anchorDef(def->fFiddle, ""));
+            this->lfAlways(2);
+            FPRINTF("---");
             this->lf(2);
             break;
         case MarkType::kExample: {
@@ -1667,6 +1666,8 @@
             break;
         case MarkType::kFile:
             break;
+        case MarkType::kFilter:
+            break;
         case MarkType::kFormula:
             break;
         case MarkType::kFunction:
@@ -1717,19 +1718,22 @@
             fBmhParser.fMC = def->fContentStart[0];
             break;
         case MarkType::kMethod: {
-            string method_name = def->methodName();
-            string formattedStr = def->formatFunction(Definition::Format::kIncludeReturn);
-			this->lfAlways(2);
-            this->htmlOut(this->anchorDef(def->fFiddle, ""));
-			if (!def->isClone()) {
+            this->lfAlways(2);
+			if (false && !def->isClone()) {
+                string method_name = def->methodName();
                 this->mdHeaderOutLF(2, 1);
-                FPRINTF("%s", method_name.c_str());
-			}
+                this->htmlOut(this->anchorDef(def->fFiddle, method_name));
+			} else {
+                this->htmlOut(this->anchorDef(def->fFiddle, ""));
+            }
+            this->lfAlways(2);
+            FPRINTF("---");
 			this->lf(2);
 
             // TODO: put in css spec that we can define somewhere else (if markup supports that)
             // TODO: 50em below should match limit = 80 in formatFunction()
             this->writePending();
+            string formattedStr = def->formatFunction(Definition::Format::kIncludeReturn);
             string preformattedStr = preformat(formattedStr);
             string references = this->addReferences(&preformattedStr.front(),
                     &preformattedStr.back() + 1, BmhParser::Resolvable::kSimple);
@@ -1845,83 +1849,62 @@
             break;
         case MarkType::kPlatform:
             break;
-        case MarkType::kPopulate:
-            if (MarkType::kSubtopic == def->fParent->fMarkType) {
-                string name = def->fParent->fName;
-                if (string::npos != name.find(SubtopicKeys::kOverview)) {
-                    this->subtopicsOut(def->fParent);
-                } else {
-                    this->subtopicOut(name);
-                }
-            } else if (MarkType::kClass == def->fParent->fMarkType
-                    || MarkType::kStruct == def->fParent->fMarkType) {
-                Definition* parent = def->fParent;
- //               SkASSERT(parent->csParent());
-                if (!parent->csParent()) {
-                    this->subtopicsOut(def->fParent);
-                }
-                // if class or struct contains constants, and doesn't contain subtopic kConstant,
-                //add it and add a child populate
-                const Definition* subtopic = parent->subtopicParent();
-                const Definition* topic = parent->topicParent();
-                for (auto item : SubtopicKeys::kGeneratedSubtopics) {
-                    if (SubtopicKeys::kRelatedFunctions == item) {
-                        continue;
-                    }
-                    if (SubtopicKeys::kMemberFunctions == item) {
-                        continue;
-                    }
-                    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);
-                        string itemStr(item);
-                        std::replace(itemStr.begin(), itemStr.end(), '_', ' ');
-                        this->htmlOut(anchorDef(subname, itemStr));
-                        this->lf(2);
-                        // generate populate
-                        this->subtopicOut(item);
-                    }
-                }
-            } else {
-                Definition* parent = def->fParent;
-                SkASSERT(parent && MarkType::kCode == parent->fMarkType);
-                // find include matching code parent
-                Definition* grand = parent->fParent;
-                SkASSERT(grand);
-                if (MarkType::kClass == grand->fMarkType
-                        || MarkType::kStruct == grand->fMarkType
-                        || MarkType::kEnum == grand->fMarkType
-                        || MarkType::kEnumClass == grand->fMarkType) {
-                    string codeBlock = fIncludeParser.codeBlock(*grand, fInProgress);
-                    this->resolveOut(codeBlock.c_str(), codeBlock.c_str() + codeBlock.length(),
-                            this->resolvable(parent));
-                } else {
-                    SkASSERT(MarkType::kTopic == grand->fMarkType);
-                    // use bmh file name to find include file name
-                    size_t start = grand->fFileName.rfind("Sk");
-                    SkASSERT(start != string::npos);
-                    size_t end = grand->fFileName.rfind("_Reference");
-                    SkASSERT(end != string::npos && end > start);
-                    string incName(grand->fFileName.substr(start, end - start));
-                    const Definition* includeDef = fIncludeParser.include(incName + ".h");
-                    SkASSERT(includeDef);
-                    string codeBlock;
-                    this->addCodeBlock(includeDef, codeBlock);
-                    this->resolveOut(codeBlock.c_str(), codeBlock.c_str() + codeBlock.length(),
-                            this->resolvable(parent));
-                }
+        case MarkType::kPopulate: {
+            Definition* parent = def->fParent;
+            SkASSERT(parent && MarkType::kCode == parent->fMarkType);
+            auto inDef = std::find_if(parent->fChildren.begin(), parent->fChildren.end(),
+                    [](const Definition* child) { return MarkType::kIn == child->fMarkType; });
+            if (parent->fChildren.end() != inDef) {
+                auto filterDef = std::find_if(parent->fChildren.begin(), parent->fChildren.end(),
+                        [](const Definition* child) { return MarkType::kFilter == child->fMarkType; });
+                SkASSERT(parent->fChildren.end() != filterDef);
+                string codeBlock = fIncludeParser.filteredBlock(
+                        string((*inDef)->fContentStart, (*inDef)->length()),
+                        string((*filterDef)->fContentStart, (*filterDef)->length()));
+                this->resolveOut(codeBlock.c_str(), codeBlock.c_str() + codeBlock.length(),
+                        this->resolvable(parent));
+                break;
             }
-            break;
+            // find include matching code parent
+            Definition* grand = parent->fParent;
+            SkASSERT(grand);
+            if (MarkType::kClass == grand->fMarkType
+                    || MarkType::kStruct == grand->fMarkType
+                    || MarkType::kEnum == grand->fMarkType
+                    || MarkType::kEnumClass == grand->fMarkType
+                    || MarkType::kTypedef == grand->fMarkType
+                    || MarkType::kDefine == grand->fMarkType) {
+                string codeBlock = fIncludeParser.codeBlock(*grand, fInProgress);
+                this->resolveOut(codeBlock.c_str(), codeBlock.c_str() + codeBlock.length(),
+                        this->resolvable(parent));
+            } else if (MarkType::kTopic == grand->fMarkType) {
+                // use bmh file name to find include file name
+                size_t start = grand->fFileName.rfind("Sk");
+                SkASSERT(start != string::npos);
+                size_t end = grand->fFileName.rfind("_Reference");
+                SkASSERT(end != string::npos && end > start);
+                string incName(grand->fFileName.substr(start, end - start));
+                const Definition* includeDef = fIncludeParser.include(incName + ".h");
+                SkASSERT(includeDef);
+                string codeBlock;
+                this->addCodeBlock(includeDef, codeBlock);
+                this->resolveOut(codeBlock.c_str(), codeBlock.c_str() + codeBlock.length(),
+                        this->resolvable(parent));
+            } else {
+                SkASSERT(MarkType::kSubtopic == grand->fMarkType);
+                auto inTag = std::find_if(grand->fChildren.begin(), grand->fChildren.end(),
+                        [](Definition* child){return MarkType::kIn == child->fMarkType;});
+                SkASSERT(grand->fChildren.end() != inTag);
+                auto filterTag = std::find_if(grand->fChildren.begin(), grand->fChildren.end(),
+                        [](Definition* child){return MarkType::kFilter == child->fMarkType;});
+                SkASSERT(grand->fChildren.end() != filterTag);
+                string inContents((*inTag)->fContentStart, (*inTag)->length());
+                string filterContents((*filterTag)->fContentStart, (*filterTag)->length());
+                string filteredBlock = fIncludeParser.filteredBlock(inContents, filterContents);
+                this->resolveOut(filteredBlock.c_str(), filteredBlock.c_str()
+                        + filteredBlock.length(), this->resolvable(parent));
+            }
+            } break;
         case MarkType::kPrivate:
             this->writeString("Private:");
             this->writeSpace();
@@ -1971,18 +1954,29 @@
             break;
         case MarkType::kSubtopic:
             fSubtopic = def->asRoot();
-            this->mdHeaderOut(2);
-            if (SubtopicKeys::kOverview == def->fName) {
+            if (false && SubtopicKeys::kOverview == def->fName) {
                 this->writeString(def->fName);
             } else {
-                this->htmlOut(anchorDef(def->fName, printable));
+                this->lfAlways(2);
+                this->htmlOut(anchorDef(def->fName, ""));
+            }
+            if (std::any_of(def->fChildren.begin(), def->fChildren.end(),
+                    [](Definition* child) {
+                    return MarkType::kSeeAlso == child->fMarkType
+                    || MarkType::kExample == child->fMarkType
+                    || MarkType::kNoExample == child->fMarkType;
+            })) {
+                this->lfAlways(2);
+                FPRINTF("---");
             }
             this->lf(2);
+#if 0
             // 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].fPlural);
             }
+#endif
             // 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;})) {
@@ -2023,9 +2017,11 @@
 //            this->lf(1);
             } break;
         case MarkType::kTypedef:
-            this->mdHeaderOut(2);
-            this->htmlOut(anchorDef(def->fFiddle, "Typedef " + def->fName));
-            this->lf(1);
+            this->lfAlways(2);
+            this->htmlOut(anchorDef(def->fFiddle, ""));
+            this->lfAlways(2);
+            FPRINTF("---");
+            this->lf(2);
             break;
         case MarkType::kUnion:
             break;
@@ -2131,9 +2127,6 @@
             } break;
         case MarkType::kMethod:
             fMethod = nullptr;
-            this->lfAlways(2);
-            FPRINTF("---");
-            this->lf(2);
             break;
         case MarkType::kConst:
         case MarkType::kMember:
diff --git a/tools/bookmaker/spellCheck.cpp b/tools/bookmaker/spellCheck.cpp
index f5786b9..75a01f7 100644
--- a/tools/bookmaker/spellCheck.cpp
+++ b/tools/bookmaker/spellCheck.cpp
@@ -200,6 +200,8 @@
             break;
         case MarkType::kFile:
             break;
+        case MarkType::kFilter:
+            break;
         case MarkType::kFormula:
             fInFormula = true;
             break;