1 /* 2 * Copyright 2017 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "bookmaker.h" 9 10 void IncludeWriter::constOut(const Definition* memberStart, const Definition& child, 11 const Definition* bmhConst) { 12 const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 : 13 memberStart->fContentStart; 14 this->writeBlockTrim((int) (bodyEnd - fStart), fStart); // may write nothing 15 this->lf(2); 16 this->writeCommentHeader(); 17 fIndent += 4; 18 this->descriptionOut(bmhConst, SkipFirstLine::kYes); 19 fIndent -= 4; 20 this->writeCommentTrailer(); 21 fStart = memberStart->fContentStart; 22 } 23 24 void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirstLine) { 25 const char* commentStart = def->fContentStart; 26 if (SkipFirstLine::kYes == skipFirstLine) { 27 TextParser parser(def); 28 SkAssertResult(parser.skipLine()); 29 commentStart = parser.fChar; 30 } 31 int commentLen = (int) (def->fContentEnd - commentStart); 32 bool breakOut = false; 33 SkDEBUGCODE(bool wroteCode = false); 34 if (def->fDeprecated) { 35 this->writeString(def->fToBeDeprecated ? "To be deprecated soon." : "Deprecated."); 36 this->lfcr(); 37 } 38 for (auto prop : def->fChildren) { 39 switch (prop->fMarkType) { 40 case MarkType::kCode: { 41 bool literal = false; 42 bool literalOutdent = false; 43 commentLen = (int) (prop->fStart - commentStart); 44 if (commentLen > 0) { 45 SkASSERT(commentLen < 1000); 46 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) { 47 this->lf(2); 48 } 49 } 50 size_t childSize = prop->fChildren.size(); 51 if (childSize) { 52 SkASSERT(1 == childSize || 2 == childSize); // incomplete 53 SkASSERT(MarkType::kLiteral == prop->fChildren[0]->fMarkType); 54 SkASSERT(1 == childSize || MarkType::kOutdent == prop->fChildren[1]->fMarkType); 55 commentStart = prop->fChildren[childSize - 1]->fContentStart; 56 literal = true; 57 literalOutdent = 2 == childSize && 58 MarkType::kOutdent == prop->fChildren[1]->fMarkType; 59 } 60 commentLen = (int) (prop->fContentEnd - commentStart); 61 SkASSERT(commentLen > 0); 62 if (literal) { 63 if (!literalOutdent) { 64 fIndent += 4; 65 } 66 this->writeBlockIndent(commentLen, commentStart); 67 this->lf(2); 68 if (!literalOutdent) { 69 fIndent -= 4; 70 } 71 commentStart = prop->fTerminator; 72 SkDEBUGCODE(wroteCode = true); 73 } 74 } break; 75 case MarkType::kDefinedBy: 76 commentStart = prop->fTerminator; 77 break; 78 case MarkType::kBug: { 79 string bugstr("(see skbug.com/" + string(prop->fContentStart, 80 prop->fContentEnd - prop->fContentStart) + ')'); 81 this->writeString(bugstr); 82 this->lfcr(); 83 } 84 case MarkType::kDeprecated: 85 case MarkType::kPrivate: 86 commentLen = (int) (prop->fStart - commentStart); 87 if (commentLen > 0) { 88 SkASSERT(commentLen < 1000); 89 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) { 90 this->lfcr(); 91 } 92 } 93 commentStart = prop->fContentStart; 94 if (def->fToBeDeprecated) { 95 commentStart += 4; // skip over "soon" // FIXME: this is awkward 96 } else if (MarkType::kBug == prop->fMarkType) { 97 commentStart = prop->fContentEnd; 98 } 99 commentLen = (int) (prop->fContentEnd - commentStart); 100 if (commentLen > 0) { 101 this->writeBlockIndent(commentLen, commentStart); 102 if ('\n' != commentStart[commentLen - 1] && '\n' == commentStart[commentLen]) { 103 this->lfcr(); 104 } 105 } 106 commentStart = prop->fTerminator; 107 commentLen = (int) (def->fContentEnd - commentStart); 108 break; 109 case MarkType::kExperimental: 110 this->writeString("EXPERIMENTAL:"); 111 this->writeSpace(); 112 commentStart = prop->fContentStart; 113 commentLen = (int) (prop->fContentEnd - commentStart); 114 if (commentLen > 0) { 115 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) { 116 this->lfcr(); 117 } 118 } 119 commentStart = prop->fTerminator; 120 commentLen = (int) (def->fContentEnd - commentStart); 121 break; 122 case MarkType::kFormula: { 123 commentLen = prop->fStart - commentStart; 124 if (commentLen > 0) { 125 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) { 126 if (commentLen > 1 && '\n' == prop->fStart[-1] && 127 '\n' == prop->fStart[-2]) { 128 this->lf(1); 129 } else { 130 this->writeSpace(); 131 } 132 } 133 } 134 int saveIndent = fIndent; 135 if (fIndent < fColumn + 1) { 136 fIndent = fColumn + 1; 137 } 138 this->writeBlockIndent(prop->length(), prop->fContentStart); 139 fIndent = saveIndent; 140 commentStart = prop->fTerminator; 141 commentLen = (int) (def->fContentEnd - commentStart); 142 if (commentLen > 1 && '\n' == commentStart[0] && '\n' == commentStart[1]) { 143 this->lf(2); 144 } else { 145 SkASSERT('\n' == prop->fTerminator[0]); 146 if ('.' != prop->fTerminator[1] && !fLinefeeds) { 147 this->writeSpace(); 148 } 149 } 150 } break; 151 case MarkType::kIn: 152 case MarkType::kLine: 153 case MarkType::kToDo: 154 commentLen = (int) (prop->fStart - commentStart); 155 if (commentLen > 0) { 156 SkASSERT(commentLen < 1000); 157 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) { 158 this->lfcr(); 159 } 160 } 161 commentStart = prop->fTerminator; 162 commentLen = (int) (def->fContentEnd - commentStart); 163 break; 164 case MarkType::kList: 165 commentLen = prop->fStart - commentStart; 166 if (commentLen > 0) { 167 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, 168 Phrase::kNo)) { 169 this->lfcr(); 170 } 171 } 172 for (auto row : prop->fChildren) { 173 SkASSERT(MarkType::kRow == row->fMarkType); 174 for (auto column : row->fChildren) { 175 SkASSERT(MarkType::kColumn == column->fMarkType); 176 this->writeString("-"); 177 this->writeSpace(); 178 this->descriptionOut(column, SkipFirstLine::kNo); 179 this->lf(1); 180 } 181 } 182 commentStart = prop->fTerminator; 183 commentLen = (int) (def->fContentEnd - commentStart); 184 if ('\n' == commentStart[0] && '\n' == commentStart[1]) { 185 this->lf(2); 186 } 187 break; 188 default: 189 commentLen = (int) (prop->fStart - commentStart); 190 breakOut = true; 191 } 192 if (breakOut) { 193 break; 194 } 195 } 196 SkASSERT(wroteCode || (commentLen > 0 && commentLen < 1500) || def->fDeprecated); 197 if (commentLen > 0) { 198 this->rewriteBlock(commentLen, commentStart, Phrase::kNo); 199 } 200 } 201 202 void IncludeWriter::enumHeaderOut(const RootDefinition* root, 203 const Definition& child) { 204 const Definition* enumDef = nullptr; 205 const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 : 206 child.fContentStart; 207 this->writeBlockTrim((int) (bodyEnd - fStart), fStart); // may write nothing 208 this->lf(2); 209 if (fIndentNext) { 210 fIndent += 4; 211 fIndentNext = false; 212 } 213 fDeferComment = nullptr; 214 fStart = child.fContentStart; 215 const auto& nameDef = child.fTokens.front(); 216 string fullName; 217 if (nullptr != nameDef.fContentEnd) { 218 TextParser enumClassCheck(&nameDef); 219 const char* start = enumClassCheck.fStart; 220 size_t len = (size_t) (enumClassCheck.fEnd - start); 221 bool enumClass = enumClassCheck.skipExact("class "); 222 if (enumClass) { 223 start = enumClassCheck.fChar; 224 const char* end = enumClassCheck.anyOf(" \n;{"); 225 len = (size_t) (end - start); 226 } 227 string enumName(start, len); 228 if (enumClass) { 229 child.fChildren[0]->fName = enumName; 230 } 231 fullName = root->fName + "::" + enumName; 232 enumDef = root->find(enumName, RootDefinition::AllowParens::kNo); 233 if (!enumDef) { 234 enumDef = root->find(fullName, RootDefinition::AllowParens::kNo); 235 } 236 SkASSERT(enumDef); 237 // child[0] should be #Code comment starts at child[0].fTerminator 238 // though skip until #Code is found (in case there's a #ToDo, etc) 239 // child[1] should be #Const comment ends at child[1].fStart 240 // comment becomes enum header (if any) 241 } else { 242 string enumName(root->fName); 243 enumName += "::_anonymous"; 244 if (fAnonymousEnumCount > 1) { 245 enumName += '_' + to_string(fAnonymousEnumCount); 246 } 247 enumDef = root->find(enumName, RootDefinition::AllowParens::kNo); 248 SkASSERT(enumDef); 249 ++fAnonymousEnumCount; 250 } 251 Definition* codeBlock = nullptr; 252 const char* commentStart = nullptr; 253 bool wroteHeader = false; 254 bool lastAnchor = false; 255 SkDEBUGCODE(bool foundConst = false); 256 for (auto test : enumDef->fChildren) { 257 if (MarkType::kCode == test->fMarkType) { 258 SkASSERT(!codeBlock); // FIXME: check enum for correct order earlier 259 codeBlock = test; 260 commentStart = codeBlock->fTerminator; 261 continue; 262 } 263 if (!codeBlock) { 264 continue; 265 } 266 const char* commentEnd = test->fStart; 267 if (!wroteHeader && 268 !this->contentFree((int) (commentEnd - commentStart), commentStart)) { 269 if (fIndentNext) { 270 fIndent += 4; 271 } 272 this->writeCommentHeader(); 273 this->writeString("\\enum"); 274 if (fullName.length() > 0) { 275 this->writeSpace(); 276 this->writeString(fullName.c_str()); 277 } 278 fIndent += 4; 279 this->lfcr(); 280 wroteHeader = true; 281 } 282 if (lastAnchor) { 283 if (commentEnd - commentStart > 1) { 284 SkASSERT('\n' == commentStart[0]); 285 if (' ' == commentStart[1]) { 286 this->writeSpace(); 287 } 288 } 289 lastAnchor = false; 290 } 291 this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo); 292 if (MarkType::kAnchor == test->fMarkType) { 293 bool newLine = commentEnd - commentStart > 1 && 294 '\n' == commentEnd[-1] && '\n' == commentEnd[-2]; 295 commentStart = test->fContentStart; 296 commentEnd = test->fChildren[0]->fStart; 297 if (newLine) { 298 this->lf(2); 299 } else { 300 this->writeSpace(); 301 } 302 this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo); 303 lastAnchor = true; // this->writeSpace(); 304 } 305 commentStart = test->fTerminator; 306 if (MarkType::kConst == test->fMarkType) { 307 SkASSERT(codeBlock); // FIXME: check enum for correct order earlier 308 SkDEBUGCODE(foundConst = true); 309 break; 310 } 311 } 312 SkASSERT(codeBlock); 313 SkASSERT(foundConst); 314 if (wroteHeader) { 315 fIndent -= 4; 316 this->lfcr(); 317 this->writeCommentTrailer(); 318 } 319 Definition* braceHolder = child.fChildren[0]; 320 if (KeyWord::kClass == braceHolder->fKeyWord) { 321 braceHolder = braceHolder->fChildren[0]; 322 } 323 bodyEnd = braceHolder->fContentStart; 324 SkASSERT('{' == bodyEnd[0]); 325 ++bodyEnd; 326 this->lfcr(); 327 this->writeBlock((int) (bodyEnd - fStart), fStart); // write include "enum Name {" 328 fIndent += 4; 329 this->singleLF(); 330 fStart = bodyEnd; 331 fEnumDef = enumDef; 332 } 333 334 void IncludeWriter::enumMembersOut(const RootDefinition* root, Definition& child) { 335 // iterate through include tokens and find how much remains for 1 line comments 336 // put ones that fit on same line, ones that are too big on preceding line? 337 const Definition* currentEnumItem = nullptr; 338 const char* commentStart = nullptr; 339 const char* lastEnd = nullptr; 340 int commentLen = 0; 341 enum class State { 342 kNoItem, 343 kItemName, 344 kItemValue, 345 kItemComment, 346 }; 347 State state = State::kNoItem; 348 vector<IterState> iterStack; 349 iterStack.emplace_back(child.fTokens.begin(), child.fTokens.end()); 350 IterState* iterState = &iterStack[0]; 351 bool preprocessorWord = false; 352 const char* preprocessStart = nullptr; 353 const char* preprocessEnd = nullptr; 354 for (int onePast = 0; onePast < 2; onePast += iterState->fDefIter == iterState->fDefEnd) { 355 Definition* token = onePast ? nullptr : &*iterState->fDefIter++; 356 if (token && Definition::Type::kBracket == token->fType) { 357 if (Bracket::kSlashSlash == token->fBracket) { 358 fStart = token->fContentEnd; 359 continue; // ignore old inline comments 360 } 361 if (Bracket::kSlashStar == token->fBracket) { 362 fStart = token->fContentEnd + 1; 363 continue; // ignore old inline comments 364 } 365 if (Bracket::kPound == token->fBracket) { // preprocessor wraps member 366 preprocessStart = token->fContentStart; 367 if (KeyWord::kIf == token->fKeyWord || KeyWord::kIfdef == token->fKeyWord) { 368 iterStack.emplace_back(token->fTokens.begin(), token->fTokens.end()); 369 iterState = &iterStack.back(); 370 preprocessorWord = true; 371 } else if (KeyWord::kEndif == token->fKeyWord) { 372 iterStack.pop_back(); 373 iterState = &iterStack.back(); 374 preprocessEnd = token->fContentEnd; 375 } else { 376 SkASSERT(0); // incomplete 377 } 378 continue; 379 } 380 SkASSERT(0); // incomplete 381 } 382 if (token && Definition::Type::kWord != token->fType) { 383 SkASSERT(0); // incomplete 384 } 385 if (preprocessorWord) { 386 preprocessorWord = false; 387 preprocessEnd = token->fContentEnd; 388 continue; 389 } 390 if (token && State::kItemName == state) { 391 TextParser enumLine(token->fFileName, lastEnd, 392 token->fContentStart, token->fLineCount); 393 const char* end = enumLine.anyOf(",}="); 394 SkASSERT(end); 395 state = '=' == *end ? State::kItemValue : State::kItemComment; 396 if (State::kItemValue == state) { // write enum value 397 this->indentToColumn(fEnumItemValueTab); 398 this->writeString("="); 399 this->writeSpace(); 400 lastEnd = token->fContentEnd; 401 this->writeBlock((int) (lastEnd - token->fContentStart), 402 token->fContentStart); // write const value if any 403 continue; 404 } 405 } 406 if (token && State::kItemValue == state) { 407 TextParser valueEnd(token->fFileName, lastEnd, 408 token->fContentStart, token->fLineCount); 409 const char* end = valueEnd.anyOf(",}"); 410 if (!end) { // write expression continuation 411 if (' ' == lastEnd[0]) { 412 this->writeSpace(); 413 } 414 this->writeBlock((int) (token->fContentEnd - lastEnd), lastEnd); 415 continue; 416 } 417 } 418 if (State::kNoItem != state) { 419 this->writeString(","); 420 SkASSERT(currentEnumItem); 421 if (currentEnumItem->fShort) { 422 this->indentToColumn(fEnumItemCommentTab); 423 if (commentLen || currentEnumItem->fDeprecated) { 424 this->writeString("//!<"); 425 this->writeSpace(); 426 if (currentEnumItem->fDeprecated) { 427 this->writeString(child.fToBeDeprecated ? "to be deprecated soon" 428 : "deprecated"); 429 } else { 430 this->rewriteBlock(commentLen, commentStart, Phrase::kNo); 431 } 432 } 433 } 434 if (onePast) { 435 fIndent -= 4; 436 } 437 this->lfcr(); 438 if (preprocessStart) { 439 SkASSERT(preprocessEnd); 440 int saveIndent = fIndent; 441 fIndent = SkTMax(0, fIndent - 8); 442 this->lf(2); 443 this->writeBlock((int) (preprocessEnd - preprocessStart), preprocessStart); 444 this->lfcr(); 445 fIndent = saveIndent; 446 preprocessStart = nullptr; 447 preprocessEnd = nullptr; 448 } 449 if (token && State::kItemValue == state) { 450 fStart = token->fContentStart; 451 } 452 state = State::kNoItem; 453 } 454 SkASSERT(State::kNoItem == state); 455 if (onePast) { 456 break; 457 } 458 SkASSERT(token); 459 string itemName = root->fName + "::"; 460 if (KeyWord::kClass == child.fParent->fKeyWord) { 461 itemName += child.fParent->fName + "::"; 462 } 463 itemName += string(token->fContentStart, (int) (token->fContentEnd - token->fContentStart)); 464 for (auto& enumItem : fEnumDef->fChildren) { 465 if (MarkType::kConst != enumItem->fMarkType) { 466 continue; 467 } 468 if (itemName != enumItem->fName) { 469 continue; 470 } 471 currentEnumItem = enumItem; 472 break; 473 } 474 SkASSERT(currentEnumItem); 475 // if description fits, it goes after item 476 commentStart = currentEnumItem->fContentStart; 477 const char* commentEnd; 478 if (currentEnumItem->fChildren.size() > 0) { 479 commentEnd = currentEnumItem->fChildren[0]->fStart; 480 } else { 481 commentEnd = currentEnumItem->fContentEnd; 482 } 483 TextParser enumComment(fFileName, commentStart, commentEnd, currentEnumItem->fLineCount); 484 bool isDeprecated = false; 485 if (enumComment.skipToLineStart()) { // skip const value 486 commentStart = enumComment.fChar; 487 commentLen = (int) (commentEnd - commentStart); 488 } else { 489 const Definition* childDef = currentEnumItem->fChildren[0]; 490 isDeprecated = MarkType::kDeprecated == childDef->fMarkType; 491 if (MarkType::kPrivate == childDef->fMarkType || isDeprecated) { 492 commentStart = childDef->fContentStart; 493 if (currentEnumItem->fToBeDeprecated) { 494 SkASSERT(isDeprecated); 495 commentStart += 4; // skip over "soon" // FIXME: this is awkward 496 } 497 commentLen = (int) (childDef->fContentEnd - commentStart); 498 } 499 } 500 // FIXME: may assert here if there's no const value 501 // should have detected and errored on that earlier when enum fContentStart was set 502 SkASSERT((commentLen > 0 && commentLen < 1000) || isDeprecated); 503 if (!currentEnumItem->fShort) { 504 this->writeCommentHeader(); 505 fIndent += 4; 506 bool wroteLineFeed = false; 507 if (isDeprecated) { 508 this->writeString(currentEnumItem->fToBeDeprecated 509 ? "To be deprecated soon." : "Deprecated."); 510 } 511 wroteLineFeed = Wrote::kLF == 512 this->rewriteBlock(commentLen, commentStart, Phrase::kNo); 513 fIndent -= 4; 514 if (wroteLineFeed || fColumn > 100 - 3 /* space * / */ ) { 515 this->lfcr(); 516 } else { 517 this->writeSpace(); 518 } 519 this->writeCommentTrailer(); 520 } 521 lastEnd = token->fContentEnd; 522 this->lfcr(); 523 if (',' == fStart[0]) { 524 ++fStart; 525 } 526 this->writeBlock((int) (lastEnd - fStart), fStart); // enum item name 527 fStart = token->fContentEnd; 528 state = State::kItemName; 529 } 530 } 531 532 void IncludeWriter::enumSizeItems(const Definition& child) { 533 enum class State { 534 kNoItem, 535 kItemName, 536 kItemValue, 537 kItemComment, 538 }; 539 State state = State::kNoItem; 540 int longestName = 0; 541 int longestValue = 0; 542 int valueLen = 0; 543 const char* lastEnd = nullptr; 544 // SkASSERT(child.fChildren.size() == 1 || child.fChildren.size() == 2); 545 auto brace = child.fChildren[0]; 546 if (KeyWord::kClass == brace->fKeyWord) { 547 brace = brace->fChildren[0]; 548 } 549 SkASSERT(Bracket::kBrace == brace->fBracket); 550 vector<IterState> iterStack; 551 iterStack.emplace_back(brace->fTokens.begin(), brace->fTokens.end()); 552 IterState* iterState = &iterStack[0]; 553 bool preprocessorWord = false; 554 while (iterState->fDefIter != iterState->fDefEnd) { 555 auto& token = *iterState->fDefIter++; 556 if (Definition::Type::kBracket == token.fType) { 557 if (Bracket::kSlashSlash == token.fBracket) { 558 continue; // ignore old inline comments 559 } 560 if (Bracket::kSlashStar == token.fBracket) { 561 continue; // ignore old inline comments 562 } 563 if (Bracket::kPound == token.fBracket) { // preprocessor wraps member 564 if (KeyWord::kIf == token.fKeyWord || KeyWord::kIfdef == token.fKeyWord) { 565 iterStack.emplace_back(token.fTokens.begin(), token.fTokens.end()); 566 iterState = &iterStack.back(); 567 preprocessorWord = true; 568 } else if (KeyWord::kEndif == token.fKeyWord) { 569 iterStack.pop_back(); 570 iterState = &iterStack.back(); 571 } else { 572 SkASSERT(0); // incomplete 573 } 574 continue; 575 } 576 SkASSERT(0); // incomplete 577 } 578 if (Definition::Type::kWord != token.fType) { 579 SkASSERT(0); // incomplete 580 } 581 if (preprocessorWord) { 582 preprocessorWord = false; 583 continue; 584 } 585 if (State::kItemName == state) { 586 TextParser enumLine(token.fFileName, lastEnd, 587 token.fContentStart, token.fLineCount); 588 const char* end = enumLine.anyOf(",}="); 589 SkASSERT(end); 590 state = '=' == *end ? State::kItemValue : State::kItemComment; 591 if (State::kItemValue == state) { 592 valueLen = (int) (token.fContentEnd - token.fContentStart); 593 lastEnd = token.fContentEnd; 594 continue; 595 } 596 } 597 if (State::kItemValue == state) { 598 TextParser valueEnd(token.fFileName, lastEnd, 599 token.fContentStart, token.fLineCount); 600 const char* end = valueEnd.anyOf(",}"); 601 if (!end) { // write expression continuation 602 valueLen += (int) (token.fContentEnd - lastEnd); 603 continue; 604 } 605 } 606 if (State::kNoItem != state) { 607 longestValue = SkTMax(longestValue, valueLen); 608 state = State::kNoItem; 609 } 610 SkASSERT(State::kNoItem == state); 611 lastEnd = token.fContentEnd; 612 longestName = SkTMax(longestName, (int) (lastEnd - token.fContentStart)); 613 state = State::kItemName; 614 } 615 if (State::kItemValue == state) { 616 longestValue = SkTMax(longestValue, valueLen); 617 } 618 fEnumItemValueTab = longestName + fIndent + 1 /* space before = */ ; 619 if (longestValue) { 620 longestValue += 3; /* = space , */ 621 } 622 fEnumItemCommentTab = fEnumItemValueTab + longestValue + 1 /* space before //!< */ ; 623 // iterate through bmh children and see which comments fit on include lines 624 for (auto& enumItem : fEnumDef->fChildren) { 625 if (MarkType::kConst != enumItem->fMarkType) { 626 continue; 627 } 628 TextParser enumLine(enumItem); 629 enumLine.trimEnd(); 630 enumLine.skipToLineStart(); // skip const value 631 const char* commentStart = enumLine.fChar; 632 enumLine.skipLine(); 633 ptrdiff_t lineLen = enumLine.fChar - commentStart + 5 /* //!< space */ ; 634 if (!enumLine.eof()) { 635 enumLine.skipWhiteSpace(); 636 } 637 enumItem->fShort = enumLine.eof() && fEnumItemCommentTab + lineLen < 100; 638 } 639 } 640 641 // walk children and output complete method doxygen description 642 void IncludeWriter::methodOut(const Definition* method, const Definition& child) { 643 if (fPendingMethod) { 644 fIndent -= 4; 645 fPendingMethod = false; 646 } 647 fBmhMethod = method; 648 fMethodDef = &child; 649 fContinuation = nullptr; 650 fDeferComment = nullptr; 651 if (0 == fIndent || fIndentNext) { 652 fIndent += 4; 653 fIndentNext = false; 654 } 655 this->writeCommentHeader(); 656 fIndent += 4; 657 this->descriptionOut(method, SkipFirstLine::kNo); 658 // compute indention column 659 size_t column = 0; 660 bool hasParmReturn = false; 661 for (auto methodPart : method->fChildren) { 662 if (MarkType::kParam == methodPart->fMarkType) { 663 column = SkTMax(column, methodPart->fName.length()); 664 hasParmReturn = true; 665 } else if (MarkType::kReturn == methodPart->fMarkType) { 666 hasParmReturn = true; 667 } 668 } 669 if (hasParmReturn) { 670 this->lf(2); 671 column += fIndent + sizeof("@return "); 672 int saveIndent = fIndent; 673 for (auto methodPart : method->fChildren) { 674 const char* partStart = methodPart->fContentStart; 675 const char* partEnd = methodPart->fContentEnd; 676 if (MarkType::kParam == methodPart->fMarkType) { 677 this->writeString("@param"); 678 this->writeSpace(); 679 this->writeString(methodPart->fName.c_str()); 680 } else if (MarkType::kReturn == methodPart->fMarkType) { 681 this->writeString("@return"); 682 } else { 683 continue; 684 } 685 while ('\n' == partEnd[-1]) { 686 --partEnd; 687 } 688 while ('#' == partEnd[-1]) { // FIXME: so wrong; should not be before fContentEnd 689 --partEnd; 690 } 691 this->indentToColumn(column); 692 int partLen = (int) (partEnd - partStart); 693 // FIXME : detect this earlier; assert if #Return is empty 694 SkASSERT(partLen > 0 && partLen < 300); // may assert if param desc is especially long 695 fIndent = column; 696 this->rewriteBlock(partLen, partStart, Phrase::kYes); 697 fIndent = saveIndent; 698 this->lfcr(); 699 } 700 } else { 701 this->lfcr(); 702 } 703 fIndent -= 4; 704 this->lfcr(); 705 this->writeCommentTrailer(); 706 fBmhMethod = nullptr; 707 fMethodDef = nullptr; 708 fEnumDef = nullptr; 709 fWroteMethod = true; 710 } 711 712 void IncludeWriter::structOut(const Definition* root, const Definition& child, 713 const char* commentStart, const char* commentEnd) { 714 this->writeCommentHeader(); 715 this->writeString("\\"); 716 SkASSERT(MarkType::kClass == child.fMarkType || MarkType::kStruct == child.fMarkType); 717 this->writeString(MarkType::kClass == child.fMarkType ? "class" : "struct"); 718 this->writeSpace(); 719 this->writeString(child.fName.c_str()); 720 fIndent += 4; 721 this->lfcr(); 722 if (child.fDeprecated) { 723 this->writeString(child.fToBeDeprecated ? "to be deprecated soon" : "deprecated"); 724 } else { 725 this->rewriteBlock((int)(commentEnd - commentStart), commentStart, Phrase::kNo); 726 } 727 fIndent -= 4; 728 this->lfcr(); 729 this->writeCommentTrailer(); 730 } 731 732 Definition* IncludeWriter::findMemberCommentBlock(const vector<Definition*>& bmhChildren, 733 const string& name) const { 734 for (auto memberDef : bmhChildren) { 735 if (MarkType::kMember != memberDef->fMarkType) { 736 continue; 737 } 738 string match = memberDef->fName; 739 // if match.endsWith(name) ... 740 if (match.length() >= name.length() && 741 0 == match.compare(match.length() - name.length(), name.length(), name)) { 742 return memberDef; 743 } 744 } 745 for (auto memberDef : bmhChildren) { 746 if (MarkType::kSubtopic != memberDef->fMarkType && MarkType::kTopic != memberDef->fMarkType) { 747 continue; 748 } 749 Definition* result = this->findMemberCommentBlock(memberDef->fChildren, name); 750 if (result) { 751 return result; 752 } 753 } 754 return nullptr; 755 } 756 757 Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const Definition& child) { 758 const char* blockStart = !fWroteMethod && fDeferComment ? fLastComment->fContentEnd : fStart; 759 const char* blockEnd = fWroteMethod && fDeferComment ? fDeferComment->fStart - 1 : 760 memberStart->fStart; 761 this->writeBlockTrim((int) (blockEnd - blockStart), blockStart); 762 if (fIndentNext) { 763 fIndent += 4; 764 fIndentNext = false; 765 } 766 fWroteMethod = false; 767 string name(child.fContentStart, (int) (child.fContentEnd - child.fContentStart)); 768 Definition* commentBlock = this->findMemberCommentBlock(fBmhStructDef->fChildren, name); 769 if (!commentBlock) { 770 return memberStart->reportError<Definition*>("member missing comment block"); 771 } 772 if (!commentBlock->fShort) { 773 const char* commentStart = commentBlock->fContentStart; 774 ptrdiff_t commentLen = commentBlock->fContentEnd - commentStart; 775 this->writeCommentHeader(); 776 bool wroteLineFeed = false; 777 fIndent += 4; 778 for (auto child : commentBlock->fChildren) { 779 commentLen = child->fStart - commentStart; 780 wroteLineFeed |= Wrote::kLF == this->rewriteBlock(commentLen, commentStart, Phrase::kNo); 781 if (MarkType::kFormula == child->fMarkType) { 782 this->writeSpace(); 783 this->writeBlock((int) (child->fContentEnd - child->fContentStart), 784 child->fContentStart); 785 } 786 commentStart = child->fTerminator; 787 } 788 commentLen = commentBlock->fContentEnd - commentStart; 789 wroteLineFeed |= Wrote::kLF == this->rewriteBlock(commentLen, commentStart, Phrase::kNo); 790 fIndent -= 4; 791 if (wroteLineFeed || fColumn > 100 - 3 /* space * / */ ) { 792 this->lfcr(); 793 } else { 794 this->writeSpace(); 795 } 796 this->writeCommentTrailer(); 797 } 798 this->lfcr(); 799 this->writeBlock((int) (child.fStart - memberStart->fContentStart), 800 memberStart->fContentStart); 801 this->indentToColumn(fStructMemberTab); 802 this->writeString(name.c_str()); 803 auto tokenIter = child.fParent->fTokens.begin(); 804 std::advance(tokenIter, child.fParentIndex + 1); 805 Definition* valueStart = &*tokenIter; 806 while (Definition::Type::kPunctuation != tokenIter->fType) { 807 std::advance(tokenIter, 1); 808 SkASSERT(child.fParent->fTokens.end() != tokenIter); 809 } 810 Definition* valueEnd = &*tokenIter; 811 if (valueStart != valueEnd) { 812 this->indentToColumn(fStructValueTab); 813 this->writeString("="); 814 this->writeSpace(); 815 this->writeBlock((int) (valueEnd->fStart - valueStart->fContentStart), 816 valueStart->fContentStart); 817 } 818 this->writeString(";"); 819 if (commentBlock->fShort) { 820 this->indentToColumn(fStructCommentTab); 821 this->writeString("//!<"); 822 this->writeSpace(); 823 string extract = commentBlock->extractText(Definition::TrimExtract::kYes); 824 this->rewriteBlock(extract.length(), &extract.front(), Phrase::kNo); 825 } 826 this->lf(2); 827 return valueEnd; 828 } 829 830 // iterate through bmh children and see which comments fit on include lines 831 void IncludeWriter::structSetMembersShort(const vector<Definition*>& bmhChildren) { 832 for (auto memberDef : bmhChildren) { 833 if (MarkType::kMember != memberDef->fMarkType) { 834 continue; 835 } 836 string extract = memberDef->extractText(Definition::TrimExtract::kYes); 837 bool multiline = string::npos != extract.find('\n'); 838 if (multiline) { 839 memberDef->fShort = false; 840 } else { 841 ptrdiff_t lineLen = extract.length() + 5 /* //!< space */ ; 842 memberDef->fShort = fStructCommentTab + lineLen < 100; 843 } 844 } 845 for (auto memberDef : bmhChildren) { 846 if (MarkType::kSubtopic != memberDef->fMarkType && MarkType::kTopic != memberDef->fMarkType) { 847 continue; 848 } 849 this->structSetMembersShort(memberDef->fChildren); 850 } 851 } 852 853 void IncludeWriter::structSizeMembers(const Definition& child) { 854 int longestType = 0; 855 Definition* typeStart = nullptr; 856 int longestName = 0; 857 int longestValue = 0; 858 SkASSERT(child.fChildren.size() == 1 || child.fChildren.size() == 2); 859 bool inEnum = false; 860 bool inMethod = false; 861 bool inMember = false; 862 auto brace = child.fChildren[0]; 863 SkASSERT(Bracket::kBrace == brace->fBracket); 864 for (auto& token : brace->fTokens) { 865 if (Definition::Type::kBracket == token.fType) { 866 if (Bracket::kSlashSlash == token.fBracket) { 867 continue; // ignore old inline comments 868 } 869 if (Bracket::kSlashStar == token.fBracket) { 870 continue; // ignore old inline comments 871 } 872 if (Bracket::kParen == token.fBracket) { 873 if (inMethod) { 874 continue; 875 } 876 break; 877 } 878 SkASSERT(0); // incomplete 879 } 880 if (Definition::Type::kKeyWord == token.fType) { 881 switch (token.fKeyWord) { 882 case KeyWord::kEnum: 883 inEnum = true; 884 break; 885 case KeyWord::kConst: 886 case KeyWord::kConstExpr: 887 case KeyWord::kStatic: 888 case KeyWord::kInt: 889 case KeyWord::kUint8_t: 890 case KeyWord::kUint16_t: 891 case KeyWord::kUint32_t: 892 case KeyWord::kUint64_t: 893 case KeyWord::kSize_t: 894 case KeyWord::kFloat: 895 case KeyWord::kBool: 896 case KeyWord::kVoid: 897 if (!typeStart) { 898 typeStart = &token; 899 } 900 break; 901 default: 902 break; 903 } 904 continue; 905 } 906 if (Definition::Type::kPunctuation == token.fType) { 907 if (inEnum) { 908 SkASSERT(Punctuation::kSemicolon == token.fPunctuation); 909 inEnum = false; 910 } 911 if (inMethod) { 912 if (Punctuation::kColon == token.fPunctuation) { 913 inMethod = false; 914 } else if (Punctuation::kLeftBrace == token.fPunctuation) { 915 inMethod = false; 916 } else if (Punctuation::kSemicolon == token.fPunctuation) { 917 inMethod = false; 918 } else { 919 SkASSERT(0); // incomplete 920 } 921 } 922 if (inMember) { 923 SkASSERT(Punctuation::kSemicolon == token.fPunctuation); 924 typeStart = nullptr; 925 inMember = false; 926 } 927 continue; 928 } 929 if (Definition::Type::kWord != token.fType) { 930 SkASSERT(0); // incomplete 931 } 932 if (MarkType::kMember == token.fMarkType) { 933 TextParser typeStr(token.fFileName, typeStart->fContentStart, token.fContentStart, 934 token.fLineCount); 935 typeStr.trimEnd(); 936 longestType = SkTMax(longestType, (int) (typeStr.fEnd - typeStr.fStart)); 937 longestName = SkTMax(longestName, (int) (token.fContentEnd - token.fContentStart)); 938 typeStart->fMemberStart = true; 939 inMember = true; 940 continue; 941 } 942 if (MarkType::kMethod == token.fMarkType) { 943 inMethod = true; 944 continue; 945 } 946 SkASSERT(MarkType::kNone == token.fMarkType); 947 if (typeStart) { 948 if (inMember) { 949 longestValue = 950 SkTMax(longestValue, (int) (token.fContentEnd - token.fContentStart)); 951 } 952 } else { 953 typeStart = &token; 954 } 955 } 956 fStructMemberTab = longestType + fIndent + 1 /* space before name */ ; 957 fStructValueTab = fStructMemberTab + longestName + 2 /* space ; */ ; 958 fStructCommentTab = fStructValueTab; 959 if (longestValue) { 960 fStructCommentTab += longestValue + 3 /* space = space */ ; 961 fStructValueTab -= 1 /* ; */ ; 962 } 963 // iterate through bmh children and see which comments fit on include lines 964 this->structSetMembersShort(fBmhStructDef->fChildren); 965 } 966 967 static bool find_start(const Definition* startDef, const char* start) { 968 for (const auto& child : startDef->fTokens) { 969 if (child.fContentStart == start) { 970 return MarkType::kMethod == child.fMarkType; 971 } 972 if (child.fContentStart >= start) { 973 break; 974 } 975 if (find_start(&child, start)) { 976 return true; 977 } 978 } 979 return false; 980 } 981 982 bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefinition* root) { 983 if (!def->fTokens.size()) { 984 return true; 985 } 986 ParentPair pair = { def, prevPair }; 987 // write bulk of original include up to class, method, enum, etc., excepting preceding comment 988 // find associated bmh object 989 // write any associated comments in Doxygen form 990 // skip include comment 991 // if there is a series of same named methods, write one set of comments, then write all methods 992 string methodName; 993 const Definition* method = nullptr; 994 const Definition* clonedMethod = nullptr; 995 const Definition* memberStart = nullptr; 996 const Definition* memberEnd = nullptr; 997 fContinuation = nullptr; 998 bool inStruct = false; 999 bool inConstructor = false; 1000 bool inInline = false; 1001 bool eatOperator = false; 1002 bool sawConst = false; 1003 bool staticOnly = false; 1004 const Definition* requireDense = nullptr; 1005 const Definition* startDef = nullptr; 1006 for (auto& child : def->fTokens) { 1007 if (KeyWord::kOperator == child.fKeyWord && method && 1008 Definition::MethodType::kOperator == method->fMethodType) { 1009 eatOperator = true; 1010 continue; 1011 } 1012 if (eatOperator) { 1013 if (Bracket::kSquare == child.fBracket || Bracket::kParen == child.fBracket) { 1014 continue; 1015 } 1016 eatOperator = false; 1017 fContinuation = nullptr; 1018 if (KeyWord::kConst == child.fKeyWord) { 1019 continue; 1020 } 1021 } 1022 if (memberEnd) { 1023 if (memberEnd != &child) { 1024 continue; 1025 } 1026 startDef = &child; 1027 fStart = child.fContentStart + 1; 1028 memberEnd = nullptr; 1029 } 1030 if (child.fPrivate) { 1031 if (MarkType::kMethod == child.fMarkType) { 1032 inInline = true; 1033 } 1034 continue; 1035 } 1036 if (inInline) { 1037 if (Definition::Type::kKeyWord == child.fType) { 1038 SkASSERT(MarkType::kMethod != child.fMarkType); 1039 continue; 1040 } 1041 if (Definition::Type::kPunctuation == child.fType) { 1042 if (Punctuation::kLeftBrace == child.fPunctuation) { 1043 inInline = false; 1044 } else { 1045 SkASSERT(Punctuation::kAsterisk == child.fPunctuation); 1046 } 1047 continue; 1048 } 1049 if (Definition::Type::kWord == child.fType) { 1050 string name(child.fContentStart, child.fContentEnd - child.fContentStart); 1051 SkASSERT(string::npos != name.find("::")); 1052 continue; 1053 } 1054 if (Definition::Type::kBracket == child.fType) { 1055 SkASSERT(Bracket::kParen == child.fBracket); 1056 continue; 1057 } 1058 } 1059 if (fContinuation) { 1060 if (Definition::Type::kKeyWord == child.fType) { 1061 if (KeyWord::kFriend == child.fKeyWord || 1062 KeyWord::kSK_API == child.fKeyWord) { 1063 continue; 1064 } 1065 const IncludeKey& includeKey = kKeyWords[(int) child.fKeyWord]; 1066 if (KeyProperty::kNumber == includeKey.fProperty) { 1067 continue; 1068 } 1069 } 1070 if (Definition::Type::kBracket == child.fType) { 1071 if (Bracket::kAngle == child.fBracket) { 1072 continue; 1073 } 1074 if (Bracket::kParen == child.fBracket) { 1075 if (!clonedMethod) { 1076 if (inConstructor) { 1077 fContinuation = child.fContentStart; 1078 } 1079 continue; 1080 } 1081 int alternate = 1; 1082 ptrdiff_t childLen = child.fContentEnd - child.fContentStart; 1083 SkASSERT(')' == child.fContentStart[childLen]); 1084 ++childLen; 1085 do { 1086 TextParser params(clonedMethod->fFileName, clonedMethod->fStart, 1087 clonedMethod->fContentStart, clonedMethod->fLineCount); 1088 params.skipToEndBracket('('); 1089 if (params.startsWith(child.fContentStart, childLen)) { 1090 this->methodOut(clonedMethod, child); 1091 break; 1092 } 1093 ++alternate; 1094 string alternateMethod = methodName + '_' + to_string(alternate); 1095 clonedMethod = root->find(alternateMethod, 1096 RootDefinition::AllowParens::kNo); 1097 } while (clonedMethod); 1098 if (!clonedMethod) { 1099 return this->reportError<bool>("cloned method not found"); 1100 } 1101 clonedMethod = nullptr; 1102 continue; 1103 } 1104 } 1105 if (Definition::Type::kWord == child.fType) { 1106 if (clonedMethod) { 1107 continue; 1108 } 1109 size_t len = (size_t) (child.fContentEnd - child.fContentStart); 1110 const char operatorStr[] = "operator"; 1111 size_t operatorLen = sizeof(operatorStr) - 1; 1112 if (len >= operatorLen && !strncmp(child.fContentStart, operatorStr, operatorLen)) { 1113 fContinuation = child.fContentEnd; 1114 continue; 1115 } 1116 } 1117 if (Definition::Type::kPunctuation == child.fType && 1118 (Punctuation::kSemicolon == child.fPunctuation || 1119 Punctuation::kLeftBrace == child.fPunctuation || 1120 (Punctuation::kColon == child.fPunctuation && inConstructor))) { 1121 SkASSERT(fContinuation[0] == '('); 1122 const char* continueEnd = child.fContentStart; 1123 while (continueEnd > fContinuation && isspace(continueEnd[-1])) { 1124 --continueEnd; 1125 } 1126 methodName += string(fContinuation, continueEnd - fContinuation); 1127 method = root->find(methodName, RootDefinition::AllowParens::kNo); 1128 if (!method) { 1129 if (fBmhStructDef && fBmhStructDef->fDeprecated) { 1130 fContinuation = nullptr; 1131 continue; 1132 } 1133 fLineCount = child.fLineCount; 1134 return this->reportError<bool>("method not found"); 1135 } 1136 this->methodOut(method, child); 1137 continue; 1138 } 1139 if (Definition::Type::kPunctuation == child.fType && 1140 Punctuation::kAsterisk == child.fPunctuation && 1141 clonedMethod) { 1142 continue; 1143 } 1144 if (inConstructor) { 1145 continue; 1146 } 1147 method = root->find(methodName + "()", RootDefinition::AllowParens::kNo); 1148 if (method && MarkType::kDefinedBy == method->fMarkType) { 1149 method = method->fParent; 1150 } 1151 if (method) { 1152 if (method->fCloned) { 1153 clonedMethod = method; 1154 continue; 1155 } 1156 this->methodOut(method, child); 1157 continue; 1158 } else if (fBmhStructDef && fBmhStructDef->fDeprecated) { 1159 fContinuation = nullptr; 1160 continue; 1161 } 1162 fLineCount = child.fLineCount; 1163 return this->reportError<bool>("method not found"); 1164 } 1165 if (Bracket::kSlashSlash == child.fBracket || Bracket::kSlashStar == child.fBracket) { 1166 if (!fDeferComment) { 1167 fDeferComment = &child; 1168 } 1169 fLastComment = &child; 1170 continue; 1171 } 1172 if (MarkType::kMethod == child.fMarkType) { 1173 if (this->internalName(child)) { 1174 continue; 1175 } 1176 const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 : 1177 fAttrDeprecated ? fAttrDeprecated->fContentStart - 1 : 1178 child.fContentStart; 1179 if (Definition::Type::kBracket == def->fType && Bracket::kDebugCode == def->fBracket) { 1180 auto tokenIter = def->fParent->fTokens.begin(); 1181 std::advance(tokenIter, def->fParentIndex - 1); 1182 Definition* prior = &*tokenIter; 1183 if (Definition::Type::kBracket == def->fType && 1184 Bracket::kSlashStar == prior->fBracket) { 1185 bodyEnd = prior->fContentStart - 1; 1186 } 1187 } 1188 // FIXME: roll end-trimming into writeBlockTrim call 1189 while (fStart < bodyEnd && ' ' >= bodyEnd[-1]) { 1190 --bodyEnd; 1191 } 1192 int blockSize = (int) (bodyEnd - fStart); 1193 if (blockSize) { 1194 string debugstr(fStart, blockSize); 1195 this->writeBlock(blockSize, fStart); 1196 } 1197 startDef = &child; 1198 fStart = child.fContentStart; 1199 methodName = root->fName + "::" + child.fName; 1200 inConstructor = root->fName == child.fName; 1201 fContinuation = child.fContentEnd; 1202 method = root->find(methodName, RootDefinition::AllowParens::kNo); 1203 // if (!method) { 1204 // method = root->find(methodName + "()", RootDefinition::AllowParens::kNo); 1205 // } 1206 if (!method) { 1207 continue; 1208 } 1209 if (method->fCloned) { 1210 clonedMethod = method; 1211 continue; 1212 } 1213 this->methodOut(method, child); 1214 if (fAttrDeprecated) { 1215 startDef = fAttrDeprecated; 1216 fStart = fAttrDeprecated->fContentStart; 1217 fAttrDeprecated = nullptr; 1218 } 1219 continue; 1220 } 1221 if (Definition::Type::kKeyWord == child.fType) { 1222 if (fIndentNext) { 1223 // too soon 1224 #if 0 // makes struct Lattice indent when it oughtn't 1225 if (KeyWord::kEnum == child.fKeyWord) { 1226 fIndent += 4; 1227 } 1228 if (KeyWord::kPublic != child.fKeyWord) { 1229 fIndentNext = false; 1230 } 1231 #endif 1232 } 1233 switch (child.fKeyWord) { 1234 case KeyWord::kStruct: 1235 case KeyWord::kClass: 1236 fStructMemberTab = 0; 1237 // if struct contains members, compute their name and comment tabs 1238 if (child.fChildren.size() > 0) { 1239 const ParentPair* testPair = &pair; 1240 while ((testPair = testPair->fPrev)) { 1241 if (KeyWord::kClass == testPair->fParent->fKeyWord) { 1242 inStruct = fInStruct = true; 1243 break; 1244 } 1245 } 1246 } 1247 if (fInStruct) { 1248 // try child; root+child; root->parent+child; etc. 1249 int trial = 0; 1250 const RootDefinition* search = root; 1251 const Definition* parent = search->fParent; 1252 do { 1253 string name; 1254 if (0 == trial) { 1255 name = child.fName; 1256 } else if (1 == trial) { 1257 name = root->fName + "::" + child.fName; 1258 } else { 1259 SkASSERT(parent); 1260 name = parent->fName + "::" + child.fName; 1261 search = parent->asRoot(); 1262 parent = search->fParent; 1263 } 1264 fBmhStructDef = search->find(name, RootDefinition::AllowParens::kNo); 1265 } while (!fBmhStructDef && ++trial); 1266 root = const_cast<RootDefinition*>(fBmhStructDef->asRoot()); 1267 SkASSERT(root); 1268 fIndent += 4; 1269 this->structSizeMembers(child); 1270 fIndent -= 4; 1271 SkASSERT(!fIndentNext); 1272 fIndentNext = true; 1273 } 1274 if (child.fChildren.size() > 0) { 1275 const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 : 1276 child.fContentStart; 1277 this->writeBlockTrim((int) (bodyEnd - fStart), fStart); 1278 if (fPendingMethod) { 1279 fIndent -= 4; 1280 fPendingMethod = false; 1281 } 1282 startDef = requireDense ? requireDense : &child; 1283 fStart = requireDense ? requireDense->fContentStart : child.fContentStart; 1284 requireDense = nullptr; 1285 if (!fInStruct && child.fName != root->fName) { 1286 root = &fBmhParser->fClassMap[child.fName]; 1287 fRootTopic = root->fParent; 1288 SkASSERT(!root->fVisited); 1289 root->clearVisited(); 1290 fIndent = 0; 1291 fBmhStructDef = root; 1292 } 1293 if (child.fName == root->fName) { 1294 if (Definition* parent = root->fParent) { 1295 if (MarkType::kTopic == parent->fMarkType || 1296 MarkType::kSubtopic == parent->fMarkType) { 1297 const char* commentStart = root->fContentStart; 1298 const char* commentEnd = root->fChildren[0]->fStart; 1299 this->structOut(root, *root, commentStart, commentEnd); 1300 } else { 1301 SkASSERT(0); // incomplete 1302 } 1303 } else { 1304 SkASSERT(0); // incomplete 1305 } 1306 } else { 1307 SkASSERT(fInStruct); 1308 #if 0 1309 fBmhStructDef = root->find(child.fName, RootDefinition::AllowParens::kNo); 1310 if (nullptr == fBmhStructDef) { 1311 fBmhStructDef = root->find(root->fName + "::" + child.fName, 1312 RootDefinition::AllowParens::kNo); 1313 } 1314 if (!fBmhStructDef) { 1315 this->lf(2); 1316 fIndent = 0; 1317 this->writeBlock((int) (fStart - bodyEnd), bodyEnd); 1318 this->lfcr(); 1319 continue; 1320 } 1321 #endif 1322 Definition* codeBlock = nullptr; 1323 Definition* nextBlock = nullptr; 1324 for (auto test : fBmhStructDef->fChildren) { 1325 if (MarkType::kCode == test->fMarkType) { 1326 SkASSERT(!codeBlock); // FIXME: check enum for correct order earlier 1327 codeBlock = test; 1328 continue; 1329 } 1330 if (codeBlock) { 1331 nextBlock = test; 1332 break; 1333 } 1334 } 1335 // FIXME: trigger error earlier if inner #Struct or #Class is missing #Code 1336 if (!fBmhStructDef->fDeprecated) { 1337 SkASSERT(codeBlock); 1338 SkASSERT(nextBlock); // FIXME: check enum for correct order earlier 1339 const char* commentStart = codeBlock->fTerminator; 1340 const char* commentEnd = nextBlock->fStart; 1341 fIndentNext = true; 1342 this->structOut(root, *fBmhStructDef, commentStart, commentEnd); 1343 } 1344 } 1345 fDeferComment = nullptr; 1346 } else { 1347 ; // empty forward reference, nothing to do here 1348 } 1349 break; 1350 case KeyWord::kEnum: { 1351 fInEnum = true; 1352 this->enumHeaderOut(root, child); 1353 this->enumSizeItems(child); 1354 } break; 1355 case KeyWord::kConst: 1356 case KeyWord::kConstExpr: 1357 sawConst = !memberStart || staticOnly; 1358 if (!memberStart) { 1359 memberStart = &child; 1360 staticOnly = true; 1361 } 1362 break; 1363 case KeyWord::kStatic: 1364 if (!memberStart) { 1365 memberStart = &child; 1366 staticOnly = true; 1367 } 1368 break; 1369 case KeyWord::kInt: 1370 case KeyWord::kUint8_t: 1371 case KeyWord::kUint16_t: 1372 case KeyWord::kUint32_t: 1373 case KeyWord::kUint64_t: 1374 case KeyWord::kUnsigned: 1375 case KeyWord::kSize_t: 1376 case KeyWord::kFloat: 1377 case KeyWord::kBool: 1378 case KeyWord::kChar: 1379 case KeyWord::kVoid: 1380 staticOnly = false; 1381 if (!memberStart) { 1382 memberStart = &child; 1383 } 1384 break; 1385 case KeyWord::kPublic: 1386 case KeyWord::kPrivate: 1387 case KeyWord::kProtected: 1388 case KeyWord::kFriend: 1389 case KeyWord::kInline: 1390 case KeyWord::kSK_API: 1391 case KeyWord::kTypedef: 1392 break; 1393 case KeyWord::kSK_BEGIN_REQUIRE_DENSE: 1394 requireDense = &child; 1395 break; 1396 default: 1397 SkASSERT(0); 1398 } 1399 if (KeyWord::kUint8_t == child.fKeyWord) { 1400 continue; 1401 } else { 1402 if (fInEnum && KeyWord::kClass == child.fChildren[0]->fKeyWord) { 1403 if (!this->populate(child.fChildren[0], &pair, root)) { 1404 return false; 1405 } 1406 } else { 1407 if (!this->populate(&child, &pair, root)) { 1408 return false; 1409 } 1410 if (KeyWord::kClass == child.fKeyWord || KeyWord::kStruct == child.fKeyWord) { 1411 if (fInStruct) { 1412 fInStruct = false; 1413 do { 1414 SkASSERT(root); 1415 root = const_cast<RootDefinition*>(root->fParent->asRoot()); 1416 } while (MarkType::kTopic == root->fMarkType || 1417 MarkType::kSubtopic == root->fMarkType); 1418 SkASSERT(MarkType::kStruct == root->fMarkType || 1419 MarkType::kClass == root->fMarkType); 1420 fPendingMethod = false; 1421 if (startDef) { 1422 fPendingMethod = find_start(startDef, fStart); 1423 } 1424 fOutdentNext = !fPendingMethod; 1425 } 1426 } 1427 } 1428 } 1429 continue; 1430 } 1431 if (Definition::Type::kBracket == child.fType) { 1432 if (KeyWord::kEnum == child.fParent->fKeyWord || 1433 (KeyWord::kClass == child.fParent->fKeyWord && child.fParent->fParent && 1434 KeyWord::kEnum == child.fParent->fParent->fKeyWord)) { 1435 SkASSERT(Bracket::kBrace == child.fBracket); 1436 this->enumMembersOut(root, child); 1437 this->writeString("};"); 1438 this->lf(2); 1439 startDef = child.fParent; 1440 fStart = child.fParent->fContentEnd; 1441 SkASSERT(';' == fStart[0]); 1442 ++fStart; 1443 fDeferComment = nullptr; 1444 fInEnum = false; 1445 if (fIndentNext) { 1446 // fIndent -= 4; 1447 fIndentNext = false; 1448 } 1449 continue; 1450 } 1451 if (fAttrDeprecated) { 1452 continue; 1453 } 1454 fDeferComment = nullptr; 1455 if (KeyWord::kClass == def->fKeyWord || KeyWord::kStruct == def->fKeyWord) { 1456 fIndentNext = true; 1457 } 1458 if (!this->populate(&child, &pair, root)) { 1459 return false; 1460 } 1461 continue; 1462 } 1463 if (Definition::Type::kWord == child.fType) { 1464 if (MarkType::kMember == child.fMarkType) { 1465 if (!memberStart) { 1466 auto iter = def->fTokens.begin(); 1467 std::advance(iter, child.fParentIndex - 1); 1468 memberStart = &*iter; 1469 staticOnly = false; 1470 if (!fStructMemberTab) { 1471 SkASSERT(KeyWord::kStruct == def->fParent->fKeyWord); 1472 fIndent += 4; 1473 this->structSizeMembers(*def->fParent); 1474 fIndent -= 4; 1475 // SkASSERT(!fIndentNext); 1476 fIndentNext = true; 1477 } 1478 } 1479 SkASSERT(fBmhStructDef); 1480 if (!fBmhStructDef->fDeprecated) { 1481 memberEnd = this->structMemberOut(memberStart, child); 1482 startDef = &child; 1483 fStart = child.fContentEnd + 1; 1484 fDeferComment = nullptr; 1485 } 1486 } else if (MarkType::kNone == child.fMarkType && sawConst 1487 && fEnumDef && !fEnumDef->fDeprecated) { 1488 const Definition* bmhConst = nullptr; 1489 string match; 1490 if (root) { 1491 match = root->fName + "::"; 1492 } 1493 match += string(child.fContentStart, child.fContentEnd - child.fContentStart); 1494 for (auto enumChild : fEnumDef->fChildren) { 1495 if (MarkType::kConst == enumChild->fMarkType && enumChild->fName == match) { 1496 bmhConst = enumChild; 1497 break; 1498 } 1499 } 1500 if (bmhConst) { 1501 this->constOut(memberStart, child, bmhConst); 1502 fDeferComment = nullptr; 1503 sawConst = false; 1504 } 1505 } 1506 if (child.fMemberStart) { 1507 memberStart = &child; 1508 staticOnly = false; 1509 } 1510 const char attrDeprecated[] = "SK_ATTR_DEPRECATED"; 1511 const size_t attrDeprecatedLen = sizeof(attrDeprecated) - 1; 1512 if (attrDeprecatedLen == child.fContentEnd - child.fContentStart && 1513 !strncmp(attrDeprecated, child.fStart, attrDeprecatedLen)) { 1514 fAttrDeprecated = &child; 1515 } 1516 continue; 1517 } 1518 if (Definition::Type::kPunctuation == child.fType) { 1519 if (Punctuation::kSemicolon == child.fPunctuation) { 1520 memberStart = nullptr; 1521 sawConst = false; 1522 staticOnly = false; 1523 if (inStruct) { 1524 fInStruct = false; 1525 } 1526 continue; 1527 } 1528 if (Punctuation::kLeftBrace == child.fPunctuation || 1529 Punctuation::kColon == child.fPunctuation || 1530 Punctuation::kAsterisk == child.fPunctuation 1531 ) { 1532 continue; 1533 } 1534 } 1535 } 1536 return true; 1537 } 1538 1539 bool IncludeWriter::populate(BmhParser& bmhParser) { 1540 bool allPassed = true; 1541 for (auto& includeMapper : fIncludeMap) { 1542 size_t lastSlash = includeMapper.first.rfind('/'); 1543 if (string::npos == lastSlash) { 1544 lastSlash = includeMapper.first.rfind('\\'); 1545 } 1546 if (string::npos == lastSlash || lastSlash >= includeMapper.first.length() - 1) { 1547 return this->reportError<bool>("malformed include name"); 1548 } 1549 string fileName = includeMapper.first.substr(lastSlash + 1); 1550 if (".h" != fileName.substr(fileName.length() - 2)) { 1551 return this->reportError<bool>("expected fileName.h"); 1552 } 1553 string skClassName = fileName.substr(0, fileName.length() - 2); 1554 fOut = fopen(fileName.c_str(), "wb"); 1555 if (!fOut) { 1556 SkDebugf("could not open output file %s\n", fileName.c_str()); 1557 return false; 1558 } 1559 if (bmhParser.fClassMap.end() == bmhParser.fClassMap.find(skClassName)) { 1560 return this->reportError<bool>("could not find bmh class"); 1561 } 1562 fBmhParser = &bmhParser; 1563 RootDefinition* root = &bmhParser.fClassMap[skClassName]; 1564 fRootTopic = root->fParent; 1565 root->clearVisited(); 1566 fStart = includeMapper.second.fContentStart; 1567 fEnd = includeMapper.second.fContentEnd; 1568 fAnonymousEnumCount = 1; 1569 allPassed &= this->populate(&includeMapper.second, nullptr, root); 1570 this->writeBlock((int) (fEnd - fStart), fStart); 1571 fIndent = 0; 1572 this->lfcr(); 1573 this->writePending(); 1574 fclose(fOut); 1575 fflush(fOut); 1576 size_t slash = fFileName.find_last_of('/'); 1577 if (string::npos == slash) { 1578 slash = 0; 1579 } 1580 size_t back = fFileName.find_last_of('\\'); 1581 if (string::npos == back) { 1582 back = 0; 1583 } 1584 string dir = fFileName.substr(0, SkTMax(slash, back) + 1); 1585 string readname = dir + fileName; 1586 if (this->writtenFileDiffers(fileName, readname)) { 1587 SkDebugf("wrote updated %s\n", fileName.c_str()); 1588 } else { 1589 remove(fileName.c_str()); 1590 } 1591 } 1592 return allPassed; 1593 } 1594 1595 // change Xxx_Xxx to xxx xxx 1596 static string ConvertRef(const string str, bool first) { 1597 string substitute; 1598 for (char c : str) { 1599 if ('_' == c) { 1600 c = ' '; // change Xxx_Xxx to xxx xxx 1601 } else if (isupper(c) && !first) { 1602 c = tolower(c); 1603 } 1604 substitute += c; 1605 first = false; 1606 } 1607 return substitute; 1608 } 1609 1610 string IncludeWriter::resolveMethod(const char* start, const char* end, bool first) { 1611 string methodname(start, end - start); 1612 if (string::npos != methodname.find("()")) { 1613 return ""; 1614 } 1615 string substitute; 1616 auto rootDefIter = fBmhParser->fMethodMap.find(methodname); 1617 if (fBmhParser->fMethodMap.end() != rootDefIter) { 1618 substitute = methodname + "()"; 1619 } else { 1620 RootDefinition* parent = nullptr; 1621 for (auto candidate : fRootTopic->fChildren) { 1622 if (MarkType::kClass == candidate->fMarkType 1623 || MarkType::kStruct == candidate->fMarkType) { 1624 parent = candidate->asRoot(); 1625 break; 1626 } 1627 } 1628 SkASSERT(parent); 1629 auto defRef = parent->find(parent->fName + "::" + methodname, 1630 RootDefinition::AllowParens::kNo); 1631 if (defRef && MarkType::kMethod == defRef->fMarkType) { 1632 substitute = methodname + "()"; 1633 } 1634 } 1635 if (fMethodDef && methodname == fMethodDef->fName) { 1636 TextParser report(fBmhMethod); 1637 report.reportError("method should not include references to itself"); 1638 return ""; 1639 } 1640 if (fBmhMethod) { 1641 for (auto child : fBmhMethod->fChildren) { 1642 if (MarkType::kParam != child->fMarkType) { 1643 continue; 1644 } 1645 if (methodname == child->fName) { 1646 return ""; 1647 } 1648 } 1649 } 1650 return substitute; 1651 } 1652 1653 string IncludeWriter::resolveRef(const char* start, const char* end, bool first, 1654 RefType* refType) { 1655 // look up Xxx_Xxx 1656 string undername(start, end - start); 1657 for (const auto& external : fBmhParser->fExternals) { 1658 if (external.fName == undername) { 1659 *refType = RefType::kExternal; 1660 return external.fName; 1661 } 1662 } 1663 *refType = RefType::kNormal; 1664 SkASSERT(string::npos == undername.find(' ')); 1665 const Definition* rootDef = nullptr; 1666 string substitute; 1667 { 1668 auto rootDefIter = fBmhParser->fTopicMap.find(undername); 1669 if (fBmhParser->fTopicMap.end() != rootDefIter) { 1670 rootDef = rootDefIter->second; 1671 } else { 1672 string prefixedName = fRootTopic->fName + '_' + undername; 1673 rootDefIter = fBmhParser->fTopicMap.find(prefixedName); 1674 if (fBmhParser->fTopicMap.end() != rootDefIter) { 1675 rootDef = rootDefIter->second; 1676 } else if (fBmhStructDef) { 1677 string localPrefix = fBmhStructDef->fFiddle + '_' + undername; 1678 rootDefIter = fBmhParser->fTopicMap.find(localPrefix); 1679 if (fBmhParser->fTopicMap.end() != rootDefIter) { 1680 rootDef = rootDefIter->second; 1681 } 1682 if (!rootDef) { 1683 size_t doubleColon = fBmhStructDef->fName.rfind("::"); 1684 if (string::npos != doubleColon && undername 1685 == fBmhStructDef->fName.substr(doubleColon + 2)) { 1686 substitute = fBmhStructDef->fName; 1687 } 1688 } 1689 } 1690 if (!rootDef && !substitute.length()) { 1691 auto aliasIter = fBmhParser->fAliasMap.find(undername); 1692 if (fBmhParser->fAliasMap.end() != aliasIter) { 1693 rootDef = aliasIter->second; 1694 } else if (!first) { 1695 SkDebugf("unfound: %s\n", undername.c_str()); 1696 this->reportError("reference unfound"); 1697 return ""; 1698 } 1699 } 1700 } 1701 } 1702 if (rootDef) { 1703 MarkType rootType = rootDef->fMarkType; 1704 bool isTopic = MarkType::kSubtopic == rootType || MarkType::kTopic == rootType; 1705 auto substituteParent = MarkType::kAlias == rootType ? rootDef->fParent : 1706 isTopic ? rootDef : nullptr; 1707 if (substituteParent) { 1708 for (auto child : substituteParent->fChildren) { 1709 if (MarkType::kSubstitute == child->fMarkType) { 1710 substitute = string(child->fContentStart, 1711 (int) (child->fContentEnd - child->fContentStart)); 1712 break; 1713 } 1714 } 1715 } 1716 if (!substitute.length()) { 1717 string match = rootDef->fName; 1718 size_t index; 1719 while (string::npos != (index = match.find('_'))) { 1720 match.erase(index, 1); 1721 } 1722 string skmatch = "Sk" + match; 1723 auto parent = substituteParent ? substituteParent : rootDef; 1724 for (auto child : parent->fChildren) { 1725 // there may be more than one 1726 // prefer the one mostly closely matching in text 1727 if ((MarkType::kClass == child->fMarkType || 1728 MarkType::kStruct == child->fMarkType || 1729 (MarkType::kEnum == child->fMarkType && !child->fAnonymous) || 1730 MarkType::kEnumClass == child->fMarkType) && (match == child->fName || 1731 skmatch == child->fName)) { 1732 substitute = child->fName; 1733 break; 1734 } 1735 } 1736 } 1737 if (!substitute.length()) { 1738 for (auto child : rootDef->fChildren) { 1739 // there may be more than one 1740 // if so, it's a bug since it's unknown which is the right one 1741 if (MarkType::kClass == child->fMarkType || 1742 MarkType::kStruct == child->fMarkType || 1743 (MarkType::kEnum == child->fMarkType && !child->fAnonymous) || 1744 MarkType::kEnumClass == child->fMarkType) { 1745 SkASSERT("" == substitute); 1746 substitute = child->fName; 1747 if (MarkType::kEnum == child->fMarkType) { 1748 size_t parentClassEnd = substitute.find("::"); 1749 SkASSERT(string::npos != parentClassEnd); 1750 string subEnd = substitute.substr(parentClassEnd + 2); 1751 if (fInEnum) { 1752 substitute = subEnd; 1753 } 1754 if (subEnd == undername) { 1755 break; 1756 } 1757 } 1758 } 1759 } 1760 } 1761 if (!substitute.length()) { 1762 const Definition* parent = rootDef; 1763 do { 1764 parent = parent->fParent; 1765 } while (parent && (MarkType::kSubtopic == parent->fMarkType 1766 || MarkType::kTopic == parent->fMarkType)); 1767 if (parent) { 1768 if (MarkType::kClass == parent->fMarkType || 1769 MarkType::kStruct == parent->fMarkType || 1770 (MarkType::kEnum == parent->fMarkType && !parent->fAnonymous) || 1771 MarkType::kEnumClass == parent->fMarkType) { 1772 if (parent->fParent != fRootTopic) { 1773 substitute = parent->fName; 1774 substitute += ' '; 1775 substitute += ConvertRef(rootDef->fName, false); 1776 } else { 1777 substitute += ConvertRef(undername, first); 1778 } 1779 } 1780 } 1781 } 1782 } 1783 // Ensure first word after period is capitalized if substitute is lower cased. 1784 if (first && isupper(start[0]) && substitute.length() > 0 && islower(substitute[0])) { 1785 substitute[0] = start[0]; 1786 } 1787 return substitute; 1788 } 1789 1790 int IncludeWriter::lookupMethod(const PunctuationState punctuation, const Word word, 1791 const int lastSpace, const int run, int lastWrite, const char* data, 1792 bool hasIndirection) { 1793 int wordStart = lastSpace; 1794 while (' ' >= data[wordStart]) { 1795 ++wordStart; 1796 } 1797 const int wordEnd = PunctuationState::kDelimiter == punctuation || 1798 PunctuationState::kParen == punctuation || 1799 PunctuationState::kPeriod == punctuation ? run - 1 : run; 1800 string temp; 1801 if (hasIndirection && '(' != data[wordEnd - 1] && ')' != data[wordEnd - 1]) { 1802 // FIXME: hard-coded to assume a.b or a->b is a.b() or a->b(). 1803 // need to check class a for member b to see if this is so 1804 TextParser parser(fFileName, &data[wordStart], &data[wordEnd], fLineCount); 1805 const char* indirection = parser.anyOf(".>"); 1806 if (&data[wordEnd] <= &indirection[2] || 'f' != indirection[1] || 1807 !isupper(indirection[2])) { 1808 temp = string(&data[wordStart], wordEnd - wordStart) + "()"; 1809 } 1810 } else { 1811 temp = this->resolveMethod(&data[wordStart], &data[wordEnd], Word::kFirst == word); 1812 } 1813 if (temp.length()) { 1814 if (wordStart > lastWrite) { 1815 SkASSERT(data[wordStart - 1] >= ' '); 1816 if (' ' == data[lastWrite]) { 1817 this->writeSpace(); 1818 } 1819 this->writeBlockTrim(wordStart - lastWrite, &data[lastWrite]); 1820 if (' ' == data[wordStart - 1]) { 1821 this->writeSpace(); 1822 } 1823 } 1824 SkASSERT(temp[temp.length() - 1] > ' '); 1825 this->writeString(temp.c_str()); 1826 lastWrite = wordEnd; 1827 } 1828 return lastWrite; 1829 } 1830 1831 int IncludeWriter::lookupReference(const PunctuationState punctuation, const Word word, 1832 const int start, const int run, int lastWrite, const char last, const char* data) { 1833 const int end = PunctuationState::kDelimiter == punctuation || 1834 PunctuationState::kParen == punctuation || 1835 PunctuationState::kPeriod == punctuation ? run - 1 : run; 1836 RefType refType = RefType::kUndefined; 1837 string resolved = string(&data[start], (size_t) (end - start)); 1838 string temp = this->resolveRef(&data[start], &data[end], Word::kFirst == word, &refType); 1839 if (!temp.length()) { 1840 if (Word::kFirst != word && '_' != last) { 1841 temp = ConvertRef(resolved, false); 1842 } 1843 } 1844 if (temp.length()) { 1845 if (start > lastWrite) { 1846 SkASSERT(data[start - 1] >= ' '); 1847 if (' ' == data[lastWrite]) { 1848 this->writeSpace(); 1849 } 1850 this->writeBlockTrim(start - lastWrite, &data[lastWrite]); 1851 if (' ' == data[start - 1]) { 1852 this->writeSpace(); 1853 } 1854 } 1855 SkASSERT(temp[temp.length() - 1] > ' '); 1856 this->writeString(temp.c_str()); 1857 lastWrite = end; 1858 } 1859 return lastWrite; 1860 } 1861 1862 /* returns true if rewriteBlock wrote linefeeds */ 1863 IncludeWriter::Wrote IncludeWriter::rewriteBlock(int size, const char* data, Phrase phrase) { 1864 bool wroteLineFeeds = false; 1865 while (size > 0 && data[0] <= ' ') { 1866 --size; 1867 ++data; 1868 } 1869 while (size > 0 && data[size - 1] <= ' ') { 1870 --size; 1871 } 1872 if (0 == size) { 1873 return Wrote::kNone; 1874 } 1875 int run = 0; 1876 Word word = Word::kStart; 1877 PunctuationState punctuation = Phrase::kNo == phrase ? 1878 PunctuationState::kStart : PunctuationState::kSpace; 1879 int start = 0; 1880 int lastWrite = 0; 1881 int lineFeeds = 0; 1882 int lastPrintable = 0; 1883 int lastSpace = -1; 1884 char c = 0; 1885 char last = 0; 1886 bool embeddedIndirection = false; 1887 bool embeddedSymbol = false; 1888 bool hasLower = false; 1889 bool hasUpper = false; 1890 bool hasIndirection = false; 1891 bool hasSymbol = false; 1892 while (run < size) { 1893 last = c; 1894 c = data[run]; 1895 SkASSERT(' ' <= c || '\n' == c); 1896 if (lineFeeds && ' ' < c) { 1897 if (lastPrintable >= lastWrite) { 1898 if (' ' == data[lastWrite]) { 1899 this->writeSpace(); 1900 lastWrite++; 1901 } 1902 this->writeBlock(lastPrintable - lastWrite + 1, &data[lastWrite]); 1903 } 1904 if (lineFeeds > 1) { 1905 this->lf(2); 1906 } 1907 this->lfcr(); // defer the indent until non-whitespace is seen 1908 lastWrite = run; 1909 lineFeeds = 0; 1910 } 1911 if (' ' < c) { 1912 lastPrintable = run; 1913 } 1914 switch (c) { 1915 case '\n': 1916 ++lineFeeds; 1917 wroteLineFeeds = true; 1918 case ' ': 1919 switch (word) { 1920 case Word::kStart: 1921 break; 1922 case Word::kUnderline: 1923 case Word::kCap: 1924 case Word::kFirst: 1925 if (!hasLower) { 1926 break; 1927 } 1928 lastWrite = this->lookupReference(punctuation, word, start, run, 1929 lastWrite, last, data); 1930 break; 1931 case Word::kMixed: 1932 if (hasUpper && hasLower && !hasSymbol && lastSpace > 0) { 1933 lastWrite = this->lookupMethod(punctuation, word, lastSpace, run, 1934 lastWrite, data, hasIndirection); 1935 } 1936 break; 1937 default: 1938 SkASSERT(0); 1939 } 1940 punctuation = PunctuationState::kPeriod == punctuation || 1941 (PunctuationState::kStart == punctuation && ' ' >= last) ? 1942 PunctuationState::kStart : PunctuationState::kSpace; 1943 word = Word::kStart; 1944 embeddedIndirection = false; 1945 embeddedSymbol = false; 1946 hasLower = false; 1947 hasUpper = false; 1948 hasIndirection = false; 1949 hasSymbol = false; 1950 lastSpace = run; 1951 break; 1952 case '.': case ',': case ';': case ':': case ')': 1953 switch (word) { 1954 case Word::kStart: 1955 punctuation = PunctuationState::kDelimiter; 1956 case Word::kCap: 1957 case Word::kFirst: 1958 case Word::kUnderline: 1959 case Word::kMixed: 1960 if (PunctuationState::kDelimiter == punctuation || 1961 PunctuationState::kPeriod == punctuation) { 1962 word = Word::kMixed; 1963 } 1964 punctuation = '.' == c ? PunctuationState::kPeriod : 1965 PunctuationState::kDelimiter; 1966 break; 1967 default: 1968 SkASSERT(0); 1969 } 1970 ('.' == c ? embeddedIndirection : embeddedSymbol) = true; 1971 break; 1972 case '>': 1973 if ('-' == last) { 1974 embeddedIndirection = true; 1975 break; 1976 } 1977 case '\'': // possessive apostrophe isn't treated as delimiting punctation 1978 case '\"': // quote is passed straight through 1979 case '=': 1980 case '!': // assumed not to be punctuation, but a programming symbol 1981 case '&': case '<': case '{': case '}': case '/': case '*': case '[': case ']': 1982 word = Word::kMixed; 1983 embeddedSymbol = true; 1984 break; 1985 case '(': 1986 if (' ' == last) { 1987 punctuation = PunctuationState::kParen; 1988 } else { 1989 word = Word::kMixed; 1990 } 1991 embeddedSymbol = true; 1992 break; 1993 case '_': 1994 switch (word) { 1995 case Word::kStart: 1996 word = Word::kMixed; 1997 break; 1998 case Word::kCap: 1999 case Word::kFirst: 2000 case Word::kUnderline: 2001 word = Word::kUnderline; 2002 break; 2003 case Word::kMixed: 2004 break; 2005 default: 2006 SkASSERT(0); 2007 } 2008 hasSymbol |= embeddedSymbol; 2009 break; 2010 case '+': 2011 // hackery to allow C++ 2012 SkASSERT('C' == last || '+' == last); // FIXME: don't allow + outside of #Formula 2013 break; 2014 case 'A': case 'B': case 'C': case 'D': case 'E': 2015 case 'F': case 'G': case 'H': case 'I': case 'J': 2016 case 'K': case 'L': case 'M': case 'N': case 'O': 2017 case 'P': case 'Q': case 'R': case 'S': case 'T': 2018 case 'U': case 'V': case 'W': case 'X': case 'Y': 2019 case 'Z': 2020 switch (word) { 2021 case Word::kStart: 2022 word = PunctuationState::kStart == punctuation ? Word::kFirst : Word::kCap; 2023 start = run; 2024 break; 2025 case Word::kCap: 2026 case Word::kFirst: 2027 if (!isupper(last) && '~' != last) { 2028 word = Word::kMixed; 2029 } 2030 break; 2031 case Word::kUnderline: 2032 // some word in Xxx_XXX_Xxx can be all upper, but all can't: XXX_XXX 2033 if ('_' != last && !isupper(last)) { 2034 word = Word::kMixed; 2035 } 2036 break; 2037 case Word::kMixed: 2038 break; 2039 default: 2040 SkASSERT(0); 2041 } 2042 hasUpper = true; 2043 if (PunctuationState::kPeriod == punctuation || 2044 PunctuationState::kDelimiter == punctuation) { 2045 word = Word::kMixed; 2046 } 2047 hasIndirection |= embeddedIndirection; 2048 hasSymbol |= embeddedSymbol; 2049 break; 2050 case 'a': case 'b': case 'c': case 'd': case 'e': 2051 case 'f': case 'g': case 'h': case 'i': case 'j': 2052 case 'k': case 'l': case 'm': case 'n': case 'o': 2053 case 'p': case 'q': case 'r': case 's': case 't': 2054 case 'u': case 'v': case 'w': case 'x': case 'y': 2055 case 'z': 2056 case '0': case '1': case '2': case '3': case '4': 2057 case '5': case '6': case '7': case '8': case '9': 2058 case '-': 2059 switch (word) { 2060 case Word::kStart: 2061 word = Word::kMixed; 2062 break; 2063 case Word::kMixed: 2064 case Word::kCap: 2065 case Word::kFirst: 2066 case Word::kUnderline: 2067 break; 2068 default: 2069 SkASSERT(0); 2070 } 2071 hasLower = true; 2072 punctuation = PunctuationState::kStart; 2073 hasIndirection |= embeddedIndirection; 2074 hasSymbol |= embeddedSymbol; 2075 break; 2076 case '~': 2077 SkASSERT(Word::kStart == word); 2078 word = PunctuationState::kStart == punctuation ? Word::kFirst : Word::kCap; 2079 start = run; 2080 hasUpper = true; 2081 hasIndirection |= embeddedIndirection; 2082 hasSymbol |= embeddedSymbol; 2083 break; 2084 default: 2085 SkASSERT(0); 2086 } 2087 ++run; 2088 } 2089 if ((word == Word::kCap || word == Word::kFirst || word == Word::kUnderline) && hasLower) { 2090 lastWrite = this->lookupReference(punctuation, word, start, run, lastWrite, last, data); 2091 } else if (word == Word::kMixed && hasUpper && hasLower && !hasSymbol && lastSpace > 0) { 2092 lastWrite = this->lookupMethod(punctuation, word, lastSpace, run, lastWrite, data, 2093 hasIndirection && !hasSymbol); 2094 } 2095 if (run > lastWrite) { 2096 if (' ' == data[lastWrite]) { 2097 this->writeSpace(); 2098 } 2099 this->writeBlock(run - lastWrite, &data[lastWrite]); 2100 } 2101 return wroteLineFeeds ? Wrote::kLF : Wrote::kChars; 2102 } 2103