blob: 2a8e58b4734fc9c13c544f9bbf581a7dad4e1938 [file] [log] [blame]
Cary Clark8032b982017-07-28 11:04:54 -04001/*
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
Cary Clark6fc50412017-09-21 12:31:06 -040010void IncludeWriter::descriptionOut(const Definition* def) {
11 const char* commentStart = def->fContentStart;
12 int commentLen = (int) (def->fContentEnd - commentStart);
13 bool breakOut = false;
Cary Clark154beea2017-10-26 07:58:48 -040014 SkDEBUGCODE(bool wroteCode = false);
Cary Clark6fc50412017-09-21 12:31:06 -040015 for (auto prop : def->fChildren) {
16 switch (prop->fMarkType) {
Cary Clark154beea2017-10-26 07:58:48 -040017 case MarkType::kCode: {
18 bool literal = false;
19 bool literalOutdent = false;
20 commentLen = (int) (prop->fStart - commentStart);
21 if (commentLen > 0) {
22 SkASSERT(commentLen < 1000);
23 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) {
24 this->lf(2);
25 }
26 }
27 size_t childSize = prop->fChildren.size();
28 if (childSize) {
29 SkASSERT(1 == childSize || 2 == childSize); // incomplete
30 SkASSERT(MarkType::kLiteral == prop->fChildren[0]->fMarkType);
31 SkASSERT(1 == childSize || MarkType::kOutdent == prop->fChildren[1]->fMarkType);
32 commentStart = prop->fChildren[childSize - 1]->fContentStart;
33 literal = true;
34 literalOutdent = 2 == childSize &&
35 MarkType::kOutdent == prop->fChildren[1]->fMarkType;
36 }
37 commentLen = (int) (prop->fContentEnd - commentStart);
38 SkASSERT(commentLen > 0);
39 if (literal) {
40 if (!literalOutdent) {
41 fIndent += 4;
42 }
43 this->writeBlockIndent(commentLen, commentStart);
44 this->lf(2);
45 if (!literalOutdent) {
46 fIndent -= 4;
47 }
48 commentStart = prop->fTerminator;
49 SkDEBUGCODE(wroteCode = true);
50 }
51 } break;
Cary Clark6fc50412017-09-21 12:31:06 -040052 case MarkType::kDefinedBy:
53 commentStart = prop->fTerminator;
54 break;
Ben Wagner63fd7602017-10-09 15:45:33 -040055 case MarkType::kDeprecated:
Cary Clark6fc50412017-09-21 12:31:06 -040056 case MarkType::kPrivate:
57 commentLen = (int) (prop->fStart - commentStart);
58 if (commentLen > 0) {
59 SkASSERT(commentLen < 1000);
60 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) {
61 this->lfcr();
62 }
63 }
64 commentStart = prop->fContentStart;
65 commentLen = (int) (prop->fContentEnd - commentStart);
66 if (commentLen > 0) {
Cary Clark3cd22cc2017-12-01 11:49:58 -050067 this->writeBlockIndent(commentLen, commentStart);
Cary Clark61dfc3a2018-01-03 08:37:53 -050068 if ('\n' != commentStart[commentLen - 1] && '\n' == commentStart[commentLen]) {
69 this->lfcr();
70 }
Cary Clark6fc50412017-09-21 12:31:06 -040071 }
72 commentStart = prop->fTerminator;
73 commentLen = (int) (def->fContentEnd - commentStart);
74 break;
75 case MarkType::kExperimental:
76 this->writeString("EXPERIMENTAL:");
77 this->writeSpace();
78 commentStart = prop->fContentStart;
79 commentLen = (int) (prop->fContentEnd - commentStart);
80 if (commentLen > 0) {
81 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) {
82 this->lfcr();
83 }
84 }
85 commentStart = prop->fTerminator;
86 commentLen = (int) (def->fContentEnd - commentStart);
87 break;
Cary Clark154beea2017-10-26 07:58:48 -040088 case MarkType::kFormula: {
Cary Clark6fc50412017-09-21 12:31:06 -040089 commentLen = prop->fStart - commentStart;
90 if (commentLen > 0) {
91 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) {
Cary Clark154beea2017-10-26 07:58:48 -040092 if (commentLen > 1 && '\n' == prop->fStart[-1] &&
93 '\n' == prop->fStart[-2]) {
94 this->lf(1);
95 } else {
96 this->writeSpace();
97 }
Cary Clark6fc50412017-09-21 12:31:06 -040098 }
99 }
Cary Clark154beea2017-10-26 07:58:48 -0400100 int saveIndent = fIndent;
101 if (fIndent < fColumn + 1) {
102 fIndent = fColumn + 1;
103 }
104 this->writeBlockIndent(prop->length(), prop->fContentStart);
105 fIndent = saveIndent;
Cary Clark6fc50412017-09-21 12:31:06 -0400106 commentStart = prop->fTerminator;
107 commentLen = (int) (def->fContentEnd - commentStart);
Cary Clark154beea2017-10-26 07:58:48 -0400108 if (commentLen > 1 && '\n' == commentStart[0] && '\n' == commentStart[1]) {
Cary Clark6fc50412017-09-21 12:31:06 -0400109 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400110 } else {
111 SkASSERT('\n' == prop->fTerminator[0]);
112 if ('.' != prop->fTerminator[1] && !fLinefeeds) {
113 this->writeSpace();
114 }
Cary Clark6fc50412017-09-21 12:31:06 -0400115 }
Cary Clark154beea2017-10-26 07:58:48 -0400116 } break;
Cary Clark6fc50412017-09-21 12:31:06 -0400117 case MarkType::kToDo:
118 commentLen = (int) (prop->fStart - commentStart);
119 if (commentLen > 0) {
120 SkASSERT(commentLen < 1000);
121 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) {
122 this->lfcr();
123 }
124 }
125 commentStart = prop->fTerminator;
126 commentLen = (int) (def->fContentEnd - commentStart);
127 break;
128 case MarkType::kList:
129 commentLen = prop->fStart - commentStart;
130 if (commentLen > 0) {
131 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart,
132 Phrase::kNo)) {
133 this->lfcr();
134 }
135 }
136 for (auto row : prop->fChildren) {
137 SkASSERT(MarkType::kRow == row->fMarkType);
138 for (auto column : row->fChildren) {
139 SkASSERT(MarkType::kColumn == column->fMarkType);
140 this->writeString("-");
141 this->writeSpace();
142 this->descriptionOut(column);
143 this->lf(1);
144 }
145 }
146 commentStart = prop->fTerminator;
147 commentLen = (int) (def->fContentEnd - commentStart);
148 if ('\n' == commentStart[0] && '\n' == commentStart[1]) {
149 this->lf(2);
150 }
151 break;
152 default:
153 commentLen = (int) (prop->fStart - commentStart);
154 breakOut = true;
155 }
156 if (breakOut) {
157 break;
158 }
159 }
Cary Clark154beea2017-10-26 07:58:48 -0400160 SkASSERT(wroteCode || (commentLen > 0 && commentLen < 1500));
161 if (commentLen > 0) {
162 this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
163 }
Cary Clark6fc50412017-09-21 12:31:06 -0400164}
165
Cary Clark8032b982017-07-28 11:04:54 -0400166void IncludeWriter::enumHeaderOut(const RootDefinition* root,
167 const Definition& child) {
168 const Definition* enumDef = nullptr;
169 const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 :
170 child.fContentStart;
171 this->writeBlockTrim((int) (bodyEnd - fStart), fStart); // may write nothing
172 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400173 if (fIndentNext) {
174 fIndent += 4;
175 fIndentNext = false;
176 }
Cary Clark8032b982017-07-28 11:04:54 -0400177 fDeferComment = nullptr;
178 fStart = child.fContentStart;
179 const auto& nameDef = child.fTokens.front();
180 string fullName;
181 if (nullptr != nameDef.fContentEnd) {
Cary Clarkbad5ad72017-08-03 17:14:08 -0400182 TextParser enumClassCheck(&nameDef);
183 const char* start = enumClassCheck.fStart;
184 size_t len = (size_t) (enumClassCheck.fEnd - start);
185 bool enumClass = enumClassCheck.skipExact("class ");
186 if (enumClass) {
187 start = enumClassCheck.fChar;
188 const char* end = enumClassCheck.anyOf(" \n;{");
189 len = (size_t) (end - start);
190 }
191 string enumName(start, len);
192 if (enumClass) {
193 child.fChildren[0]->fName = enumName;
194 }
Cary Clark8032b982017-07-28 11:04:54 -0400195 fullName = root->fName + "::" + enumName;
Cary Clarkce101242017-09-01 15:51:02 -0400196 enumDef = root->find(enumName, RootDefinition::AllowParens::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400197 if (!enumDef) {
Cary Clarkce101242017-09-01 15:51:02 -0400198 enumDef = root->find(fullName, RootDefinition::AllowParens::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400199 }
200 SkASSERT(enumDef);
201 // child[0] should be #Code comment starts at child[0].fTerminator
202 // though skip until #Code is found (in case there's a #ToDo, etc)
203 // child[1] should be #Const comment ends at child[1].fStart
204 // comment becomes enum header (if any)
205 } else {
206 string enumName(root->fName);
207 enumName += "::_anonymous";
208 if (fAnonymousEnumCount > 1) {
209 enumName += '_' + to_string(fAnonymousEnumCount);
210 }
Cary Clarkce101242017-09-01 15:51:02 -0400211 enumDef = root->find(enumName, RootDefinition::AllowParens::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400212 SkASSERT(enumDef);
213 ++fAnonymousEnumCount;
214 }
215 Definition* codeBlock = nullptr;
216 const char* commentStart = nullptr;
217 bool wroteHeader = false;
Cary Clark6fc50412017-09-21 12:31:06 -0400218 bool lastAnchor = false;
Cary Clark8032b982017-07-28 11:04:54 -0400219 SkDEBUGCODE(bool foundConst = false);
220 for (auto test : enumDef->fChildren) {
221 if (MarkType::kCode == test->fMarkType) {
222 SkASSERT(!codeBlock); // FIXME: check enum for correct order earlier
223 codeBlock = test;
224 commentStart = codeBlock->fTerminator;
225 continue;
226 }
227 if (!codeBlock) {
228 continue;
229 }
230 const char* commentEnd = test->fStart;
231 if (!wroteHeader &&
232 !this->contentFree((int) (commentEnd - commentStart), commentStart)) {
Cary Clark154beea2017-10-26 07:58:48 -0400233 if (fIndentNext) {
234 fIndent += 4;
235 }
Cary Clark8032b982017-07-28 11:04:54 -0400236 this->writeCommentHeader();
237 this->writeString("\\enum");
Cary Clarkbad5ad72017-08-03 17:14:08 -0400238 if (fullName.length() > 0) {
239 this->writeSpace();
240 this->writeString(fullName.c_str());
241 }
Cary Clark8032b982017-07-28 11:04:54 -0400242 fIndent += 4;
243 this->lfcr();
244 wroteHeader = true;
245 }
Cary Clark6fc50412017-09-21 12:31:06 -0400246 if (lastAnchor) {
247 if (commentEnd - commentStart > 1) {
248 SkASSERT('\n' == commentStart[0]);
249 if (' ' == commentStart[1]) {
250 this->writeSpace();
251 }
252 }
253 lastAnchor = false;
254 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400255 this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400256 if (MarkType::kAnchor == test->fMarkType) {
Cary Clark154beea2017-10-26 07:58:48 -0400257 bool newLine = commentEnd - commentStart > 1 &&
258 '\n' == commentEnd[-1] && '\n' == commentEnd[-2];
Cary Clark8032b982017-07-28 11:04:54 -0400259 commentStart = test->fContentStart;
260 commentEnd = test->fChildren[0]->fStart;
Cary Clark154beea2017-10-26 07:58:48 -0400261 if (newLine) {
262 this->lf(2);
263 } else {
264 this->writeSpace();
265 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400266 this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo);
Cary Clark6fc50412017-09-21 12:31:06 -0400267 lastAnchor = true; // this->writeSpace();
Cary Clark8032b982017-07-28 11:04:54 -0400268 }
269 commentStart = test->fTerminator;
270 if (MarkType::kConst == test->fMarkType) {
271 SkASSERT(codeBlock); // FIXME: check enum for correct order earlier
272 SkDEBUGCODE(foundConst = true);
273 break;
274 }
275 }
276 SkASSERT(codeBlock);
277 SkASSERT(foundConst);
278 if (wroteHeader) {
279 fIndent -= 4;
280 this->lfcr();
281 this->writeCommentTrailer();
282 }
Cary Clarkbad5ad72017-08-03 17:14:08 -0400283 Definition* braceHolder = child.fChildren[0];
284 if (KeyWord::kClass == braceHolder->fKeyWord) {
285 braceHolder = braceHolder->fChildren[0];
286 }
287 bodyEnd = braceHolder->fContentStart;
Cary Clark8032b982017-07-28 11:04:54 -0400288 SkASSERT('{' == bodyEnd[0]);
289 ++bodyEnd;
290 this->lfcr();
291 this->writeBlock((int) (bodyEnd - fStart), fStart); // write include "enum Name {"
292 fIndent += 4;
293 this->singleLF();
294 fStart = bodyEnd;
295 fEnumDef = enumDef;
296}
297
Cary Clarkbad5ad72017-08-03 17:14:08 -0400298void IncludeWriter::enumMembersOut(const RootDefinition* root, Definition& child) {
Cary Clark8032b982017-07-28 11:04:54 -0400299 // iterate through include tokens and find how much remains for 1 line comments
300 // put ones that fit on same line, ones that are too big on preceding line?
301 const Definition* currentEnumItem = nullptr;
302 const char* commentStart = nullptr;
303 const char* lastEnd = nullptr;
304 int commentLen = 0;
305 enum class State {
306 kNoItem,
307 kItemName,
308 kItemValue,
309 kItemComment,
310 };
311 State state = State::kNoItem;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400312 vector<IterState> iterStack;
313 iterStack.emplace_back(child.fTokens.begin(), child.fTokens.end());
314 IterState* iterState = &iterStack[0];
315 bool preprocessorWord = false;
316 const char* preprocessStart = nullptr;
317 const char* preprocessEnd = nullptr;
318 for (int onePast = 0; onePast < 2; onePast += iterState->fDefIter == iterState->fDefEnd) {
319 Definition* token = onePast ? nullptr : &*iterState->fDefIter++;
Cary Clark8032b982017-07-28 11:04:54 -0400320 if (token && Definition::Type::kBracket == token->fType) {
321 if (Bracket::kSlashSlash == token->fBracket) {
322 fStart = token->fContentEnd;
323 continue; // ignore old inline comments
324 }
325 if (Bracket::kSlashStar == token->fBracket) {
326 fStart = token->fContentEnd + 1;
327 continue; // ignore old inline comments
328 }
Cary Clarkbad5ad72017-08-03 17:14:08 -0400329 if (Bracket::kPound == token->fBracket) { // preprocessor wraps member
330 preprocessStart = token->fContentStart;
331 if (KeyWord::kIf == token->fKeyWord || KeyWord::kIfdef == token->fKeyWord) {
332 iterStack.emplace_back(token->fTokens.begin(), token->fTokens.end());
333 iterState = &iterStack.back();
334 preprocessorWord = true;
335 } else if (KeyWord::kEndif == token->fKeyWord) {
336 iterStack.pop_back();
337 iterState = &iterStack.back();
338 preprocessEnd = token->fContentEnd;
339 } else {
340 SkASSERT(0); // incomplete
341 }
342 continue;
343 }
Cary Clark8032b982017-07-28 11:04:54 -0400344 SkASSERT(0); // incomplete
345 }
346 if (token && Definition::Type::kWord != token->fType) {
347 SkASSERT(0); // incomplete
348 }
Cary Clarkbad5ad72017-08-03 17:14:08 -0400349 if (preprocessorWord) {
350 preprocessorWord = false;
351 preprocessEnd = token->fContentEnd;
352 continue;
353 }
Cary Clark8032b982017-07-28 11:04:54 -0400354 if (token && State::kItemName == state) {
355 TextParser enumLine(token->fFileName, lastEnd,
356 token->fContentStart, token->fLineCount);
357 const char* end = enumLine.anyOf(",}=");
358 SkASSERT(end);
359 state = '=' == *end ? State::kItemValue : State::kItemComment;
360 if (State::kItemValue == state) { // write enum value
361 this->indentToColumn(fEnumItemValueTab);
362 this->writeString("=");
363 this->writeSpace();
364 lastEnd = token->fContentEnd;
365 this->writeBlock((int) (lastEnd - token->fContentStart),
366 token->fContentStart); // write const value if any
367 continue;
368 }
369 }
370 if (token && State::kItemValue == state) {
371 TextParser valueEnd(token->fFileName, lastEnd,
372 token->fContentStart, token->fLineCount);
373 const char* end = valueEnd.anyOf(",}");
374 if (!end) { // write expression continuation
375 if (' ' == lastEnd[0]) {
376 this->writeSpace();
377 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400378 this->writeBlock((int) (token->fContentEnd - lastEnd), lastEnd);
Cary Clark8032b982017-07-28 11:04:54 -0400379 continue;
380 }
381 }
382 if (State::kNoItem != state) {
383 this->writeString(",");
384 SkASSERT(currentEnumItem);
385 if (currentEnumItem->fShort) {
386 this->indentToColumn(fEnumItemCommentTab);
387 this->writeString("//!<");
388 this->writeSpace();
Cary Clarkd0530ba2017-09-14 11:25:39 -0400389 this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400390 }
391 if (onePast) {
392 fIndent -= 4;
393 }
394 this->lfcr();
Cary Clarkbad5ad72017-08-03 17:14:08 -0400395 if (preprocessStart) {
396 SkASSERT(preprocessEnd);
397 int saveIndent = fIndent;
398 fIndent = SkTMax(0, fIndent - 8);
399 this->lf(2);
400 this->writeBlock((int) (preprocessEnd - preprocessStart), preprocessStart);
401 this->lfcr();
402 fIndent = saveIndent;
403 preprocessStart = nullptr;
404 preprocessEnd = nullptr;
405 }
Cary Clark8032b982017-07-28 11:04:54 -0400406 if (token && State::kItemValue == state) {
407 fStart = token->fContentStart;
408 }
409 state = State::kNoItem;
410 }
411 SkASSERT(State::kNoItem == state);
412 if (onePast) {
413 break;
414 }
415 SkASSERT(token);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400416 string itemName = root->fName + "::";
417 if (KeyWord::kClass == child.fParent->fKeyWord) {
418 itemName += child.fParent->fName + "::";
419 }
420 itemName += string(token->fContentStart, (int) (token->fContentEnd - token->fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400421 for (auto& enumItem : fEnumDef->fChildren) {
422 if (MarkType::kConst != enumItem->fMarkType) {
423 continue;
424 }
425 if (itemName != enumItem->fName) {
426 continue;
427 }
428 currentEnumItem = enumItem;
429 break;
430 }
431 SkASSERT(currentEnumItem);
432 // if description fits, it goes after item
433 commentStart = currentEnumItem->fContentStart;
434 const char* commentEnd;
435 if (currentEnumItem->fChildren.size() > 0) {
436 commentEnd = currentEnumItem->fChildren[0]->fStart;
437 } else {
438 commentEnd = currentEnumItem->fContentEnd;
439 }
440 TextParser enumComment(fFileName, commentStart, commentEnd, currentEnumItem->fLineCount);
441 if (enumComment.skipToLineStart()) { // skip const value
442 commentStart = enumComment.fChar;
443 commentLen = (int) (commentEnd - commentStart);
444 } else {
445 const Definition* privateDef = currentEnumItem->fChildren[0];
446 SkASSERT(MarkType::kPrivate == privateDef->fMarkType);
447 commentStart = privateDef->fContentStart;
448 commentLen = (int) (privateDef->fContentEnd - privateDef->fContentStart);
449 }
Cary Clark73fa9722017-08-29 17:36:51 -0400450 // FIXME: may assert here if there's no const value
451 // should have detected and errored on that earlier when enum fContentStart was set
Cary Clark8032b982017-07-28 11:04:54 -0400452 SkASSERT(commentLen > 0 && commentLen < 1000);
453 if (!currentEnumItem->fShort) {
454 this->writeCommentHeader();
455 fIndent += 4;
Cary Clarkd0530ba2017-09-14 11:25:39 -0400456 bool wroteLineFeed = Wrote::kLF ==
457 this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400458 fIndent -= 4;
459 if (wroteLineFeed || fColumn > 100 - 3 /* space * / */ ) {
460 this->lfcr();
461 } else {
462 this->writeSpace();
463 }
464 this->writeCommentTrailer();
465 }
466 lastEnd = token->fContentEnd;
467 this->lfcr();
468 if (',' == fStart[0]) {
469 ++fStart;
470 }
471 this->writeBlock((int) (lastEnd - fStart), fStart); // enum item name
472 fStart = token->fContentEnd;
473 state = State::kItemName;
474 }
475}
476
477void IncludeWriter::enumSizeItems(const Definition& child) {
478 enum class State {
479 kNoItem,
480 kItemName,
481 kItemValue,
482 kItemComment,
483 };
484 State state = State::kNoItem;
485 int longestName = 0;
486 int longestValue = 0;
487 int valueLen = 0;
488 const char* lastEnd = nullptr;
Cary Clark7cfcbca2018-01-04 16:11:51 -0500489// SkASSERT(child.fChildren.size() == 1 || child.fChildren.size() == 2);
Cary Clark8032b982017-07-28 11:04:54 -0400490 auto brace = child.fChildren[0];
Cary Clarkbad5ad72017-08-03 17:14:08 -0400491 if (KeyWord::kClass == brace->fKeyWord) {
492 brace = brace->fChildren[0];
493 }
Cary Clark8032b982017-07-28 11:04:54 -0400494 SkASSERT(Bracket::kBrace == brace->fBracket);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400495 vector<IterState> iterStack;
496 iterStack.emplace_back(brace->fTokens.begin(), brace->fTokens.end());
497 IterState* iterState = &iterStack[0];
498 bool preprocessorWord = false;
499 while (iterState->fDefIter != iterState->fDefEnd) {
500 auto& token = *iterState->fDefIter++;
Cary Clark8032b982017-07-28 11:04:54 -0400501 if (Definition::Type::kBracket == token.fType) {
502 if (Bracket::kSlashSlash == token.fBracket) {
503 continue; // ignore old inline comments
504 }
505 if (Bracket::kSlashStar == token.fBracket) {
506 continue; // ignore old inline comments
507 }
Cary Clarkbad5ad72017-08-03 17:14:08 -0400508 if (Bracket::kPound == token.fBracket) { // preprocessor wraps member
509 if (KeyWord::kIf == token.fKeyWord || KeyWord::kIfdef == token.fKeyWord) {
510 iterStack.emplace_back(token.fTokens.begin(), token.fTokens.end());
511 iterState = &iterStack.back();
512 preprocessorWord = true;
513 } else if (KeyWord::kEndif == token.fKeyWord) {
514 iterStack.pop_back();
515 iterState = &iterStack.back();
516 } else {
517 SkASSERT(0); // incomplete
518 }
519 continue;
520 }
Cary Clark8032b982017-07-28 11:04:54 -0400521 SkASSERT(0); // incomplete
522 }
523 if (Definition::Type::kWord != token.fType) {
524 SkASSERT(0); // incomplete
525 }
Cary Clarkbad5ad72017-08-03 17:14:08 -0400526 if (preprocessorWord) {
527 preprocessorWord = false;
528 continue;
529 }
Cary Clark8032b982017-07-28 11:04:54 -0400530 if (State::kItemName == state) {
531 TextParser enumLine(token.fFileName, lastEnd,
532 token.fContentStart, token.fLineCount);
533 const char* end = enumLine.anyOf(",}=");
534 SkASSERT(end);
535 state = '=' == *end ? State::kItemValue : State::kItemComment;
536 if (State::kItemValue == state) {
537 valueLen = (int) (token.fContentEnd - token.fContentStart);
538 lastEnd = token.fContentEnd;
539 continue;
540 }
541 }
542 if (State::kItemValue == state) {
543 TextParser valueEnd(token.fFileName, lastEnd,
544 token.fContentStart, token.fLineCount);
545 const char* end = valueEnd.anyOf(",}");
546 if (!end) { // write expression continuation
Ben Wagner63fd7602017-10-09 15:45:33 -0400547 valueLen += (int) (token.fContentEnd - lastEnd);
Cary Clark8032b982017-07-28 11:04:54 -0400548 continue;
549 }
550 }
551 if (State::kNoItem != state) {
552 longestValue = SkTMax(longestValue, valueLen);
553 state = State::kNoItem;
554 }
555 SkASSERT(State::kNoItem == state);
556 lastEnd = token.fContentEnd;
557 longestName = SkTMax(longestName, (int) (lastEnd - token.fContentStart));
558 state = State::kItemName;
559 }
560 if (State::kItemValue == state) {
561 longestValue = SkTMax(longestValue, valueLen);
562 }
563 fEnumItemValueTab = longestName + fIndent + 1 /* space before = */ ;
564 if (longestValue) {
565 longestValue += 3; /* = space , */
566 }
567 fEnumItemCommentTab = fEnumItemValueTab + longestValue + 1 /* space before //!< */ ;
568 // iterate through bmh children and see which comments fit on include lines
569 for (auto& enumItem : fEnumDef->fChildren) {
570 if (MarkType::kConst != enumItem->fMarkType) {
571 continue;
572 }
573 TextParser enumLine(enumItem);
574 enumLine.trimEnd();
575 enumLine.skipToLineStart(); // skip const value
576 const char* commentStart = enumLine.fChar;
577 enumLine.skipLine();
578 ptrdiff_t lineLen = enumLine.fChar - commentStart + 5 /* //!< space */ ;
579 if (!enumLine.eof()) {
580 enumLine.skipWhiteSpace();
581 }
582 enumItem->fShort = enumLine.eof() && fEnumItemCommentTab + lineLen < 100;
583 }
584}
585
586// walk children and output complete method doxygen description
Cary Clark579985c2017-07-31 11:48:27 -0400587void IncludeWriter::methodOut(const Definition* method, const Definition& child) {
Cary Clark154beea2017-10-26 07:58:48 -0400588 if (fPendingMethod) {
589 fIndent -= 4;
590 fPendingMethod = false;
591 }
Cary Clark579985c2017-07-31 11:48:27 -0400592 fBmhMethod = method;
593 fMethodDef = &child;
Cary Clark8032b982017-07-28 11:04:54 -0400594 fContinuation = nullptr;
595 fDeferComment = nullptr;
Cary Clark154beea2017-10-26 07:58:48 -0400596 if (0 == fIndent || fIndentNext) {
597 fIndent += 4;
598 fIndentNext = false;
Cary Clark8032b982017-07-28 11:04:54 -0400599 }
600 this->writeCommentHeader();
601 fIndent += 4;
Cary Clark6fc50412017-09-21 12:31:06 -0400602 this->descriptionOut(method);
Cary Clark8032b982017-07-28 11:04:54 -0400603 // compute indention column
604 size_t column = 0;
605 bool hasParmReturn = false;
606 for (auto methodPart : method->fChildren) {
607 if (MarkType::kParam == methodPart->fMarkType) {
608 column = SkTMax(column, methodPart->fName.length());
609 hasParmReturn = true;
610 } else if (MarkType::kReturn == methodPart->fMarkType) {
611 hasParmReturn = true;
612 }
613 }
614 if (hasParmReturn) {
615 this->lf(2);
616 column += fIndent + sizeof("@return ");
617 int saveIndent = fIndent;
618 for (auto methodPart : method->fChildren) {
619 const char* partStart = methodPart->fContentStart;
Ben Wagner63fd7602017-10-09 15:45:33 -0400620 const char* partEnd = methodPart->fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400621 if (MarkType::kParam == methodPart->fMarkType) {
622 this->writeString("@param");
623 this->writeSpace();
624 this->writeString(methodPart->fName.c_str());
625 } else if (MarkType::kReturn == methodPart->fMarkType) {
626 this->writeString("@return");
627 } else {
628 continue;
629 }
630 while ('\n' == partEnd[-1]) {
631 --partEnd;
632 }
633 while ('#' == partEnd[-1]) { // FIXME: so wrong; should not be before fContentEnd
634 --partEnd;
635 }
636 this->indentToColumn(column);
637 int partLen = (int) (partEnd - partStart);
Cary Clark73fa9722017-08-29 17:36:51 -0400638 // FIXME : detect this earlier; assert if #Return is empty
Cary Clarkf059e7c2017-12-20 14:53:21 -0500639 SkASSERT(partLen > 0 && partLen < 300); // may assert if param desc is especially long
Cary Clark8032b982017-07-28 11:04:54 -0400640 fIndent = column;
Cary Clarkd0530ba2017-09-14 11:25:39 -0400641 this->rewriteBlock(partLen, partStart, Phrase::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400642 fIndent = saveIndent;
643 this->lfcr();
644 }
645 } else {
646 this->lfcr();
647 }
648 fIndent -= 4;
649 this->lfcr();
650 this->writeCommentTrailer();
Cary Clark579985c2017-07-31 11:48:27 -0400651 fBmhMethod = nullptr;
652 fMethodDef = nullptr;
Cary Clarkd0530ba2017-09-14 11:25:39 -0400653 fWroteMethod = true;
Cary Clark8032b982017-07-28 11:04:54 -0400654}
655
656void IncludeWriter::structOut(const Definition* root, const Definition& child,
657 const char* commentStart, const char* commentEnd) {
658 this->writeCommentHeader();
659 this->writeString("\\");
660 SkASSERT(MarkType::kClass == child.fMarkType || MarkType::kStruct == child.fMarkType);
661 this->writeString(MarkType::kClass == child.fMarkType ? "class" : "struct");
662 this->writeSpace();
663 this->writeString(child.fName.c_str());
664 fIndent += 4;
665 this->lfcr();
Cary Clarkd0530ba2017-09-14 11:25:39 -0400666 this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400667 fIndent -= 4;
668 this->lfcr();
669 this->writeCommentTrailer();
670}
671
Cary Clark2dc84ad2018-01-26 12:56:22 -0500672Definition* IncludeWriter::findMemberCommentBlock(const vector<Definition*>& bmhChildren,
673 const string& name) const {
674 for (auto memberDef : bmhChildren) {
675 if (memberDef->fName.length() - name.length() == memberDef->fName.find(name)) {
676 return memberDef;
677 }
678 }
679 for (auto memberDef : bmhChildren) {
680 if (MarkType::kSubtopic == memberDef->fMarkType || MarkType::kTopic == memberDef->fMarkType) {
681 Definition* result = this->findMemberCommentBlock(memberDef->fChildren, name);
682 if (result) {
683 return result;
684 }
685 }
686 }
687 return nullptr;
688}
689
Cary Clarkbad5ad72017-08-03 17:14:08 -0400690Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const Definition& child) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400691 const char* blockStart = !fWroteMethod && fDeferComment ? fLastComment->fContentEnd : fStart;
692 const char* blockEnd = fWroteMethod && fDeferComment ? fDeferComment->fStart - 1 :
693 memberStart->fStart;
694 this->writeBlockTrim((int) (blockEnd - blockStart), blockStart);
Cary Clark154beea2017-10-26 07:58:48 -0400695 if (fIndentNext) {
696 fIndent += 4;
697 fIndentNext = false;
698 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400699 fWroteMethod = false;
Cary Clark8032b982017-07-28 11:04:54 -0400700 string name(child.fContentStart, (int) (child.fContentEnd - child.fContentStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -0500701 Definition* commentBlock = this->findMemberCommentBlock(fBmhStructDef->fChildren, name);
702 if (!commentBlock) {
703 return memberStart->reportError<Definition*>("member missing comment block");
704 }
705 const char* commentStart = commentBlock->fContentStart;
706 ptrdiff_t commentLen = commentBlock->fContentEnd - commentStart;
707 bool isShort = commentBlock->fShort;
708 SkASSERT(!isShort || commentBlock->fChildren.size() == 0);
709
Cary Clark8032b982017-07-28 11:04:54 -0400710 if (!isShort) {
711 this->writeCommentHeader();
Cary Clarkbad5ad72017-08-03 17:14:08 -0400712 bool wroteLineFeed = false;
Cary Clark8032b982017-07-28 11:04:54 -0400713 fIndent += 4;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400714 for (auto child : commentBlock->fChildren) {
715 commentLen = child->fStart - commentStart;
Cary Clarkd0530ba2017-09-14 11:25:39 -0400716 wroteLineFeed |= Wrote::kLF == this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400717 if (MarkType::kFormula == child->fMarkType) {
718 this->writeSpace();
719 this->writeBlock((int) (child->fContentEnd - child->fContentStart),
Ben Wagner63fd7602017-10-09 15:45:33 -0400720 child->fContentStart);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400721 }
722 commentStart = child->fTerminator;
723 }
724 commentLen = commentBlock->fContentEnd - commentStart;
Cary Clarkd0530ba2017-09-14 11:25:39 -0400725 wroteLineFeed |= Wrote::kLF == this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400726 fIndent -= 4;
727 if (wroteLineFeed || fColumn > 100 - 3 /* space * / */ ) {
728 this->lfcr();
729 } else {
730 this->writeSpace();
731 }
732 this->writeCommentTrailer();
733 }
734 this->lfcr();
Cary Clarkbad5ad72017-08-03 17:14:08 -0400735 this->writeBlock((int) (child.fStart - memberStart->fContentStart),
Cary Clark8032b982017-07-28 11:04:54 -0400736 memberStart->fContentStart);
737 this->indentToColumn(fStructMemberTab);
738 this->writeString(name.c_str());
Cary Clark6fc50412017-09-21 12:31:06 -0400739 auto tokenIter = child.fParent->fTokens.begin();
Cary Clarkbad5ad72017-08-03 17:14:08 -0400740 std::advance(tokenIter, child.fParentIndex + 1);
741 Definition* valueStart = &*tokenIter;
742 while (Definition::Type::kPunctuation != tokenIter->fType) {
743 std::advance(tokenIter, 1);
744 SkASSERT(child.fParent->fTokens.end() != tokenIter);
745 }
746 Definition* valueEnd = &*tokenIter;
747 if (valueStart != valueEnd) {
748 this->indentToColumn(fStructValueTab);
749 this->writeString("=");
750 this->writeSpace();
751 this->writeBlock((int) (valueEnd->fStart - valueStart->fContentStart),
Ben Wagner63fd7602017-10-09 15:45:33 -0400752 valueStart->fContentStart);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400753 }
Cary Clark8032b982017-07-28 11:04:54 -0400754 this->writeString(";");
755 if (isShort) {
756 this->indentToColumn(fStructCommentTab);
757 this->writeString("//!<");
758 this->writeSpace();
Cary Clarkd0530ba2017-09-14 11:25:39 -0400759 this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400760 }
Cary Clark154beea2017-10-26 07:58:48 -0400761 this->lf(2);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400762 return valueEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400763}
764
Cary Clark884dd7d2017-10-11 10:37:52 -0400765void IncludeWriter::structSizeMembers(const Definition& child) {
Cary Clark8032b982017-07-28 11:04:54 -0400766 int longestType = 0;
767 Definition* typeStart = nullptr;
768 int longestName = 0;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400769 int longestValue = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400770 SkASSERT(child.fChildren.size() == 1 || child.fChildren.size() == 2);
771 bool inEnum = false;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400772 bool inMethod = false;
773 bool inMember = false;
Cary Clark8032b982017-07-28 11:04:54 -0400774 auto brace = child.fChildren[0];
775 SkASSERT(Bracket::kBrace == brace->fBracket);
776 for (auto& token : brace->fTokens) {
777 if (Definition::Type::kBracket == token.fType) {
778 if (Bracket::kSlashSlash == token.fBracket) {
779 continue; // ignore old inline comments
780 }
781 if (Bracket::kSlashStar == token.fBracket) {
782 continue; // ignore old inline comments
783 }
784 if (Bracket::kParen == token.fBracket) {
Cary Clarkbad5ad72017-08-03 17:14:08 -0400785 if (inMethod) {
786 continue;
787 }
Cary Clark8032b982017-07-28 11:04:54 -0400788 break;
789 }
790 SkASSERT(0); // incomplete
791 }
792 if (Definition::Type::kKeyWord == token.fType) {
793 switch (token.fKeyWord) {
794 case KeyWord::kEnum:
795 inEnum = true;
796 break;
797 case KeyWord::kConst:
798 case KeyWord::kConstExpr:
799 case KeyWord::kStatic:
800 case KeyWord::kInt:
Cary Clarkd0530ba2017-09-14 11:25:39 -0400801 case KeyWord::kUint8_t:
802 case KeyWord::kUint16_t:
Cary Clark8032b982017-07-28 11:04:54 -0400803 case KeyWord::kUint32_t:
Cary Clarkd0530ba2017-09-14 11:25:39 -0400804 case KeyWord::kUint64_t:
Cary Clark8032b982017-07-28 11:04:54 -0400805 case KeyWord::kSize_t:
806 case KeyWord::kFloat:
807 case KeyWord::kBool:
808 case KeyWord::kVoid:
809 if (!typeStart) {
810 typeStart = &token;
811 }
812 break;
813 default:
814 break;
815 }
816 continue;
817 }
818 if (Definition::Type::kPunctuation == token.fType) {
819 if (inEnum) {
820 SkASSERT(Punctuation::kSemicolon == token.fPunctuation);
821 inEnum = false;
822 }
Cary Clarkbad5ad72017-08-03 17:14:08 -0400823 if (inMethod) {
824 if (Punctuation::kColon == token.fPunctuation) {
825 inMethod = false;
826 } else if (Punctuation::kLeftBrace == token.fPunctuation) {
827 inMethod = false;
Cary Clark73fa9722017-08-29 17:36:51 -0400828 } else if (Punctuation::kSemicolon == token.fPunctuation) {
829 inMethod = false;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400830 } else {
831 SkASSERT(0); // incomplete
832 }
833 }
834 if (inMember) {
835 SkASSERT(Punctuation::kSemicolon == token.fPunctuation);
836 typeStart = nullptr;
837 inMember = false;
838 }
Cary Clark8032b982017-07-28 11:04:54 -0400839 continue;
840 }
841 if (Definition::Type::kWord != token.fType) {
842 SkASSERT(0); // incomplete
843 }
844 if (MarkType::kMember == token.fMarkType) {
845 TextParser typeStr(token.fFileName, typeStart->fContentStart, token.fContentStart,
846 token.fLineCount);
847 typeStr.trimEnd();
Cary Clarkbad5ad72017-08-03 17:14:08 -0400848 longestType = SkTMax(longestType, (int) (typeStr.fEnd - typeStr.fStart));
Cary Clark8032b982017-07-28 11:04:54 -0400849 longestName = SkTMax(longestName, (int) (token.fContentEnd - token.fContentStart));
850 typeStart->fMemberStart = true;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400851 inMember = true;
852 continue;
853 }
854 if (MarkType::kMethod == token.fMarkType) {
855 inMethod = true;
Cary Clark8032b982017-07-28 11:04:54 -0400856 continue;
857 }
858 SkASSERT(MarkType::kNone == token.fMarkType);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400859 if (typeStart) {
860 if (inMember) {
861 longestValue =
862 SkTMax(longestValue, (int) (token.fContentEnd - token.fContentStart));
863 }
864 } else {
Cary Clark8032b982017-07-28 11:04:54 -0400865 typeStart = &token;
866 }
867 }
868 fStructMemberTab = longestType + fIndent + 1 /* space before name */ ;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400869 fStructValueTab = fStructMemberTab + longestName + 2 /* space ; */ ;
870 fStructCommentTab = fStructValueTab;
871 if (longestValue) {
872 fStructCommentTab += longestValue + 3 /* space = space */ ;
873 fStructValueTab -= 1 /* ; */ ;
874 }
Cary Clark8032b982017-07-28 11:04:54 -0400875 // iterate through bmh children and see which comments fit on include lines
Cary Clark154beea2017-10-26 07:58:48 -0400876 for (auto& member : fBmhStructDef->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -0400877 if (MarkType::kMember != member->fMarkType) {
878 continue;
879 }
880 TextParser memberLine(member);
881 memberLine.trimEnd();
882 const char* commentStart = memberLine.fChar;
883 memberLine.skipLine();
884 ptrdiff_t lineLen = memberLine.fChar - commentStart + 5 /* //!< space */ ;
885 if (!memberLine.eof()) {
886 memberLine.skipWhiteSpace();
887 }
888 member->fShort = memberLine.eof() && fStructCommentTab + lineLen < 100;
889 }
890}
891
Cary Clark154beea2017-10-26 07:58:48 -0400892static bool find_start(const Definition* startDef, const char* start) {
893 for (const auto& child : startDef->fTokens) {
894 if (child.fContentStart == start) {
895 return MarkType::kMethod == child.fMarkType;
896 }
897 if (child.fContentStart >= start) {
898 break;
899 }
900 if (find_start(&child, start)) {
901 return true;
902 }
903 }
904 return false;
905}
906
Cary Clark73fa9722017-08-29 17:36:51 -0400907bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefinition* root) {
908 ParentPair pair = { def, prevPair };
Cary Clark8032b982017-07-28 11:04:54 -0400909 // write bulk of original include up to class, method, enum, etc., excepting preceding comment
910 // find associated bmh object
911 // write any associated comments in Doxygen form
912 // skip include comment
913 // if there is a series of same named methods, write one set of comments, then write all methods
914 string methodName;
Kevin Lubick42846132018-01-05 10:11:11 -0500915 const Definition* method = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -0400916 const Definition* clonedMethod = nullptr;
917 const Definition* memberStart = nullptr;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400918 const Definition* memberEnd = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -0400919 fContinuation = nullptr;
920 bool inStruct = false;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400921 bool inConstructor = false;
Cary Clark884dd7d2017-10-11 10:37:52 -0400922 bool inInline = false;
Cary Clark154beea2017-10-26 07:58:48 -0400923 bool eatOperator = false;
924 const Definition* requireDense = nullptr;
925 const Definition* startDef = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -0400926 for (auto& child : def->fTokens) {
Cary Clark154beea2017-10-26 07:58:48 -0400927 if (KeyWord::kOperator == child.fKeyWord && method &&
928 Definition::MethodType::kOperator == method->fMethodType) {
929 eatOperator = true;
930 continue;
931 }
932 if (eatOperator) {
933 if (Bracket::kSquare == child.fBracket || Bracket::kParen == child.fBracket) {
934 continue;
935 }
936 eatOperator = false;
937 fContinuation = nullptr;
938 if (KeyWord::kConst == child.fKeyWord) {
939 continue;
940 }
941 }
Cary Clarkbad5ad72017-08-03 17:14:08 -0400942 if (memberEnd) {
943 if (memberEnd != &child) {
944 continue;
945 }
Cary Clark154beea2017-10-26 07:58:48 -0400946 startDef = &child;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400947 fStart = child.fContentStart + 1;
948 memberEnd = nullptr;
949 }
Cary Clark8032b982017-07-28 11:04:54 -0400950 if (child.fPrivate) {
Cary Clark884dd7d2017-10-11 10:37:52 -0400951 if (MarkType::kMethod == child.fMarkType) {
952 inInline = true;
953 }
Cary Clark8032b982017-07-28 11:04:54 -0400954 continue;
955 }
Cary Clark884dd7d2017-10-11 10:37:52 -0400956 if (inInline) {
957 if (Definition::Type::kKeyWord == child.fType) {
958 SkASSERT(MarkType::kMethod != child.fMarkType);
959 continue;
960 }
961 if (Definition::Type::kPunctuation == child.fType) {
962 if (Punctuation::kLeftBrace == child.fPunctuation) {
963 inInline = false;
964 } else {
965 SkASSERT(Punctuation::kAsterisk == child.fPunctuation);
966 }
967 continue;
968 }
969 if (Definition::Type::kWord == child.fType) {
970 string name(child.fContentStart, child.fContentEnd - child.fContentStart);
971 SkASSERT(string::npos != name.find("::"));
972 continue;
973 }
974 if (Definition::Type::kBracket == child.fType) {
975 SkASSERT(Bracket::kParen == child.fBracket);
976 continue;
977 }
978 }
Cary Clark8032b982017-07-28 11:04:54 -0400979 if (fContinuation) {
980 if (Definition::Type::kKeyWord == child.fType) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400981 if (KeyWord::kFriend == child.fKeyWord ||
Cary Clark73fa9722017-08-29 17:36:51 -0400982 KeyWord::kSK_API == child.fKeyWord) {
Cary Clark8032b982017-07-28 11:04:54 -0400983 continue;
984 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400985 const IncludeKey& includeKey = kKeyWords[(int) child.fKeyWord];
986 if (KeyProperty::kNumber == includeKey.fProperty) {
987 continue;
988 }
Cary Clark8032b982017-07-28 11:04:54 -0400989 }
Cary Clark3cd22cc2017-12-01 11:49:58 -0500990 if (Definition::Type::kBracket == child.fType) {
991 if (Bracket::kAngle == child.fBracket) {
Cary Clark8032b982017-07-28 11:04:54 -0400992 continue;
993 }
Cary Clark3cd22cc2017-12-01 11:49:58 -0500994 if (Bracket::kParen == child.fBracket) {
995 if (!clonedMethod) {
996 if (inConstructor) {
997 fContinuation = child.fContentStart;
998 }
999 continue;
Cary Clark8032b982017-07-28 11:04:54 -04001000 }
Cary Clark3cd22cc2017-12-01 11:49:58 -05001001 int alternate = 1;
1002 ptrdiff_t childLen = child.fContentEnd - child.fContentStart;
1003 SkASSERT(')' == child.fContentStart[childLen]);
1004 ++childLen;
1005 do {
1006 TextParser params(clonedMethod->fFileName, clonedMethod->fStart,
1007 clonedMethod->fContentStart, clonedMethod->fLineCount);
1008 params.skipToEndBracket('(');
1009 if (params.startsWith(child.fContentStart, childLen)) {
1010 this->methodOut(clonedMethod, child);
1011 break;
1012 }
1013 ++alternate;
1014 string alternateMethod = methodName + '_' + to_string(alternate);
1015 clonedMethod = root->find(alternateMethod,
1016 RootDefinition::AllowParens::kNo);
1017 } while (clonedMethod);
1018 if (!clonedMethod) {
1019 return this->reportError<bool>("cloned method not found");
1020 }
1021 clonedMethod = nullptr;
1022 continue;
Cary Clark8032b982017-07-28 11:04:54 -04001023 }
Cary Clark8032b982017-07-28 11:04:54 -04001024 }
1025 if (Definition::Type::kWord == child.fType) {
1026 if (clonedMethod) {
1027 continue;
1028 }
1029 size_t len = (size_t) (child.fContentEnd - child.fContentStart);
1030 const char operatorStr[] = "operator";
1031 size_t operatorLen = sizeof(operatorStr) - 1;
1032 if (len >= operatorLen && !strncmp(child.fContentStart, operatorStr, operatorLen)) {
1033 fContinuation = child.fContentEnd;
1034 continue;
1035 }
1036 }
1037 if (Definition::Type::kPunctuation == child.fType &&
1038 (Punctuation::kSemicolon == child.fPunctuation ||
Cary Clark6fc50412017-09-21 12:31:06 -04001039 Punctuation::kLeftBrace == child.fPunctuation ||
1040 (Punctuation::kColon == child.fPunctuation && inConstructor))) {
Cary Clark8032b982017-07-28 11:04:54 -04001041 SkASSERT(fContinuation[0] == '(');
1042 const char* continueEnd = child.fContentStart;
1043 while (continueEnd > fContinuation && isspace(continueEnd[-1])) {
1044 --continueEnd;
1045 }
1046 methodName += string(fContinuation, continueEnd - fContinuation);
Cary Clarkce101242017-09-01 15:51:02 -04001047 method = root->find(methodName, RootDefinition::AllowParens::kNo);
Cary Clark8032b982017-07-28 11:04:54 -04001048 if (!method) {
1049 fLineCount = child.fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001050 return this->reportError<bool>("method not found");
1051 }
Cary Clark579985c2017-07-31 11:48:27 -04001052 this->methodOut(method, child);
Cary Clark8032b982017-07-28 11:04:54 -04001053 continue;
1054 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001055 if (Definition::Type::kPunctuation == child.fType &&
1056 Punctuation::kAsterisk == child.fPunctuation &&
1057 clonedMethod) {
1058 continue;
1059 }
Cary Clarkbad5ad72017-08-03 17:14:08 -04001060 if (inConstructor) {
1061 continue;
1062 }
Cary Clark6fc50412017-09-21 12:31:06 -04001063 method = root->find(methodName + "()", RootDefinition::AllowParens::kNo);
Cary Clarkbad5ad72017-08-03 17:14:08 -04001064 if (method && MarkType::kDefinedBy == method->fMarkType) {
Cary Clark8032b982017-07-28 11:04:54 -04001065 method = method->fParent;
1066 }
1067 if (method) {
Cary Clark6fc50412017-09-21 12:31:06 -04001068 if (method->fCloned) {
1069 clonedMethod = method;
1070 continue;
1071 }
Cary Clark579985c2017-07-31 11:48:27 -04001072 this->methodOut(method, child);
Cary Clark8032b982017-07-28 11:04:54 -04001073 continue;
1074 }
1075 fLineCount = child.fLineCount;
Cary Clark8032b982017-07-28 11:04:54 -04001076 return this->reportError<bool>("method not found");
1077 }
1078 if (Bracket::kSlashSlash == child.fBracket || Bracket::kSlashStar == child.fBracket) {
1079 if (!fDeferComment) {
1080 fDeferComment = &child;
1081 }
Cary Clarkbad5ad72017-08-03 17:14:08 -04001082 fLastComment = &child;
Cary Clark8032b982017-07-28 11:04:54 -04001083 continue;
Ben Wagner63fd7602017-10-09 15:45:33 -04001084 }
Cary Clark8032b982017-07-28 11:04:54 -04001085 if (MarkType::kMethod == child.fMarkType) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001086 if (this->internalName(child)) {
1087 continue;
1088 }
Cary Clark8032b982017-07-28 11:04:54 -04001089 const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 :
Cary Clark154beea2017-10-26 07:58:48 -04001090 fAttrDeprecated ? fAttrDeprecated->fContentStart - 1 :
Cary Clark8032b982017-07-28 11:04:54 -04001091 child.fContentStart;
Cary Clark7cfcbca2018-01-04 16:11:51 -05001092 if (Definition::Type::kBracket == def->fType && Bracket::kDebugCode == def->fBracket) {
1093 auto tokenIter = def->fParent->fTokens.begin();
1094 std::advance(tokenIter, def->fParentIndex - 1);
1095 Definition* prior = &*tokenIter;
1096 if (Definition::Type::kBracket == def->fType &&
1097 Bracket::kSlashStar == prior->fBracket) {
1098 bodyEnd = prior->fContentStart - 1;
1099 }
1100 }
Cary Clark8032b982017-07-28 11:04:54 -04001101 // FIXME: roll end-trimming into writeBlockTrim call
1102 while (fStart < bodyEnd && ' ' >= bodyEnd[-1]) {
1103 --bodyEnd;
1104 }
1105 int blockSize = (int) (bodyEnd - fStart);
1106 if (blockSize) {
Cary Clark7cfcbca2018-01-04 16:11:51 -05001107 string debugstr(fStart, blockSize);
Cary Clark8032b982017-07-28 11:04:54 -04001108 this->writeBlock(blockSize, fStart);
1109 }
Cary Clark154beea2017-10-26 07:58:48 -04001110 startDef = &child;
Cary Clark8032b982017-07-28 11:04:54 -04001111 fStart = child.fContentStart;
1112 methodName = root->fName + "::" + child.fName;
Cary Clarkbad5ad72017-08-03 17:14:08 -04001113 inConstructor = root->fName == child.fName;
Cary Clark8032b982017-07-28 11:04:54 -04001114 fContinuation = child.fContentEnd;
Cary Clarkce101242017-09-01 15:51:02 -04001115 method = root->find(methodName, RootDefinition::AllowParens::kNo);
Cary Clark154beea2017-10-26 07:58:48 -04001116// if (!method) {
1117// method = root->find(methodName + "()", RootDefinition::AllowParens::kNo);
1118// }
Cary Clark8032b982017-07-28 11:04:54 -04001119 if (!method) {
1120 continue;
1121 }
1122 if (method->fCloned) {
1123 clonedMethod = method;
1124 continue;
1125 }
Cary Clark579985c2017-07-31 11:48:27 -04001126 this->methodOut(method, child);
Cary Clark73fa9722017-08-29 17:36:51 -04001127 if (fAttrDeprecated) {
Cary Clark154beea2017-10-26 07:58:48 -04001128 startDef = fAttrDeprecated;
Cary Clark73fa9722017-08-29 17:36:51 -04001129 fStart = fAttrDeprecated->fContentStart;
1130 fAttrDeprecated = nullptr;
1131 }
Cary Clark8032b982017-07-28 11:04:54 -04001132 continue;
Ben Wagner63fd7602017-10-09 15:45:33 -04001133 }
Cary Clark8032b982017-07-28 11:04:54 -04001134 if (Definition::Type::kKeyWord == child.fType) {
Cary Clark154beea2017-10-26 07:58:48 -04001135 if (fIndentNext) {
Cary Clark154beea2017-10-26 07:58:48 -04001136 // too soon
1137#if 0 // makes struct Lattice indent when it oughtn't
1138 if (KeyWord::kEnum == child.fKeyWord) {
1139 fIndent += 4;
1140 }
1141 if (KeyWord::kPublic != child.fKeyWord) {
1142 fIndentNext = false;
1143 }
1144#endif
1145 }
1146 const Definition* cIncludeStructDef = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04001147 switch (child.fKeyWord) {
1148 case KeyWord::kStruct:
Cary Clark73fa9722017-08-29 17:36:51 -04001149 case KeyWord::kClass:
Cary Clark8032b982017-07-28 11:04:54 -04001150 // if struct contains members, compute their name and comment tabs
Cary Clark73fa9722017-08-29 17:36:51 -04001151 if (child.fChildren.size() > 0) {
1152 const ParentPair* testPair = &pair;
1153 while ((testPair = testPair->fPrev)) {
1154 if (KeyWord::kClass == testPair->fParent->fKeyWord) {
1155 inStruct = fInStruct = true;
1156 break;
1157 }
1158 }
1159 }
Cary Clark8032b982017-07-28 11:04:54 -04001160 if (fInStruct) {
Cary Clark154beea2017-10-26 07:58:48 -04001161 // try child; root+child; root->parent+child; etc.
1162 int trial = 0;
1163 const RootDefinition* search = root;
1164 const Definition* parent = search->fParent;
1165 do {
1166 string name;
1167 if (0 == trial) {
1168 name = child.fName;
1169 } else if (1 == trial) {
1170 name = root->fName + "::" + child.fName;
1171 } else {
1172 SkASSERT(parent);
1173 name = parent->fName + "::" + child.fName;
1174 search = parent->asRoot();
1175 parent = search->fParent;
1176 }
1177 fBmhStructDef = search->find(name, RootDefinition::AllowParens::kNo);
1178 } while (!fBmhStructDef && ++trial);
1179 root = const_cast<RootDefinition*>(fBmhStructDef->asRoot());
1180 SkASSERT(root);
Cary Clark8032b982017-07-28 11:04:54 -04001181 fIndent += 4;
Cary Clark8032b982017-07-28 11:04:54 -04001182 this->structSizeMembers(child);
1183 fIndent -= 4;
Cary Clark154beea2017-10-26 07:58:48 -04001184 SkASSERT(!fIndentNext);
1185 fIndentNext = true;
Cary Clark8032b982017-07-28 11:04:54 -04001186 }
Cary Clark8032b982017-07-28 11:04:54 -04001187 if (child.fChildren.size() > 0) {
1188 const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 :
1189 child.fContentStart;
Cary Clark73fa9722017-08-29 17:36:51 -04001190 this->writeBlockTrim((int) (bodyEnd - fStart), fStart);
Cary Clark154beea2017-10-26 07:58:48 -04001191 if (fPendingMethod) {
1192 fIndent -= 4;
1193 fPendingMethod = false;
1194 }
1195 startDef = requireDense ? requireDense : &child;
1196 fStart = requireDense ? requireDense->fContentStart : child.fContentStart;
1197 requireDense = nullptr;
1198 if (!fInStruct && child.fName != root->fName) {
1199 root = &fBmhParser->fClassMap[child.fName];
1200 fRootTopic = root->fParent;
1201 SkASSERT(!root->fVisited);
1202 root->clearVisited();
1203 fIndent = 0;
1204 fBmhStructDef = root;
1205 }
Cary Clark8032b982017-07-28 11:04:54 -04001206 if (child.fName == root->fName) {
1207 if (Definition* parent = root->fParent) {
1208 if (MarkType::kTopic == parent->fMarkType ||
1209 MarkType::kSubtopic == parent->fMarkType) {
Cary Clarke4aa3712017-09-15 02:56:12 -04001210 const char* commentStart = root->fContentStart;
1211 const char* commentEnd = root->fChildren[0]->fStart;
Cary Clark8032b982017-07-28 11:04:54 -04001212 this->structOut(root, *root, commentStart, commentEnd);
1213 } else {
1214 SkASSERT(0); // incomplete
1215 }
1216 } else {
1217 SkASSERT(0); // incomplete
1218 }
1219 } else {
Cary Clark154beea2017-10-26 07:58:48 -04001220 SkASSERT(fInStruct);
1221 #if 0
1222 fBmhStructDef = root->find(child.fName, RootDefinition::AllowParens::kNo);
1223 if (nullptr == fBmhStructDef) {
1224 fBmhStructDef = root->find(root->fName + "::" + child.fName,
Cary Clarkce101242017-09-01 15:51:02 -04001225 RootDefinition::AllowParens::kNo);
Cary Clark8032b982017-07-28 11:04:54 -04001226 }
Cary Clark154beea2017-10-26 07:58:48 -04001227 if (!fBmhStructDef) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001228 this->lf(2);
1229 fIndent = 0;
1230 this->writeBlock((int) (fStart - bodyEnd), bodyEnd);
1231 this->lfcr();
1232 continue;
1233 }
Cary Clark154beea2017-10-26 07:58:48 -04001234 #endif
Cary Clark8032b982017-07-28 11:04:54 -04001235 Definition* codeBlock = nullptr;
Cary Clark154beea2017-10-26 07:58:48 -04001236 Definition* nextBlock = nullptr;
1237 for (auto test : fBmhStructDef->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04001238 if (MarkType::kCode == test->fMarkType) {
1239 SkASSERT(!codeBlock); // FIXME: check enum for correct order earlier
1240 codeBlock = test;
1241 continue;
1242 }
1243 if (codeBlock) {
Cary Clark154beea2017-10-26 07:58:48 -04001244 nextBlock = test;
Cary Clark8032b982017-07-28 11:04:54 -04001245 break;
1246 }
1247 }
Cary Clark73fa9722017-08-29 17:36:51 -04001248 // FIXME: trigger error earlier if inner #Struct or #Class is missing #Code
Cary Clark8032b982017-07-28 11:04:54 -04001249 SkASSERT(nextBlock); // FIXME: check enum for correct order earlier
Cary Clark154beea2017-10-26 07:58:48 -04001250 const char* commentStart = codeBlock->fTerminator;
1251 const char* commentEnd = nextBlock->fStart;
1252 if (fIndentNext) {
1253// fIndent += 4;
1254 }
1255 fIndentNext = true;
1256 this->structOut(root, *fBmhStructDef, commentStart, commentEnd);
Cary Clark8032b982017-07-28 11:04:54 -04001257 }
1258 fDeferComment = nullptr;
1259 } else {
1260 ; // empty forward reference, nothing to do here
1261 }
1262 break;
1263 case KeyWord::kEnum: {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001264 fInEnum = true;
Cary Clark8032b982017-07-28 11:04:54 -04001265 this->enumHeaderOut(root, child);
1266 this->enumSizeItems(child);
1267 } break;
1268 case KeyWord::kConst:
1269 case KeyWord::kConstExpr:
1270 case KeyWord::kStatic:
1271 case KeyWord::kInt:
Cary Clarkd0530ba2017-09-14 11:25:39 -04001272 case KeyWord::kUint8_t:
1273 case KeyWord::kUint16_t:
Cary Clark8032b982017-07-28 11:04:54 -04001274 case KeyWord::kUint32_t:
Cary Clarkd0530ba2017-09-14 11:25:39 -04001275 case KeyWord::kUint64_t:
Cary Clark73fa9722017-08-29 17:36:51 -04001276 case KeyWord::kUnsigned:
Cary Clark8032b982017-07-28 11:04:54 -04001277 case KeyWord::kSize_t:
1278 case KeyWord::kFloat:
1279 case KeyWord::kBool:
1280 case KeyWord::kVoid:
1281 if (!memberStart) {
1282 memberStart = &child;
1283 }
1284 break;
1285 case KeyWord::kPublic:
1286 case KeyWord::kPrivate:
1287 case KeyWord::kProtected:
1288 case KeyWord::kFriend:
Cary Clark73fa9722017-08-29 17:36:51 -04001289 case KeyWord::kInline:
1290 case KeyWord::kSK_API:
Cary Clarkbad5ad72017-08-03 17:14:08 -04001291 case KeyWord::kTypedef:
Cary Clark8032b982017-07-28 11:04:54 -04001292 break;
Cary Clark154beea2017-10-26 07:58:48 -04001293 case KeyWord::kSK_BEGIN_REQUIRE_DENSE:
1294 requireDense = &child;
1295 break;
Cary Clark8032b982017-07-28 11:04:54 -04001296 default:
1297 SkASSERT(0);
1298 }
Cary Clark154beea2017-10-26 07:58:48 -04001299 if (cIncludeStructDef) {
Cary Clark8032b982017-07-28 11:04:54 -04001300 TextParser structName(&child);
1301 SkAssertResult(structName.skipToEndBracket('{'));
Cary Clark154beea2017-10-26 07:58:48 -04001302 startDef = &child;
Cary Clark8032b982017-07-28 11:04:54 -04001303 fStart = structName.fChar + 1;
1304 this->writeBlock((int) (fStart - child.fStart), child.fStart);
1305 this->lf(2);
1306 fIndent += 4;
Cary Clark154beea2017-10-26 07:58:48 -04001307 if (!this->populate(&child, &pair, const_cast<Definition*>(cIncludeStructDef)->asRoot())) {
Cary Clark8032b982017-07-28 11:04:54 -04001308 return false;
1309 }
1310 // output any remaining definitions at current indent level
1311 const char* structEnd = child.fContentEnd;
1312 SkAssertResult('}' == structEnd[-1]);
1313 --structEnd;
Cary Clarkbad5ad72017-08-03 17:14:08 -04001314 this->writeBlockTrim((int) (structEnd - fStart), fStart);
Cary Clark8032b982017-07-28 11:04:54 -04001315 this->lf(2);
1316 fStart = structEnd;
1317 fIndent -= 4;
1318 fContinuation = nullptr;
1319 fDeferComment = nullptr;
Cary Clarkd0530ba2017-09-14 11:25:39 -04001320 } else if (KeyWord::kUint8_t == child.fKeyWord) {
1321 continue;
Cary Clark8032b982017-07-28 11:04:54 -04001322 } else {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001323 if (fInEnum && KeyWord::kClass == child.fChildren[0]->fKeyWord) {
Cary Clark73fa9722017-08-29 17:36:51 -04001324 if (!this->populate(child.fChildren[0], &pair, root)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001325 return false;
1326 }
Cary Clark154beea2017-10-26 07:58:48 -04001327 } else {
1328 if (!this->populate(&child, &pair, root)) {
1329 return false;
1330 }
1331 if (KeyWord::kClass == child.fKeyWord || KeyWord::kStruct == child.fKeyWord) {
1332 fStructMemberTab = 0;
1333 if (fInStruct) {
1334 fInStruct = false;
1335 do {
1336 SkASSERT(root);
1337 root = const_cast<RootDefinition*>(root->fParent->asRoot());
1338 } while (MarkType::kTopic == root->fMarkType ||
1339 MarkType::kSubtopic == root->fMarkType);
1340 SkASSERT(MarkType::kStruct == root->fMarkType ||
1341 MarkType::kClass == root->fMarkType);
1342 fPendingMethod = false;
1343 if (startDef) {
1344 fPendingMethod = find_start(startDef, fStart);
1345 }
1346 fOutdentNext = !fPendingMethod;
1347 }
1348 }
Cary Clark8032b982017-07-28 11:04:54 -04001349 }
1350 }
1351 continue;
Ben Wagner63fd7602017-10-09 15:45:33 -04001352 }
Cary Clark8032b982017-07-28 11:04:54 -04001353 if (Definition::Type::kBracket == child.fType) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001354 if (KeyWord::kEnum == child.fParent->fKeyWord ||
Cary Clarkbad5ad72017-08-03 17:14:08 -04001355 (KeyWord::kClass == child.fParent->fKeyWord && child.fParent->fParent &&
1356 KeyWord::kEnum == child.fParent->fParent->fKeyWord)) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001357 SkASSERT(Bracket::kBrace == child.fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04001358 this->enumMembersOut(root, child);
1359 this->writeString("};");
1360 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -04001361 startDef = child.fParent;
Cary Clark8032b982017-07-28 11:04:54 -04001362 fStart = child.fParent->fContentEnd;
1363 SkASSERT(';' == fStart[0]);
1364 ++fStart;
1365 fDeferComment = nullptr;
1366 fInEnum = false;
Cary Clark154beea2017-10-26 07:58:48 -04001367 if (fIndentNext) {
1368// fIndent -= 4;
1369 fIndentNext = false;
1370 }
Cary Clark8032b982017-07-28 11:04:54 -04001371 continue;
Cary Clark73fa9722017-08-29 17:36:51 -04001372 }
1373 if (fAttrDeprecated) {
1374 continue;
1375 }
Cary Clark8032b982017-07-28 11:04:54 -04001376 fDeferComment = nullptr;
Cary Clark154beea2017-10-26 07:58:48 -04001377 if (KeyWord::kClass == def->fKeyWord || KeyWord::kStruct == def->fKeyWord) {
1378 fIndentNext = true;
1379 }
Cary Clark73fa9722017-08-29 17:36:51 -04001380 if (!this->populate(&child, &pair, root)) {
Cary Clark8032b982017-07-28 11:04:54 -04001381 return false;
1382 }
1383 continue;
1384 }
1385 if (Definition::Type::kWord == child.fType) {
1386 if (MarkType::kMember == child.fMarkType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001387 if (!memberStart) {
1388 auto iter = def->fTokens.begin();
1389 std::advance(iter, child.fParentIndex - 1);
1390 memberStart = &*iter;
Cary Clark154beea2017-10-26 07:58:48 -04001391 if (!fStructMemberTab) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001392 SkASSERT(KeyWord::kStruct == def->fParent->fKeyWord);
Cary Clark154beea2017-10-26 07:58:48 -04001393 fIndent += 4;
1394 this->structSizeMembers(*def->fParent);
1395 fIndent -= 4;
1396// SkASSERT(!fIndentNext);
1397 fIndentNext = true;
Cary Clark884dd7d2017-10-11 10:37:52 -04001398 }
1399 }
Cary Clarkbad5ad72017-08-03 17:14:08 -04001400 memberEnd = this->structMemberOut(memberStart, child);
Cary Clark154beea2017-10-26 07:58:48 -04001401 startDef = &child;
Cary Clark8032b982017-07-28 11:04:54 -04001402 fStart = child.fContentEnd + 1;
1403 fDeferComment = nullptr;
1404 }
1405 if (child.fMemberStart) {
1406 memberStart = &child;
1407 }
Cary Clark73fa9722017-08-29 17:36:51 -04001408 const char attrDeprecated[] = "SK_ATTR_DEPRECATED";
1409 const size_t attrDeprecatedLen = sizeof(attrDeprecated) - 1;
1410 if (attrDeprecatedLen == child.fContentEnd - child.fContentStart &&
1411 !strncmp(attrDeprecated, child.fStart, attrDeprecatedLen)) {
1412 fAttrDeprecated = &child;
1413 }
Cary Clark8032b982017-07-28 11:04:54 -04001414 continue;
1415 }
1416 if (Definition::Type::kPunctuation == child.fType) {
1417 if (Punctuation::kSemicolon == child.fPunctuation) {
1418 memberStart = nullptr;
1419 if (inStruct) {
1420 fInStruct = false;
1421 }
1422 continue;
1423 }
1424 if (Punctuation::kLeftBrace == child.fPunctuation ||
1425 Punctuation::kColon == child.fPunctuation ||
1426 Punctuation::kAsterisk == child.fPunctuation
1427 ) {
1428 continue;
1429 }
1430 }
1431 }
1432 return true;
1433}
1434
1435bool IncludeWriter::populate(BmhParser& bmhParser) {
1436 bool allPassed = true;
1437 for (auto& includeMapper : fIncludeMap) {
1438 size_t lastSlash = includeMapper.first.rfind('/');
Cary Clarkd0530ba2017-09-14 11:25:39 -04001439 if (string::npos == lastSlash) {
1440 lastSlash = includeMapper.first.rfind('\\');
1441 }
Cary Clark8032b982017-07-28 11:04:54 -04001442 if (string::npos == lastSlash || lastSlash >= includeMapper.first.length() - 1) {
1443 return this->reportError<bool>("malformed include name");
1444 }
1445 string fileName = includeMapper.first.substr(lastSlash + 1);
1446 if (".h" != fileName.substr(fileName.length() - 2)) {
1447 return this->reportError<bool>("expected fileName.h");
1448 }
1449 string skClassName = fileName.substr(0, fileName.length() - 2);
1450 fOut = fopen(fileName.c_str(), "wb");
1451 if (!fOut) {
1452 SkDebugf("could not open output file %s\n", fileName.c_str());
1453 return false;
1454 }
1455 if (bmhParser.fClassMap.end() == bmhParser.fClassMap.find(skClassName)) {
1456 return this->reportError<bool>("could not find bmh class");
1457 }
1458 fBmhParser = &bmhParser;
1459 RootDefinition* root = &bmhParser.fClassMap[skClassName];
1460 fRootTopic = root->fParent;
1461 root->clearVisited();
1462 fStart = includeMapper.second.fContentStart;
1463 fEnd = includeMapper.second.fContentEnd;
Cary Clark2f466242017-12-11 16:03:17 -05001464 fAnonymousEnumCount = 1;
Cary Clark73fa9722017-08-29 17:36:51 -04001465 allPassed &= this->populate(&includeMapper.second, nullptr, root);
Cary Clark8032b982017-07-28 11:04:54 -04001466 this->writeBlock((int) (fEnd - fStart), fStart);
1467 fIndent = 0;
1468 this->lfcr();
1469 this->writePending();
1470 fclose(fOut);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001471 fflush(fOut);
1472 size_t slash = fFileName.find_last_of('/');
1473 if (string::npos == slash) {
1474 slash = 0;
1475 }
1476 size_t back = fFileName.find_last_of('\\');
1477 if (string::npos == back) {
1478 back = 0;
1479 }
1480 string dir = fFileName.substr(0, SkTMax(slash, back) + 1);
1481 string readname = dir + fileName;
1482 if (this->writtenFileDiffers(fileName, readname)) {
1483 SkDebugf("wrote updated %s\n", fileName.c_str());
1484 } else {
1485 remove(fileName.c_str());
1486 }
Cary Clark8032b982017-07-28 11:04:54 -04001487 }
1488 return allPassed;
1489}
1490
1491// change Xxx_Xxx to xxx xxx
1492static string ConvertRef(const string str, bool first) {
1493 string substitute;
1494 for (char c : str) {
1495 if ('_' == c) {
1496 c = ' '; // change Xxx_Xxx to xxx xxx
1497 } else if (isupper(c) && !first) {
1498 c = tolower(c);
1499 }
1500 substitute += c;
1501 first = false;
1502 }
1503 return substitute;
1504}
1505
Cary Clark8032b982017-07-28 11:04:54 -04001506string IncludeWriter::resolveMethod(const char* start, const char* end, bool first) {
1507 string methodname(start, end - start);
Cary Clark579985c2017-07-31 11:48:27 -04001508 if (string::npos != methodname.find("()")) {
1509 return "";
1510 }
Cary Clark8032b982017-07-28 11:04:54 -04001511 string substitute;
1512 auto rootDefIter = fBmhParser->fMethodMap.find(methodname);
1513 if (fBmhParser->fMethodMap.end() != rootDefIter) {
1514 substitute = methodname + "()";
1515 } else {
Cary Clark1eace2d2017-07-31 07:52:43 -04001516 RootDefinition* parent = nullptr;
1517 for (auto candidate : fRootTopic->fChildren) {
1518 if (MarkType::kClass == candidate->fMarkType
1519 || MarkType::kStruct == candidate->fMarkType) {
1520 parent = candidate->asRoot();
1521 break;
1522 }
1523 }
1524 SkASSERT(parent);
Cary Clarkce101242017-09-01 15:51:02 -04001525 auto defRef = parent->find(parent->fName + "::" + methodname,
1526 RootDefinition::AllowParens::kNo);
Cary Clark8032b982017-07-28 11:04:54 -04001527 if (defRef && MarkType::kMethod == defRef->fMarkType) {
1528 substitute = methodname + "()";
1529 }
1530 }
Cary Clark579985c2017-07-31 11:48:27 -04001531 if (fMethodDef && methodname == fMethodDef->fName) {
1532 TextParser report(fBmhMethod);
1533 report.reportError("method should not include references to itself");
1534 return "";
1535 }
Cary Clark6fc50412017-09-21 12:31:06 -04001536 if (fBmhMethod) {
1537 for (auto child : fBmhMethod->fChildren) {
1538 if (MarkType::kParam != child->fMarkType) {
1539 continue;
1540 }
1541 if (methodname == child->fName) {
1542 return "";
1543 }
1544 }
1545 }
Cary Clark8032b982017-07-28 11:04:54 -04001546 return substitute;
1547}
1548
Cary Clarkce101242017-09-01 15:51:02 -04001549string IncludeWriter::resolveRef(const char* start, const char* end, bool first,
1550 RefType* refType) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001551 // look up Xxx_Xxx
Cary Clark8032b982017-07-28 11:04:54 -04001552 string undername(start, end - start);
Cary Clarkce101242017-09-01 15:51:02 -04001553 for (const auto& external : fBmhParser->fExternals) {
1554 if (external.fName == undername) {
1555 *refType = RefType::kExternal;
1556 return external.fName;
1557 }
1558 }
1559 *refType = RefType::kNormal;
Cary Clark8032b982017-07-28 11:04:54 -04001560 SkASSERT(string::npos == undername.find(' '));
1561 const Definition* rootDef = nullptr;
1562 {
1563 auto rootDefIter = fBmhParser->fTopicMap.find(undername);
1564 if (fBmhParser->fTopicMap.end() != rootDefIter) {
1565 rootDef = rootDefIter->second;
1566 } else {
1567 string prefixedName = fRootTopic->fName + '_' + undername;
1568 rootDefIter = fBmhParser->fTopicMap.find(prefixedName);
1569 if (fBmhParser->fTopicMap.end() != rootDefIter) {
1570 rootDef = rootDefIter->second;
Cary Clark154beea2017-10-26 07:58:48 -04001571 } else if (fBmhStructDef) {
1572 string localPrefix = fBmhStructDef->fFiddle + '_' + undername;
Cary Clarkbad5ad72017-08-03 17:14:08 -04001573 rootDefIter = fBmhParser->fTopicMap.find(localPrefix);
1574 if (fBmhParser->fTopicMap.end() != rootDefIter) {
1575 rootDef = rootDefIter->second;
1576 }
Cary Clark8032b982017-07-28 11:04:54 -04001577 } else {
1578 auto aliasIter = fBmhParser->fAliasMap.find(undername);
1579 if (fBmhParser->fAliasMap.end() != aliasIter) {
1580 rootDef = aliasIter->second->fParent;
1581 } else if (!first) {
Cary Clark8032b982017-07-28 11:04:54 -04001582 SkDebugf("unfound: %s\n", undername.c_str());
Cary Clark579985c2017-07-31 11:48:27 -04001583 this->reportError("reference unfound");
1584 return "";
Cary Clark8032b982017-07-28 11:04:54 -04001585 }
1586 }
1587 }
1588 }
1589 string substitute;
1590 if (rootDef) {
1591 for (auto child : rootDef->fChildren) {
1592 if (MarkType::kSubstitute == child->fMarkType) {
1593 substitute = string(child->fContentStart,
1594 (int) (child->fContentEnd - child->fContentStart));
1595 break;
1596 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001597 }
1598 if (!substitute.length()) {
1599 for (auto child : rootDef->fChildren) {
1600 if (MarkType::kClass == child->fMarkType ||
1601 MarkType::kStruct == child->fMarkType ||
1602 (MarkType::kEnum == child->fMarkType && !child->fAnonymous) ||
1603 MarkType::kEnumClass == child->fMarkType) {
1604 substitute = child->fName;
1605 if (MarkType::kEnum == child->fMarkType && fInEnum) {
1606 size_t parentClassEnd = substitute.find("::");
1607 SkASSERT(string::npos != parentClassEnd);
1608 substitute = substitute.substr(parentClassEnd + 2);
1609 }
1610 break;
Cary Clark8032b982017-07-28 11:04:54 -04001611 }
Cary Clark8032b982017-07-28 11:04:54 -04001612 }
1613 }
1614 if (!substitute.length()) {
1615 auto parent = rootDef->fParent;
1616 if (parent) {
1617 if (MarkType::kClass == parent->fMarkType ||
1618 MarkType::kStruct == parent->fMarkType ||
Cary Clarkd0530ba2017-09-14 11:25:39 -04001619 (MarkType::kEnum == parent->fMarkType && !parent->fAnonymous) ||
Cary Clark8032b982017-07-28 11:04:54 -04001620 MarkType::kEnumClass == parent->fMarkType) {
1621 if (parent->fParent != fRootTopic) {
1622 substitute = parent->fName;
1623 size_t under = undername.find('_');
1624 SkASSERT(string::npos != under);
1625 string secondHalf(&undername[under], (size_t) (undername.length() - under));
1626 substitute += ConvertRef(secondHalf, false);
1627 } else {
1628 substitute += ConvertRef(undername, first);
1629 }
1630 }
1631 }
1632 }
1633 }
Cary Clark1eace2d2017-07-31 07:52:43 -04001634 // Ensure first word after period is capitalized if substitute is lower cased.
1635 if (first && isupper(start[0]) && substitute.length() > 0 && islower(substitute[0])) {
1636 substitute[0] = start[0];
1637 }
Cary Clark8032b982017-07-28 11:04:54 -04001638 return substitute;
1639}
Cary Clark579985c2017-07-31 11:48:27 -04001640
Cary Clark8032b982017-07-28 11:04:54 -04001641int IncludeWriter::lookupMethod(const PunctuationState punctuation, const Word word,
Cary Clark6fc50412017-09-21 12:31:06 -04001642 const int lastSpace, const int run, int lastWrite, const char* data,
1643 bool hasIndirection) {
Cary Clark579985c2017-07-31 11:48:27 -04001644 int wordStart = lastSpace;
1645 while (' ' >= data[wordStart]) {
1646 ++wordStart;
1647 }
1648 const int wordEnd = PunctuationState::kDelimiter == punctuation ||
Cary Clark8032b982017-07-28 11:04:54 -04001649 PunctuationState::kPeriod == punctuation ? run - 1 : run;
Cary Clark6fc50412017-09-21 12:31:06 -04001650 string temp;
1651 if (hasIndirection && '(' != data[wordEnd - 1] && ')' != data[wordEnd - 1]) {
1652 // FIXME: hard-coded to assume a.b or a->b is a.b() or a->b().
1653 // need to check class a for member b to see if this is so
1654 TextParser parser(fFileName, &data[wordStart], &data[wordEnd], fLineCount);
1655 const char* indirection = parser.anyOf(".>");
1656 if (&data[wordEnd] <= &indirection[2] || 'f' != indirection[1] ||
1657 !isupper(indirection[2])) {
1658 temp = string(&data[wordStart], wordEnd - wordStart) + "()";
1659 }
1660 } else {
1661 temp = this->resolveMethod(&data[wordStart], &data[wordEnd], Word::kFirst == word);
1662 }
Cary Clark8032b982017-07-28 11:04:54 -04001663 if (temp.length()) {
Cary Clark579985c2017-07-31 11:48:27 -04001664 if (wordStart > lastWrite) {
1665 SkASSERT(data[wordStart - 1] >= ' ');
Cary Clark8032b982017-07-28 11:04:54 -04001666 if (' ' == data[lastWrite]) {
1667 this->writeSpace();
1668 }
Cary Clark579985c2017-07-31 11:48:27 -04001669 this->writeBlockTrim(wordStart - lastWrite, &data[lastWrite]);
1670 if (' ' == data[wordStart - 1]) {
Cary Clark8032b982017-07-28 11:04:54 -04001671 this->writeSpace();
1672 }
1673 }
1674 SkASSERT(temp[temp.length() - 1] > ' ');
1675 this->writeString(temp.c_str());
Cary Clark579985c2017-07-31 11:48:27 -04001676 lastWrite = wordEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001677 }
1678 return lastWrite;
1679}
1680
1681int IncludeWriter::lookupReference(const PunctuationState punctuation, const Word word,
1682 const int start, const int run, int lastWrite, const char last, const char* data) {
1683 const int end = PunctuationState::kDelimiter == punctuation ||
1684 PunctuationState::kPeriod == punctuation ? run - 1 : run;
Cary Clarkce101242017-09-01 15:51:02 -04001685 RefType refType = RefType::kUndefined;
1686 string resolved = string(&data[start], (size_t) (end - start));
1687 string temp = this->resolveRef(&data[start], &data[end], Word::kFirst == word, &refType);
Cary Clark8032b982017-07-28 11:04:54 -04001688 if (!temp.length()) {
1689 if (Word::kFirst != word && '_' != last) {
Cary Clarkce101242017-09-01 15:51:02 -04001690 temp = ConvertRef(resolved, false);
Cary Clark8032b982017-07-28 11:04:54 -04001691 }
Ben Wagner63fd7602017-10-09 15:45:33 -04001692 }
Cary Clark8032b982017-07-28 11:04:54 -04001693 if (temp.length()) {
1694 if (start > lastWrite) {
1695 SkASSERT(data[start - 1] >= ' ');
1696 if (' ' == data[lastWrite]) {
1697 this->writeSpace();
1698 }
1699 this->writeBlockTrim(start - lastWrite, &data[lastWrite]);
1700 if (' ' == data[start - 1]) {
1701 this->writeSpace();
1702 }
1703 }
1704 SkASSERT(temp[temp.length() - 1] > ' ');
1705 this->writeString(temp.c_str());
1706 lastWrite = end;
1707 }
1708 return lastWrite;
1709}
1710
1711/* returns true if rewriteBlock wrote linefeeds */
Cary Clarkd0530ba2017-09-14 11:25:39 -04001712IncludeWriter::Wrote IncludeWriter::rewriteBlock(int size, const char* data, Phrase phrase) {
Cary Clark8032b982017-07-28 11:04:54 -04001713 bool wroteLineFeeds = false;
1714 while (size > 0 && data[0] <= ' ') {
1715 --size;
1716 ++data;
1717 }
1718 while (size > 0 && data[size - 1] <= ' ') {
1719 --size;
1720 }
1721 if (0 == size) {
1722 return Wrote::kNone;
1723 }
1724 int run = 0;
1725 Word word = Word::kStart;
Cary Clarkd0530ba2017-09-14 11:25:39 -04001726 PunctuationState punctuation = Phrase::kNo == phrase ?
1727 PunctuationState::kStart : PunctuationState::kSpace;
Cary Clark8032b982017-07-28 11:04:54 -04001728 int start = 0;
1729 int lastWrite = 0;
1730 int lineFeeds = 0;
1731 int lastPrintable = 0;
Cary Clark579985c2017-07-31 11:48:27 -04001732 int lastSpace = -1;
Cary Clark8032b982017-07-28 11:04:54 -04001733 char c = 0;
Kevin Lubick42846132018-01-05 10:11:11 -05001734 char last = 0;
Cary Clark6fc50412017-09-21 12:31:06 -04001735 bool embeddedIndirection = false;
Cary Clark579985c2017-07-31 11:48:27 -04001736 bool embeddedSymbol = false;
Cary Clark8032b982017-07-28 11:04:54 -04001737 bool hasLower = false;
1738 bool hasUpper = false;
Cary Clark6fc50412017-09-21 12:31:06 -04001739 bool hasIndirection = false;
Cary Clark8032b982017-07-28 11:04:54 -04001740 bool hasSymbol = false;
1741 while (run < size) {
1742 last = c;
1743 c = data[run];
1744 SkASSERT(' ' <= c || '\n' == c);
1745 if (lineFeeds && ' ' < c) {
1746 if (lastPrintable >= lastWrite) {
1747 if (' ' == data[lastWrite]) {
1748 this->writeSpace();
Cary Clarka560c472017-11-27 10:44:06 -05001749 lastWrite++;
Cary Clark8032b982017-07-28 11:04:54 -04001750 }
1751 this->writeBlock(lastPrintable - lastWrite + 1, &data[lastWrite]);
1752 }
1753 if (lineFeeds > 1) {
1754 this->lf(2);
1755 }
1756 this->lfcr(); // defer the indent until non-whitespace is seen
1757 lastWrite = run;
1758 lineFeeds = 0;
1759 }
1760 if (' ' < c) {
1761 lastPrintable = run;
1762 }
1763 switch (c) {
1764 case '\n':
1765 ++lineFeeds;
1766 wroteLineFeeds = true;
1767 case ' ':
1768 switch (word) {
1769 case Word::kStart:
1770 break;
1771 case Word::kUnderline:
1772 case Word::kCap:
1773 case Word::kFirst:
1774 if (!hasLower) {
1775 break;
1776 }
1777 lastWrite = this->lookupReference(punctuation, word, start, run,
1778 lastWrite, last, data);
1779 break;
1780 case Word::kMixed:
Cary Clark579985c2017-07-31 11:48:27 -04001781 if (hasUpper && hasLower && !hasSymbol && lastSpace > 0) {
1782 lastWrite = this->lookupMethod(punctuation, word, lastSpace, run,
Cary Clark6fc50412017-09-21 12:31:06 -04001783 lastWrite, data, hasIndirection && !hasSymbol);
Cary Clark8032b982017-07-28 11:04:54 -04001784 }
1785 break;
1786 default:
1787 SkASSERT(0);
1788 }
Cary Clark1eace2d2017-07-31 07:52:43 -04001789 punctuation = PunctuationState::kPeriod == punctuation ||
Ben Wagner63fd7602017-10-09 15:45:33 -04001790 (PunctuationState::kStart == punctuation && ' ' >= last) ?
Cary Clark1eace2d2017-07-31 07:52:43 -04001791 PunctuationState::kStart : PunctuationState::kSpace;
Cary Clark8032b982017-07-28 11:04:54 -04001792 word = Word::kStart;
Cary Clark6fc50412017-09-21 12:31:06 -04001793 embeddedIndirection = false;
Cary Clark579985c2017-07-31 11:48:27 -04001794 embeddedSymbol = false;
Cary Clark8032b982017-07-28 11:04:54 -04001795 hasLower = false;
1796 hasUpper = false;
Cary Clark6fc50412017-09-21 12:31:06 -04001797 hasIndirection = false;
Cary Clark8032b982017-07-28 11:04:54 -04001798 hasSymbol = false;
Cary Clark579985c2017-07-31 11:48:27 -04001799 lastSpace = run;
Cary Clark8032b982017-07-28 11:04:54 -04001800 break;
1801 case '.':
1802 switch (word) {
1803 case Word::kStart:
1804 punctuation = PunctuationState::kDelimiter;
1805 case Word::kCap:
1806 case Word::kFirst:
1807 case Word::kUnderline:
1808 case Word::kMixed:
1809 if (PunctuationState::kDelimiter == punctuation ||
1810 PunctuationState::kPeriod == punctuation) {
1811 word = Word::kMixed;
1812 }
1813 punctuation = PunctuationState::kPeriod;
1814 break;
1815 default:
1816 SkASSERT(0);
1817 }
Cary Clark6fc50412017-09-21 12:31:06 -04001818 embeddedIndirection = true;
Cary Clark8032b982017-07-28 11:04:54 -04001819 break;
1820 case ',': case ';': case ':':
Cary Clark8032b982017-07-28 11:04:54 -04001821 switch (word) {
1822 case Word::kStart:
1823 punctuation = PunctuationState::kDelimiter;
1824 case Word::kCap:
1825 case Word::kFirst:
1826 case Word::kUnderline:
1827 case Word::kMixed:
1828 if (PunctuationState::kDelimiter == punctuation ||
1829 PunctuationState::kPeriod == punctuation) {
1830 word = Word::kMixed;
1831 }
1832 punctuation = PunctuationState::kDelimiter;
1833 break;
1834 default:
1835 SkASSERT(0);
1836 }
Cary Clark579985c2017-07-31 11:48:27 -04001837 embeddedSymbol = true;
Cary Clark8032b982017-07-28 11:04:54 -04001838 break;
Cary Clark6fc50412017-09-21 12:31:06 -04001839 case '>':
1840 if ('-' == last) {
1841 embeddedIndirection = true;
1842 break;
1843 }
Cary Clark8032b982017-07-28 11:04:54 -04001844 case '\'': // possessive apostrophe isn't treated as delimiting punctation
Cary Clarkce101242017-09-01 15:51:02 -04001845 case '\"': // quote is passed straight through
Cary Clark8032b982017-07-28 11:04:54 -04001846 case '=':
1847 case '!': // assumed not to be punctuation, but a programming symbol
Cary Clark6fc50412017-09-21 12:31:06 -04001848 case '&': case '<': case '{': case '}': case '/': case '*': case '[': case ']':
Cary Clark8032b982017-07-28 11:04:54 -04001849 word = Word::kMixed;
Cary Clark579985c2017-07-31 11:48:27 -04001850 embeddedSymbol = true;
Cary Clark8032b982017-07-28 11:04:54 -04001851 break;
1852 case '(':
1853 if (' ' == last) {
1854 punctuation = PunctuationState::kDelimiter;
1855 } else {
1856 word = Word::kMixed;
1857 }
Cary Clark579985c2017-07-31 11:48:27 -04001858 embeddedSymbol = true;
Cary Clark8032b982017-07-28 11:04:54 -04001859 break;
1860 case ')': // assume word type has already been set
1861 punctuation = PunctuationState::kDelimiter;
Cary Clark579985c2017-07-31 11:48:27 -04001862 embeddedSymbol = true;
Cary Clark8032b982017-07-28 11:04:54 -04001863 break;
1864 case '_':
1865 switch (word) {
1866 case Word::kStart:
1867 word = Word::kMixed;
1868 break;
1869 case Word::kCap:
1870 case Word::kFirst:
1871 case Word::kUnderline:
1872 word = Word::kUnderline;
1873 break;
1874 case Word::kMixed:
1875 break;
1876 default:
1877 SkASSERT(0);
1878 }
Cary Clark579985c2017-07-31 11:48:27 -04001879 hasSymbol |= embeddedSymbol;
Cary Clark8032b982017-07-28 11:04:54 -04001880 break;
Cary Clark73fa9722017-08-29 17:36:51 -04001881 case '+':
1882 // hackery to allow C++
1883 SkASSERT('C' == last || '+' == last); // FIXME: don't allow + outside of #Formula
1884 break;
Cary Clark8032b982017-07-28 11:04:54 -04001885 case 'A': case 'B': case 'C': case 'D': case 'E':
1886 case 'F': case 'G': case 'H': case 'I': case 'J':
1887 case 'K': case 'L': case 'M': case 'N': case 'O':
1888 case 'P': case 'Q': case 'R': case 'S': case 'T':
1889 case 'U': case 'V': case 'W': case 'X': case 'Y':
1890 case 'Z':
1891 switch (word) {
1892 case Word::kStart:
1893 word = PunctuationState::kStart == punctuation ? Word::kFirst : Word::kCap;
1894 start = run;
1895 break;
1896 case Word::kCap:
1897 case Word::kFirst:
Cary Clark154beea2017-10-26 07:58:48 -04001898 if (!isupper(last) && '~' != last) {
Cary Clark8032b982017-07-28 11:04:54 -04001899 word = Word::kMixed;
1900 }
1901 break;
1902 case Word::kUnderline:
1903 // some word in Xxx_XXX_Xxx can be all upper, but all can't: XXX_XXX
1904 if ('_' != last && !isupper(last)) {
1905 word = Word::kMixed;
1906 }
1907 break;
1908 case Word::kMixed:
1909 break;
1910 default:
1911 SkASSERT(0);
1912 }
1913 hasUpper = true;
1914 if (PunctuationState::kPeriod == punctuation ||
1915 PunctuationState::kDelimiter == punctuation) {
1916 word = Word::kMixed;
Cary Clark8032b982017-07-28 11:04:54 -04001917 }
Cary Clark6fc50412017-09-21 12:31:06 -04001918 hasIndirection |= embeddedIndirection;
Cary Clark579985c2017-07-31 11:48:27 -04001919 hasSymbol |= embeddedSymbol;
Cary Clark8032b982017-07-28 11:04:54 -04001920 break;
1921 case 'a': case 'b': case 'c': case 'd': case 'e':
1922 case 'f': case 'g': case 'h': case 'i': case 'j':
1923 case 'k': case 'l': case 'm': case 'n': case 'o':
1924 case 'p': case 'q': case 'r': case 's': case 't':
1925 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04001926 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04001927 case '0': case '1': case '2': case '3': case '4':
1928 case '5': case '6': case '7': case '8': case '9':
1929 case '-':
1930 switch (word) {
1931 case Word::kStart:
1932 word = Word::kMixed;
1933 break;
1934 case Word::kMixed:
1935 case Word::kCap:
1936 case Word::kFirst:
1937 case Word::kUnderline:
1938 break;
1939 default:
1940 SkASSERT(0);
1941 }
1942 hasLower = true;
1943 punctuation = PunctuationState::kStart;
Cary Clark6fc50412017-09-21 12:31:06 -04001944 hasIndirection |= embeddedIndirection;
Cary Clark579985c2017-07-31 11:48:27 -04001945 hasSymbol |= embeddedSymbol;
Cary Clark8032b982017-07-28 11:04:54 -04001946 break;
Cary Clark154beea2017-10-26 07:58:48 -04001947 case '~':
1948 SkASSERT(Word::kStart == word);
1949 word = PunctuationState::kStart == punctuation ? Word::kFirst : Word::kCap;
1950 start = run;
1951 hasUpper = true;
1952 hasIndirection |= embeddedIndirection;
1953 hasSymbol |= embeddedSymbol;
1954 break;
Cary Clark8032b982017-07-28 11:04:54 -04001955 default:
1956 SkASSERT(0);
1957 }
1958 ++run;
1959 }
1960 if ((word == Word::kCap || word == Word::kFirst || word == Word::kUnderline) && hasLower) {
1961 lastWrite = this->lookupReference(punctuation, word, start, run, lastWrite, last, data);
Cary Clark579985c2017-07-31 11:48:27 -04001962 } else if (word == Word::kMixed && hasUpper && hasLower && !hasSymbol && lastSpace > 0) {
Cary Clark6fc50412017-09-21 12:31:06 -04001963 lastWrite = this->lookupMethod(punctuation, word, lastSpace, run, lastWrite, data,
1964 hasIndirection && !hasSymbol);
Cary Clark8032b982017-07-28 11:04:54 -04001965 }
1966 if (run > lastWrite) {
1967 if (' ' == data[lastWrite]) {
1968 this->writeSpace();
1969 }
1970 this->writeBlock(run - lastWrite, &data[lastWrite]);
1971 }
1972 return wroteLineFeeds ? Wrote::kLF : Wrote::kChars;
1973}