blob: e2ba6c67b8d94d55d76af96d90f5dbdb16c249d9 [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 Clark56356312018-02-08 14:45:18 -050010void IncludeWriter::constOut(const Definition* memberStart, const Definition& child,
Cary Clarkc68ba1d2018-02-20 10:35:29 -050011 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;
Cary Clark1a8d7622018-03-05 13:26:16 -050018 this->descriptionOut(bmhConst, SkipFirstLine::kYes, Phrase::kNo);
Cary Clarkc68ba1d2018-02-20 10:35:29 -050019 fIndent -= 4;
20 this->writeCommentTrailer();
21 fStart = memberStart->fContentStart;
Cary Clark56356312018-02-08 14:45:18 -050022}
23
Cary Clark1a8d7622018-03-05 13:26:16 -050024void IncludeWriter::descriptionOut(const Definition* def, SkipFirstLine skipFirstLine,
25 Phrase phrase) {
Cary Clark6fc50412017-09-21 12:31:06 -040026 const char* commentStart = def->fContentStart;
Cary Clarkc68ba1d2018-02-20 10:35:29 -050027 if (SkipFirstLine::kYes == skipFirstLine) {
28 TextParser parser(def);
29 SkAssertResult(parser.skipLine());
30 commentStart = parser.fChar;
31 }
32 int commentLen = (int) (def->fContentEnd - commentStart);
33 bool breakOut = false;
Cary Clark154beea2017-10-26 07:58:48 -040034 SkDEBUGCODE(bool wroteCode = false);
Cary Clarkc68ba1d2018-02-20 10:35:29 -050035 if (def->fDeprecated) {
36 this->writeString(def->fToBeDeprecated ? "To be deprecated soon." : "Deprecated.");
37 this->lfcr();
38 }
Cary Clark6fc50412017-09-21 12:31:06 -040039 for (auto prop : def->fChildren) {
40 switch (prop->fMarkType) {
Cary Clark154beea2017-10-26 07:58:48 -040041 case MarkType::kCode: {
42 bool literal = false;
43 bool literalOutdent = false;
44 commentLen = (int) (prop->fStart - commentStart);
45 if (commentLen > 0) {
46 SkASSERT(commentLen < 1000);
47 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) {
48 this->lf(2);
49 }
50 }
51 size_t childSize = prop->fChildren.size();
52 if (childSize) {
53 SkASSERT(1 == childSize || 2 == childSize); // incomplete
54 SkASSERT(MarkType::kLiteral == prop->fChildren[0]->fMarkType);
55 SkASSERT(1 == childSize || MarkType::kOutdent == prop->fChildren[1]->fMarkType);
56 commentStart = prop->fChildren[childSize - 1]->fContentStart;
57 literal = true;
58 literalOutdent = 2 == childSize &&
59 MarkType::kOutdent == prop->fChildren[1]->fMarkType;
60 }
61 commentLen = (int) (prop->fContentEnd - commentStart);
62 SkASSERT(commentLen > 0);
63 if (literal) {
64 if (!literalOutdent) {
65 fIndent += 4;
66 }
67 this->writeBlockIndent(commentLen, commentStart);
68 this->lf(2);
69 if (!literalOutdent) {
70 fIndent -= 4;
71 }
72 commentStart = prop->fTerminator;
73 SkDEBUGCODE(wroteCode = true);
74 }
75 } break;
Cary Clark6fc50412017-09-21 12:31:06 -040076 case MarkType::kDefinedBy:
77 commentStart = prop->fTerminator;
78 break;
Cary Clarkc68ba1d2018-02-20 10:35:29 -050079 case MarkType::kBug: {
80 string bugstr("(see skbug.com/" + string(prop->fContentStart,
81 prop->fContentEnd - prop->fContentStart) + ')');
82 this->writeString(bugstr);
83 this->lfcr();
84 }
Ben Wagner63fd7602017-10-09 15:45:33 -040085 case MarkType::kDeprecated:
Cary Clark6fc50412017-09-21 12:31:06 -040086 case MarkType::kPrivate:
87 commentLen = (int) (prop->fStart - commentStart);
88 if (commentLen > 0) {
89 SkASSERT(commentLen < 1000);
90 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) {
91 this->lfcr();
92 }
93 }
94 commentStart = prop->fContentStart;
Cary Clark4855f782018-02-06 09:41:53 -050095 if (def->fToBeDeprecated) {
96 commentStart += 4; // skip over "soon" // FIXME: this is awkward
Cary Clarkc68ba1d2018-02-20 10:35:29 -050097 } else if (MarkType::kBug == prop->fMarkType) {
98 commentStart = prop->fContentEnd;
99 }
Cary Clark6fc50412017-09-21 12:31:06 -0400100 commentLen = (int) (prop->fContentEnd - commentStart);
101 if (commentLen > 0) {
Cary Clark3cd22cc2017-12-01 11:49:58 -0500102 this->writeBlockIndent(commentLen, commentStart);
Cary Clark1a8d7622018-03-05 13:26:16 -0500103 const char* end = commentStart + commentLen;
104 while (end > commentStart && ' ' == end[-1]) {
105 --end;
106 }
107 if (end > commentStart && '\n' == end[-1]) {
Cary Clark61dfc3a2018-01-03 08:37:53 -0500108 this->lfcr();
109 }
Cary Clark6fc50412017-09-21 12:31:06 -0400110 }
111 commentStart = prop->fTerminator;
112 commentLen = (int) (def->fContentEnd - commentStart);
113 break;
114 case MarkType::kExperimental:
115 this->writeString("EXPERIMENTAL:");
116 this->writeSpace();
117 commentStart = prop->fContentStart;
118 commentLen = (int) (prop->fContentEnd - commentStart);
119 if (commentLen > 0) {
120 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) {
121 this->lfcr();
122 }
123 }
124 commentStart = prop->fTerminator;
125 commentLen = (int) (def->fContentEnd - commentStart);
126 break;
Cary Clark154beea2017-10-26 07:58:48 -0400127 case MarkType::kFormula: {
Cary Clark6fc50412017-09-21 12:31:06 -0400128 commentLen = prop->fStart - commentStart;
129 if (commentLen > 0) {
130 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) {
Cary Clark154beea2017-10-26 07:58:48 -0400131 if (commentLen > 1 && '\n' == prop->fStart[-1] &&
132 '\n' == prop->fStart[-2]) {
133 this->lf(1);
134 } else {
135 this->writeSpace();
136 }
Cary Clark6fc50412017-09-21 12:31:06 -0400137 }
138 }
Cary Clark154beea2017-10-26 07:58:48 -0400139 int saveIndent = fIndent;
140 if (fIndent < fColumn + 1) {
141 fIndent = fColumn + 1;
142 }
143 this->writeBlockIndent(prop->length(), prop->fContentStart);
144 fIndent = saveIndent;
Cary Clark6fc50412017-09-21 12:31:06 -0400145 commentStart = prop->fTerminator;
146 commentLen = (int) (def->fContentEnd - commentStart);
Cary Clark154beea2017-10-26 07:58:48 -0400147 if (commentLen > 1 && '\n' == commentStart[0] && '\n' == commentStart[1]) {
Cary Clark6fc50412017-09-21 12:31:06 -0400148 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400149 } else {
150 SkASSERT('\n' == prop->fTerminator[0]);
151 if ('.' != prop->fTerminator[1] && !fLinefeeds) {
152 this->writeSpace();
153 }
Cary Clark6fc50412017-09-21 12:31:06 -0400154 }
Cary Clark154beea2017-10-26 07:58:48 -0400155 } break;
Cary Clarkab2621d2018-01-30 10:08:57 -0500156 case MarkType::kIn:
157 case MarkType::kLine:
Cary Clark6fc50412017-09-21 12:31:06 -0400158 case MarkType::kToDo:
159 commentLen = (int) (prop->fStart - commentStart);
160 if (commentLen > 0) {
161 SkASSERT(commentLen < 1000);
162 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart, Phrase::kNo)) {
163 this->lfcr();
164 }
165 }
166 commentStart = prop->fTerminator;
167 commentLen = (int) (def->fContentEnd - commentStart);
168 break;
169 case MarkType::kList:
170 commentLen = prop->fStart - commentStart;
171 if (commentLen > 0) {
172 if (Wrote::kNone != this->rewriteBlock(commentLen, commentStart,
173 Phrase::kNo)) {
174 this->lfcr();
175 }
176 }
177 for (auto row : prop->fChildren) {
178 SkASSERT(MarkType::kRow == row->fMarkType);
179 for (auto column : row->fChildren) {
180 SkASSERT(MarkType::kColumn == column->fMarkType);
181 this->writeString("-");
182 this->writeSpace();
Cary Clark1a8d7622018-03-05 13:26:16 -0500183 this->descriptionOut(column, SkipFirstLine::kNo, Phrase::kNo);
Cary Clark6fc50412017-09-21 12:31:06 -0400184 this->lf(1);
185 }
186 }
187 commentStart = prop->fTerminator;
188 commentLen = (int) (def->fContentEnd - commentStart);
189 if ('\n' == commentStart[0] && '\n' == commentStart[1]) {
190 this->lf(2);
191 }
192 break;
Cary Clark1a8d7622018-03-05 13:26:16 -0500193 case MarkType::kPhraseRef: {
194 commentLen = prop->fStart - commentStart;
195 if (commentLen > 0) {
196 this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
197 // ince we don't do line wrapping, always insert LF before phrase
198 this->lfcr(); // TODO: remove this once rewriteBlock rewraps paragraphs
199 }
200 auto iter = fBmhParser->fPhraseMap.find(prop->fName);
201 if (fBmhParser->fPhraseMap.end() == iter) {
202 return this->reportError<void>("missing phrase definition");
203 }
204 Definition* phraseDef = iter->second;
205 this->rewriteBlock(phraseDef->length(), phraseDef->fContentStart, Phrase::kYes);
206 commentStart = prop->fContentStart;
207 commentLen = (int) (def->fContentEnd - commentStart);
208 } break;
Cary Clark6fc50412017-09-21 12:31:06 -0400209 default:
210 commentLen = (int) (prop->fStart - commentStart);
211 breakOut = true;
212 }
213 if (breakOut) {
214 break;
215 }
216 }
Cary Clark4855f782018-02-06 09:41:53 -0500217 SkASSERT(wroteCode || (commentLen > 0 && commentLen < 1500) || def->fDeprecated);
Cary Clark154beea2017-10-26 07:58:48 -0400218 if (commentLen > 0) {
Cary Clark1a8d7622018-03-05 13:26:16 -0500219 this->rewriteBlock(commentLen, commentStart, phrase);
Cary Clark154beea2017-10-26 07:58:48 -0400220 }
Cary Clark6fc50412017-09-21 12:31:06 -0400221}
222
Cary Clark8032b982017-07-28 11:04:54 -0400223void IncludeWriter::enumHeaderOut(const RootDefinition* root,
224 const Definition& child) {
225 const Definition* enumDef = nullptr;
226 const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 :
227 child.fContentStart;
228 this->writeBlockTrim((int) (bodyEnd - fStart), fStart); // may write nothing
229 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -0400230 if (fIndentNext) {
231 fIndent += 4;
232 fIndentNext = false;
233 }
Cary Clark8032b982017-07-28 11:04:54 -0400234 fDeferComment = nullptr;
235 fStart = child.fContentStart;
236 const auto& nameDef = child.fTokens.front();
237 string fullName;
238 if (nullptr != nameDef.fContentEnd) {
Cary Clarkbad5ad72017-08-03 17:14:08 -0400239 TextParser enumClassCheck(&nameDef);
240 const char* start = enumClassCheck.fStart;
241 size_t len = (size_t) (enumClassCheck.fEnd - start);
242 bool enumClass = enumClassCheck.skipExact("class ");
243 if (enumClass) {
244 start = enumClassCheck.fChar;
245 const char* end = enumClassCheck.anyOf(" \n;{");
246 len = (size_t) (end - start);
247 }
248 string enumName(start, len);
249 if (enumClass) {
250 child.fChildren[0]->fName = enumName;
251 }
Cary Clark8032b982017-07-28 11:04:54 -0400252 fullName = root->fName + "::" + enumName;
Cary Clarkce101242017-09-01 15:51:02 -0400253 enumDef = root->find(enumName, RootDefinition::AllowParens::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400254 if (!enumDef) {
Cary Clarkce101242017-09-01 15:51:02 -0400255 enumDef = root->find(fullName, RootDefinition::AllowParens::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400256 }
Cary Clark06c20f32018-03-20 15:53:27 -0400257 if (!enumDef) {
258 auto mapEntry = fBmhParser->fEnumMap.find(enumName);
259 if (fBmhParser->fEnumMap.end() != mapEntry) {
260 enumDef = &mapEntry->second;
261 }
262 }
Cary Clark8032b982017-07-28 11:04:54 -0400263 SkASSERT(enumDef);
264 // child[0] should be #Code comment starts at child[0].fTerminator
265 // though skip until #Code is found (in case there's a #ToDo, etc)
266 // child[1] should be #Const comment ends at child[1].fStart
267 // comment becomes enum header (if any)
268 } else {
269 string enumName(root->fName);
270 enumName += "::_anonymous";
271 if (fAnonymousEnumCount > 1) {
272 enumName += '_' + to_string(fAnonymousEnumCount);
273 }
Cary Clarkce101242017-09-01 15:51:02 -0400274 enumDef = root->find(enumName, RootDefinition::AllowParens::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400275 SkASSERT(enumDef);
276 ++fAnonymousEnumCount;
277 }
278 Definition* codeBlock = nullptr;
279 const char* commentStart = nullptr;
280 bool wroteHeader = false;
Cary Clark6fc50412017-09-21 12:31:06 -0400281 bool lastAnchor = false;
Cary Clark8032b982017-07-28 11:04:54 -0400282 SkDEBUGCODE(bool foundConst = false);
283 for (auto test : enumDef->fChildren) {
284 if (MarkType::kCode == test->fMarkType) {
285 SkASSERT(!codeBlock); // FIXME: check enum for correct order earlier
286 codeBlock = test;
287 commentStart = codeBlock->fTerminator;
288 continue;
289 }
290 if (!codeBlock) {
291 continue;
292 }
293 const char* commentEnd = test->fStart;
294 if (!wroteHeader &&
295 !this->contentFree((int) (commentEnd - commentStart), commentStart)) {
Cary Clark154beea2017-10-26 07:58:48 -0400296 if (fIndentNext) {
297 fIndent += 4;
298 }
Cary Clark8032b982017-07-28 11:04:54 -0400299 this->writeCommentHeader();
300 this->writeString("\\enum");
Cary Clarkbad5ad72017-08-03 17:14:08 -0400301 if (fullName.length() > 0) {
302 this->writeSpace();
303 this->writeString(fullName.c_str());
304 }
Cary Clark8032b982017-07-28 11:04:54 -0400305 fIndent += 4;
306 this->lfcr();
307 wroteHeader = true;
308 }
Cary Clark6fc50412017-09-21 12:31:06 -0400309 if (lastAnchor) {
310 if (commentEnd - commentStart > 1) {
311 SkASSERT('\n' == commentStart[0]);
312 if (' ' == commentStart[1]) {
313 this->writeSpace();
314 }
315 }
316 lastAnchor = false;
317 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400318 this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400319 if (MarkType::kAnchor == test->fMarkType) {
Cary Clark154beea2017-10-26 07:58:48 -0400320 bool newLine = commentEnd - commentStart > 1 &&
321 '\n' == commentEnd[-1] && '\n' == commentEnd[-2];
Cary Clark8032b982017-07-28 11:04:54 -0400322 commentStart = test->fContentStart;
323 commentEnd = test->fChildren[0]->fStart;
Cary Clark154beea2017-10-26 07:58:48 -0400324 if (newLine) {
325 this->lf(2);
326 } else {
327 this->writeSpace();
328 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400329 this->rewriteBlock((int) (commentEnd - commentStart), commentStart, Phrase::kNo);
Cary Clark6fc50412017-09-21 12:31:06 -0400330 lastAnchor = true; // this->writeSpace();
Cary Clark8032b982017-07-28 11:04:54 -0400331 }
332 commentStart = test->fTerminator;
333 if (MarkType::kConst == test->fMarkType) {
334 SkASSERT(codeBlock); // FIXME: check enum for correct order earlier
335 SkDEBUGCODE(foundConst = true);
336 break;
337 }
338 }
339 SkASSERT(codeBlock);
340 SkASSERT(foundConst);
341 if (wroteHeader) {
342 fIndent -= 4;
343 this->lfcr();
344 this->writeCommentTrailer();
345 }
Cary Clarkbad5ad72017-08-03 17:14:08 -0400346 Definition* braceHolder = child.fChildren[0];
347 if (KeyWord::kClass == braceHolder->fKeyWord) {
348 braceHolder = braceHolder->fChildren[0];
349 }
350 bodyEnd = braceHolder->fContentStart;
Cary Clark8032b982017-07-28 11:04:54 -0400351 SkASSERT('{' == bodyEnd[0]);
352 ++bodyEnd;
353 this->lfcr();
354 this->writeBlock((int) (bodyEnd - fStart), fStart); // write include "enum Name {"
355 fIndent += 4;
356 this->singleLF();
357 fStart = bodyEnd;
358 fEnumDef = enumDef;
359}
360
Cary Clarkbad5ad72017-08-03 17:14:08 -0400361void IncludeWriter::enumMembersOut(const RootDefinition* root, Definition& child) {
Cary Clark8032b982017-07-28 11:04:54 -0400362 // iterate through include tokens and find how much remains for 1 line comments
363 // put ones that fit on same line, ones that are too big on preceding line?
364 const Definition* currentEnumItem = nullptr;
365 const char* commentStart = nullptr;
366 const char* lastEnd = nullptr;
367 int commentLen = 0;
368 enum class State {
369 kNoItem,
370 kItemName,
371 kItemValue,
372 kItemComment,
373 };
374 State state = State::kNoItem;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400375 vector<IterState> iterStack;
376 iterStack.emplace_back(child.fTokens.begin(), child.fTokens.end());
377 IterState* iterState = &iterStack[0];
Cary Clark186d08f2018-04-03 08:43:27 -0400378 Preprocessor preprocessor;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400379 for (int onePast = 0; onePast < 2; onePast += iterState->fDefIter == iterState->fDefEnd) {
380 Definition* token = onePast ? nullptr : &*iterState->fDefIter++;
Cary Clark186d08f2018-04-03 08:43:27 -0400381 if (this->enumPreprocessor(token, MemberPass::kOut, iterStack, &iterState,
382 &preprocessor)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -0400383 continue;
384 }
Cary Clark8032b982017-07-28 11:04:54 -0400385 if (token && State::kItemName == state) {
386 TextParser enumLine(token->fFileName, lastEnd,
387 token->fContentStart, token->fLineCount);
388 const char* end = enumLine.anyOf(",}=");
389 SkASSERT(end);
390 state = '=' == *end ? State::kItemValue : State::kItemComment;
391 if (State::kItemValue == state) { // write enum value
392 this->indentToColumn(fEnumItemValueTab);
393 this->writeString("=");
394 this->writeSpace();
395 lastEnd = token->fContentEnd;
396 this->writeBlock((int) (lastEnd - token->fContentStart),
397 token->fContentStart); // write const value if any
398 continue;
399 }
400 }
401 if (token && State::kItemValue == state) {
402 TextParser valueEnd(token->fFileName, lastEnd,
403 token->fContentStart, token->fLineCount);
404 const char* end = valueEnd.anyOf(",}");
405 if (!end) { // write expression continuation
406 if (' ' == lastEnd[0]) {
407 this->writeSpace();
408 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400409 this->writeBlock((int) (token->fContentEnd - lastEnd), lastEnd);
Cary Clark8032b982017-07-28 11:04:54 -0400410 continue;
411 }
412 }
413 if (State::kNoItem != state) {
414 this->writeString(",");
415 SkASSERT(currentEnumItem);
416 if (currentEnumItem->fShort) {
417 this->indentToColumn(fEnumItemCommentTab);
Cary Clarkc68ba1d2018-02-20 10:35:29 -0500418 if (commentLen || currentEnumItem->fDeprecated) {
419 this->writeString("//!<");
420 this->writeSpace();
421 if (currentEnumItem->fDeprecated) {
422 this->writeString(child.fToBeDeprecated ? "to be deprecated soon"
423 : "deprecated");
424 } else {
425 this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
426 }
427 }
Cary Clark8032b982017-07-28 11:04:54 -0400428 }
429 if (onePast) {
430 fIndent -= 4;
431 }
432 this->lfcr();
Cary Clark186d08f2018-04-03 08:43:27 -0400433 if (preprocessor.fStart) {
434 SkASSERT(preprocessor.fEnd);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400435 int saveIndent = fIndent;
436 fIndent = SkTMax(0, fIndent - 8);
437 this->lf(2);
Cary Clark186d08f2018-04-03 08:43:27 -0400438 this->writeBlock(
439 (int) (preprocessor.fEnd - preprocessor.fStart), preprocessor.fStart);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400440 this->lfcr();
441 fIndent = saveIndent;
Cary Clarka4f581a2018-04-03 15:31:59 -0400442 preprocessor.reset();
Cary Clarkbad5ad72017-08-03 17:14:08 -0400443 }
Cary Clark8032b982017-07-28 11:04:54 -0400444 if (token && State::kItemValue == state) {
445 fStart = token->fContentStart;
446 }
447 state = State::kNoItem;
448 }
449 SkASSERT(State::kNoItem == state);
450 if (onePast) {
451 break;
452 }
453 SkASSERT(token);
Cary Clark06c20f32018-03-20 15:53:27 -0400454 string itemName;
455 if (!fEnumDef->isRoot()) {
456 itemName = root->fName + "::";
457 if (KeyWord::kClass == child.fParent->fKeyWord) {
458 itemName += child.fParent->fName + "::";
459 }
Cary Clarkbad5ad72017-08-03 17:14:08 -0400460 }
461 itemName += string(token->fContentStart, (int) (token->fContentEnd - token->fContentStart));
Cary Clark8032b982017-07-28 11:04:54 -0400462 for (auto& enumItem : fEnumDef->fChildren) {
463 if (MarkType::kConst != enumItem->fMarkType) {
464 continue;
465 }
466 if (itemName != enumItem->fName) {
467 continue;
468 }
469 currentEnumItem = enumItem;
470 break;
471 }
472 SkASSERT(currentEnumItem);
473 // if description fits, it goes after item
474 commentStart = currentEnumItem->fContentStart;
475 const char* commentEnd;
476 if (currentEnumItem->fChildren.size() > 0) {
477 commentEnd = currentEnumItem->fChildren[0]->fStart;
478 } else {
479 commentEnd = currentEnumItem->fContentEnd;
480 }
481 TextParser enumComment(fFileName, commentStart, commentEnd, currentEnumItem->fLineCount);
Cary Clark4855f782018-02-06 09:41:53 -0500482 bool isDeprecated = false;
Cary Clark8032b982017-07-28 11:04:54 -0400483 if (enumComment.skipToLineStart()) { // skip const value
484 commentStart = enumComment.fChar;
485 commentLen = (int) (commentEnd - commentStart);
486 } else {
Cary Clark4855f782018-02-06 09:41:53 -0500487 const Definition* childDef = currentEnumItem->fChildren[0];
488 isDeprecated = MarkType::kDeprecated == childDef->fMarkType;
489 if (MarkType::kPrivate == childDef->fMarkType || isDeprecated) {
490 commentStart = childDef->fContentStart;
491 if (currentEnumItem->fToBeDeprecated) {
492 SkASSERT(isDeprecated);
493 commentStart += 4; // skip over "soon" // FIXME: this is awkward
494 }
495 commentLen = (int) (childDef->fContentEnd - commentStart);
496 }
Cary Clark8032b982017-07-28 11:04:54 -0400497 }
Cary Clark73fa9722017-08-29 17:36:51 -0400498 // FIXME: may assert here if there's no const value
499 // should have detected and errored on that earlier when enum fContentStart was set
Cary Clark4855f782018-02-06 09:41:53 -0500500 SkASSERT((commentLen > 0 && commentLen < 1000) || isDeprecated);
Cary Clark8032b982017-07-28 11:04:54 -0400501 if (!currentEnumItem->fShort) {
502 this->writeCommentHeader();
503 fIndent += 4;
Cary Clark4855f782018-02-06 09:41:53 -0500504 bool wroteLineFeed = false;
505 if (isDeprecated) {
506 this->writeString(currentEnumItem->fToBeDeprecated
507 ? "To be deprecated soon." : "Deprecated.");
508 }
Cary Clark186d08f2018-04-03 08:43:27 -0400509 TextParserSave save(this);
510 this->setForErrorReporting(currentEnumItem, commentStart);
Cary Clark4855f782018-02-06 09:41:53 -0500511 wroteLineFeed = Wrote::kLF ==
512 this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
Cary Clark186d08f2018-04-03 08:43:27 -0400513 save.restore();
Cary Clark8032b982017-07-28 11:04:54 -0400514 fIndent -= 4;
515 if (wroteLineFeed || fColumn > 100 - 3 /* space * / */ ) {
516 this->lfcr();
517 } else {
518 this->writeSpace();
519 }
520 this->writeCommentTrailer();
521 }
522 lastEnd = token->fContentEnd;
523 this->lfcr();
524 if (',' == fStart[0]) {
525 ++fStart;
526 }
527 this->writeBlock((int) (lastEnd - fStart), fStart); // enum item name
528 fStart = token->fContentEnd;
529 state = State::kItemName;
530 }
531}
532
Cary Clark186d08f2018-04-03 08:43:27 -0400533bool IncludeWriter::enumPreprocessor(Definition* token, MemberPass pass,
534 vector<IterState>& iterStack, IterState** iterState, Preprocessor* preprocessor) {
535 if (token && Definition::Type::kBracket == token->fType) {
536 if (Bracket::kSlashSlash == token->fBracket) {
537 if (MemberPass::kOut == pass) {
538 fStart = token->fContentEnd;
539 }
540 return true; // ignore old inline comments
541 }
542 if (Bracket::kSlashStar == token->fBracket) {
543 if (MemberPass::kOut == pass) {
544 fStart = token->fContentEnd + 1;
545 }
546 return true; // ignore old inline comments
547 }
548 if (Bracket::kPound == token->fBracket) { // preprocessor wraps member
Cary Clarka4f581a2018-04-03 15:31:59 -0400549 preprocessor->fDefinition = token;
Cary Clark186d08f2018-04-03 08:43:27 -0400550 preprocessor->fStart = token->fContentStart;
551 if (KeyWord::kIf == token->fKeyWord || KeyWord::kIfdef == token->fKeyWord) {
552 iterStack.emplace_back(token->fTokens.begin(), token->fTokens.end());
553 *iterState = &iterStack.back();
554 preprocessor->fWord = true;
555 } else if (KeyWord::kEndif == token->fKeyWord || KeyWord::kElif == token->fKeyWord
556 || KeyWord::kElse == token->fKeyWord) {
557 iterStack.pop_back();
558 *iterState = &iterStack.back();
559 preprocessor->fEnd = token->fContentEnd;
560 if (KeyWord::kElif == token->fKeyWord) {
561 iterStack.emplace_back(token->fTokens.begin(), token->fTokens.end());
562 *iterState = &iterStack.back();
563 preprocessor->fWord = true;
564 }
565 } else {
566 SkASSERT(0); // incomplete
567 }
568 return true;
569 }
Cary Clarka4f581a2018-04-03 15:31:59 -0400570 if (preprocessor->fDefinition) {
571 if (Bracket::kParen == token->fBracket) {
572 preprocessor->fEnd = token->fContentEnd;
573 SkASSERT(')' == *preprocessor->fEnd);
574 ++preprocessor->fEnd;
575 return true;
576 }
577 SkASSERT(0); // incomplete
578 }
Cary Clark186d08f2018-04-03 08:43:27 -0400579 return true;
580 }
581 if (token && Definition::Type::kWord != token->fType) {
582 SkASSERT(0); // incomplete
583 }
584 if (preprocessor->fWord) {
585 preprocessor->fWord = false;
586 preprocessor->fEnd = token->fContentEnd;
587 return true;
588 }
589 return false;
590}
591
Cary Clark8032b982017-07-28 11:04:54 -0400592void IncludeWriter::enumSizeItems(const Definition& child) {
593 enum class State {
594 kNoItem,
595 kItemName,
596 kItemValue,
597 kItemComment,
598 };
599 State state = State::kNoItem;
600 int longestName = 0;
601 int longestValue = 0;
602 int valueLen = 0;
603 const char* lastEnd = nullptr;
Cary Clark7cfcbca2018-01-04 16:11:51 -0500604// SkASSERT(child.fChildren.size() == 1 || child.fChildren.size() == 2);
Cary Clark8032b982017-07-28 11:04:54 -0400605 auto brace = child.fChildren[0];
Cary Clarkbad5ad72017-08-03 17:14:08 -0400606 if (KeyWord::kClass == brace->fKeyWord) {
607 brace = brace->fChildren[0];
608 }
Cary Clark8032b982017-07-28 11:04:54 -0400609 SkASSERT(Bracket::kBrace == brace->fBracket);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400610 vector<IterState> iterStack;
611 iterStack.emplace_back(brace->fTokens.begin(), brace->fTokens.end());
612 IterState* iterState = &iterStack[0];
Cary Clark186d08f2018-04-03 08:43:27 -0400613 Preprocessor preprocessor;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400614 while (iterState->fDefIter != iterState->fDefEnd) {
615 auto& token = *iterState->fDefIter++;
Cary Clark186d08f2018-04-03 08:43:27 -0400616 if (this->enumPreprocessor(&token, MemberPass::kCount, iterStack, &iterState,
617 &preprocessor)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -0400618 continue;
619 }
Cary Clark8032b982017-07-28 11:04:54 -0400620 if (State::kItemName == state) {
621 TextParser enumLine(token.fFileName, lastEnd,
622 token.fContentStart, token.fLineCount);
623 const char* end = enumLine.anyOf(",}=");
624 SkASSERT(end);
625 state = '=' == *end ? State::kItemValue : State::kItemComment;
626 if (State::kItemValue == state) {
627 valueLen = (int) (token.fContentEnd - token.fContentStart);
628 lastEnd = token.fContentEnd;
629 continue;
630 }
631 }
632 if (State::kItemValue == state) {
633 TextParser valueEnd(token.fFileName, lastEnd,
634 token.fContentStart, token.fLineCount);
635 const char* end = valueEnd.anyOf(",}");
636 if (!end) { // write expression continuation
Ben Wagner63fd7602017-10-09 15:45:33 -0400637 valueLen += (int) (token.fContentEnd - lastEnd);
Cary Clark8032b982017-07-28 11:04:54 -0400638 continue;
639 }
640 }
641 if (State::kNoItem != state) {
642 longestValue = SkTMax(longestValue, valueLen);
643 state = State::kNoItem;
644 }
645 SkASSERT(State::kNoItem == state);
646 lastEnd = token.fContentEnd;
647 longestName = SkTMax(longestName, (int) (lastEnd - token.fContentStart));
648 state = State::kItemName;
649 }
650 if (State::kItemValue == state) {
651 longestValue = SkTMax(longestValue, valueLen);
652 }
653 fEnumItemValueTab = longestName + fIndent + 1 /* space before = */ ;
654 if (longestValue) {
655 longestValue += 3; /* = space , */
656 }
657 fEnumItemCommentTab = fEnumItemValueTab + longestValue + 1 /* space before //!< */ ;
658 // iterate through bmh children and see which comments fit on include lines
659 for (auto& enumItem : fEnumDef->fChildren) {
660 if (MarkType::kConst != enumItem->fMarkType) {
661 continue;
662 }
663 TextParser enumLine(enumItem);
664 enumLine.trimEnd();
665 enumLine.skipToLineStart(); // skip const value
666 const char* commentStart = enumLine.fChar;
667 enumLine.skipLine();
668 ptrdiff_t lineLen = enumLine.fChar - commentStart + 5 /* //!< space */ ;
669 if (!enumLine.eof()) {
670 enumLine.skipWhiteSpace();
671 }
672 enumItem->fShort = enumLine.eof() && fEnumItemCommentTab + lineLen < 100;
673 }
674}
675
676// walk children and output complete method doxygen description
Cary Clark579985c2017-07-31 11:48:27 -0400677void IncludeWriter::methodOut(const Definition* method, const Definition& child) {
Cary Clark154beea2017-10-26 07:58:48 -0400678 if (fPendingMethod) {
679 fIndent -= 4;
680 fPendingMethod = false;
681 }
Cary Clark579985c2017-07-31 11:48:27 -0400682 fBmhMethod = method;
683 fMethodDef = &child;
Cary Clark8032b982017-07-28 11:04:54 -0400684 fContinuation = nullptr;
685 fDeferComment = nullptr;
Cary Clarka4f581a2018-04-03 15:31:59 -0400686 const Definition* csParent = method->csParent();
Cary Clarka4f581a2018-04-03 15:31:59 -0400687 if (csParent && (0 == fIndent || fIndentNext)) {
Cary Clark154beea2017-10-26 07:58:48 -0400688 fIndent += 4;
689 fIndentNext = false;
Cary Clark8032b982017-07-28 11:04:54 -0400690 }
691 this->writeCommentHeader();
692 fIndent += 4;
Cary Clark1a8d7622018-03-05 13:26:16 -0500693 this->descriptionOut(method, SkipFirstLine::kNo, Phrase::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400694 // compute indention column
695 size_t column = 0;
696 bool hasParmReturn = false;
697 for (auto methodPart : method->fChildren) {
698 if (MarkType::kParam == methodPart->fMarkType) {
699 column = SkTMax(column, methodPart->fName.length());
700 hasParmReturn = true;
701 } else if (MarkType::kReturn == methodPart->fMarkType) {
702 hasParmReturn = true;
703 }
704 }
705 if (hasParmReturn) {
706 this->lf(2);
707 column += fIndent + sizeof("@return ");
708 int saveIndent = fIndent;
709 for (auto methodPart : method->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -0400710 if (MarkType::kParam == methodPart->fMarkType) {
711 this->writeString("@param");
712 this->writeSpace();
713 this->writeString(methodPart->fName.c_str());
714 } else if (MarkType::kReturn == methodPart->fMarkType) {
715 this->writeString("@return");
716 } else {
717 continue;
718 }
Cary Clark1a8d7622018-03-05 13:26:16 -0500719 this->indentToColumn(column);
720 fIndent = column;
Cary Clark1a8d7622018-03-05 13:26:16 -0500721 this->descriptionOut(methodPart, SkipFirstLine::kNo, Phrase::kYes);
Cary Clark8032b982017-07-28 11:04:54 -0400722 fIndent = saveIndent;
723 this->lfcr();
724 }
725 } else {
726 this->lfcr();
727 }
728 fIndent -= 4;
729 this->lfcr();
730 this->writeCommentTrailer();
Cary Clark579985c2017-07-31 11:48:27 -0400731 fBmhMethod = nullptr;
732 fMethodDef = nullptr;
Cary Clark78c110e2018-02-09 16:49:09 -0500733 fEnumDef = nullptr;
Cary Clarkd0530ba2017-09-14 11:25:39 -0400734 fWroteMethod = true;
Cary Clark8032b982017-07-28 11:04:54 -0400735}
736
737void IncludeWriter::structOut(const Definition* root, const Definition& child,
738 const char* commentStart, const char* commentEnd) {
739 this->writeCommentHeader();
740 this->writeString("\\");
741 SkASSERT(MarkType::kClass == child.fMarkType || MarkType::kStruct == child.fMarkType);
742 this->writeString(MarkType::kClass == child.fMarkType ? "class" : "struct");
743 this->writeSpace();
744 this->writeString(child.fName.c_str());
745 fIndent += 4;
746 this->lfcr();
Cary Clarkc68ba1d2018-02-20 10:35:29 -0500747 if (child.fDeprecated) {
748 this->writeString(child.fToBeDeprecated ? "to be deprecated soon" : "deprecated");
749 } else {
750 this->rewriteBlock((int)(commentEnd - commentStart), commentStart, Phrase::kNo);
751 }
Cary Clark8032b982017-07-28 11:04:54 -0400752 fIndent -= 4;
753 this->lfcr();
754 this->writeCommentTrailer();
755}
756
Cary Clark186d08f2018-04-03 08:43:27 -0400757bool IncludeWriter::findEnumSubtopic(string undername, const Definition** rootDefPtr) const {
758 const Definition* subtopic = fEnumDef->fParent;
759 string subcheck = subtopic->fFiddle + '_' + undername;
760 auto iter = fBmhParser->fTopicMap.find(subcheck);
761 if (iter == fBmhParser->fTopicMap.end()) {
762 return false;
763 }
764 *rootDefPtr = iter->second;
765 return true;
766}
767
Cary Clark2dc84ad2018-01-26 12:56:22 -0500768Definition* IncludeWriter::findMemberCommentBlock(const vector<Definition*>& bmhChildren,
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400769 string name) const {
Cary Clarkc68ba1d2018-02-20 10:35:29 -0500770 for (auto memberDef : bmhChildren) {
771 if (MarkType::kMember != memberDef->fMarkType) {
772 continue;
773 }
774 string match = memberDef->fName;
775 // if match.endsWith(name) ...
Cary Clark300cc5b2018-02-20 12:50:35 -0500776 if (match.length() >= name.length() &&
Cary Clarkc68ba1d2018-02-20 10:35:29 -0500777 0 == match.compare(match.length() - name.length(), name.length(), name)) {
778 return memberDef;
779 }
780 }
781 for (auto memberDef : bmhChildren) {
782 if (MarkType::kSubtopic != memberDef->fMarkType && MarkType::kTopic != memberDef->fMarkType) {
783 continue;
784 }
785 Definition* result = this->findMemberCommentBlock(memberDef->fChildren, name);
786 if (result) {
787 return result;
788 }
789 }
790 return nullptr;
Cary Clark2dc84ad2018-01-26 12:56:22 -0500791}
792
Cary Clarkbad5ad72017-08-03 17:14:08 -0400793Definition* IncludeWriter::structMemberOut(const Definition* memberStart, const Definition& child) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400794 const char* blockStart = !fWroteMethod && fDeferComment ? fLastComment->fContentEnd : fStart;
795 const char* blockEnd = fWroteMethod && fDeferComment ? fDeferComment->fStart - 1 :
796 memberStart->fStart;
797 this->writeBlockTrim((int) (blockEnd - blockStart), blockStart);
Cary Clark154beea2017-10-26 07:58:48 -0400798 if (fIndentNext) {
799 fIndent += 4;
800 fIndentNext = false;
801 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400802 fWroteMethod = false;
Cary Clark8032b982017-07-28 11:04:54 -0400803 string name(child.fContentStart, (int) (child.fContentEnd - child.fContentStart));
Cary Clark2dc84ad2018-01-26 12:56:22 -0500804 Definition* commentBlock = this->findMemberCommentBlock(fBmhStructDef->fChildren, name);
Cary Clarkc68ba1d2018-02-20 10:35:29 -0500805 if (!commentBlock) {
806 return memberStart->reportError<Definition*>("member missing comment block");
807 }
808 if (!commentBlock->fShort) {
809 const char* commentStart = commentBlock->fContentStart;
810 ptrdiff_t commentLen = commentBlock->fContentEnd - commentStart;
Cary Clark8032b982017-07-28 11:04:54 -0400811 this->writeCommentHeader();
Cary Clarkbad5ad72017-08-03 17:14:08 -0400812 bool wroteLineFeed = false;
Cary Clark8032b982017-07-28 11:04:54 -0400813 fIndent += 4;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400814 for (auto child : commentBlock->fChildren) {
815 commentLen = child->fStart - commentStart;
Cary Clarkd0530ba2017-09-14 11:25:39 -0400816 wroteLineFeed |= Wrote::kLF == this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400817 if (MarkType::kFormula == child->fMarkType) {
818 this->writeSpace();
819 this->writeBlock((int) (child->fContentEnd - child->fContentStart),
Ben Wagner63fd7602017-10-09 15:45:33 -0400820 child->fContentStart);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400821 }
822 commentStart = child->fTerminator;
823 }
824 commentLen = commentBlock->fContentEnd - commentStart;
Cary Clarkd0530ba2017-09-14 11:25:39 -0400825 wroteLineFeed |= Wrote::kLF == this->rewriteBlock(commentLen, commentStart, Phrase::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400826 fIndent -= 4;
827 if (wroteLineFeed || fColumn > 100 - 3 /* space * / */ ) {
828 this->lfcr();
829 } else {
830 this->writeSpace();
831 }
832 this->writeCommentTrailer();
833 }
834 this->lfcr();
Cary Clarkbad5ad72017-08-03 17:14:08 -0400835 this->writeBlock((int) (child.fStart - memberStart->fContentStart),
Cary Clark8032b982017-07-28 11:04:54 -0400836 memberStart->fContentStart);
837 this->indentToColumn(fStructMemberTab);
838 this->writeString(name.c_str());
Cary Clark6fc50412017-09-21 12:31:06 -0400839 auto tokenIter = child.fParent->fTokens.begin();
Cary Clarkbad5ad72017-08-03 17:14:08 -0400840 std::advance(tokenIter, child.fParentIndex + 1);
841 Definition* valueStart = &*tokenIter;
842 while (Definition::Type::kPunctuation != tokenIter->fType) {
843 std::advance(tokenIter, 1);
844 SkASSERT(child.fParent->fTokens.end() != tokenIter);
845 }
846 Definition* valueEnd = &*tokenIter;
847 if (valueStart != valueEnd) {
848 this->indentToColumn(fStructValueTab);
849 this->writeString("=");
850 this->writeSpace();
851 this->writeBlock((int) (valueEnd->fStart - valueStart->fContentStart),
Ben Wagner63fd7602017-10-09 15:45:33 -0400852 valueStart->fContentStart);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400853 }
Cary Clark8032b982017-07-28 11:04:54 -0400854 this->writeString(";");
Cary Clarkc68ba1d2018-02-20 10:35:29 -0500855 if (commentBlock->fShort) {
Cary Clark8032b982017-07-28 11:04:54 -0400856 this->indentToColumn(fStructCommentTab);
857 this->writeString("//!<");
858 this->writeSpace();
Cary Clark1a8d7622018-03-05 13:26:16 -0500859 string extract = fBmhParser->extractText(commentBlock, BmhParser::TrimExtract::kYes);
Cary Clarkc68ba1d2018-02-20 10:35:29 -0500860 this->rewriteBlock(extract.length(), &extract.front(), Phrase::kNo);
Cary Clark8032b982017-07-28 11:04:54 -0400861 }
Cary Clark154beea2017-10-26 07:58:48 -0400862 this->lf(2);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400863 return valueEnd;
Cary Clark8032b982017-07-28 11:04:54 -0400864}
865
Cary Clarkc68ba1d2018-02-20 10:35:29 -0500866// iterate through bmh children and see which comments fit on include lines
867void IncludeWriter::structSetMembersShort(const vector<Definition*>& bmhChildren) {
868 for (auto memberDef : bmhChildren) {
869 if (MarkType::kMember != memberDef->fMarkType) {
870 continue;
871 }
Cary Clark1a8d7622018-03-05 13:26:16 -0500872 string extract = fBmhParser->extractText(memberDef, BmhParser::TrimExtract::kYes);
Cary Clarkc68ba1d2018-02-20 10:35:29 -0500873 bool multiline = string::npos != extract.find('\n');
874 if (multiline) {
875 memberDef->fShort = false;
876 } else {
877 ptrdiff_t lineLen = extract.length() + 5 /* //!< space */ ;
878 memberDef->fShort = fStructCommentTab + lineLen < 100;
879 }
880 }
881 for (auto memberDef : bmhChildren) {
882 if (MarkType::kSubtopic != memberDef->fMarkType && MarkType::kTopic != memberDef->fMarkType) {
883 continue;
884 }
885 this->structSetMembersShort(memberDef->fChildren);
886 }
887}
888
Cary Clark884dd7d2017-10-11 10:37:52 -0400889void IncludeWriter::structSizeMembers(const Definition& child) {
Cary Clark8032b982017-07-28 11:04:54 -0400890 int longestType = 0;
891 Definition* typeStart = nullptr;
892 int longestName = 0;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400893 int longestValue = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400894 SkASSERT(child.fChildren.size() == 1 || child.fChildren.size() == 2);
895 bool inEnum = false;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400896 bool inMethod = false;
897 bool inMember = false;
Cary Clark8032b982017-07-28 11:04:54 -0400898 auto brace = child.fChildren[0];
899 SkASSERT(Bracket::kBrace == brace->fBracket);
900 for (auto& token : brace->fTokens) {
901 if (Definition::Type::kBracket == token.fType) {
902 if (Bracket::kSlashSlash == token.fBracket) {
903 continue; // ignore old inline comments
904 }
905 if (Bracket::kSlashStar == token.fBracket) {
906 continue; // ignore old inline comments
907 }
908 if (Bracket::kParen == token.fBracket) {
Cary Clarkbad5ad72017-08-03 17:14:08 -0400909 if (inMethod) {
910 continue;
911 }
Cary Clark8032b982017-07-28 11:04:54 -0400912 break;
913 }
914 SkASSERT(0); // incomplete
915 }
916 if (Definition::Type::kKeyWord == token.fType) {
917 switch (token.fKeyWord) {
918 case KeyWord::kEnum:
919 inEnum = true;
920 break;
921 case KeyWord::kConst:
922 case KeyWord::kConstExpr:
923 case KeyWord::kStatic:
924 case KeyWord::kInt:
Cary Clarkd0530ba2017-09-14 11:25:39 -0400925 case KeyWord::kUint8_t:
926 case KeyWord::kUint16_t:
Cary Clark8032b982017-07-28 11:04:54 -0400927 case KeyWord::kUint32_t:
Cary Clarkd0530ba2017-09-14 11:25:39 -0400928 case KeyWord::kUint64_t:
Cary Clark8032b982017-07-28 11:04:54 -0400929 case KeyWord::kSize_t:
930 case KeyWord::kFloat:
931 case KeyWord::kBool:
932 case KeyWord::kVoid:
933 if (!typeStart) {
934 typeStart = &token;
935 }
936 break;
937 default:
938 break;
939 }
940 continue;
941 }
942 if (Definition::Type::kPunctuation == token.fType) {
943 if (inEnum) {
944 SkASSERT(Punctuation::kSemicolon == token.fPunctuation);
945 inEnum = false;
946 }
Cary Clarkbad5ad72017-08-03 17:14:08 -0400947 if (inMethod) {
948 if (Punctuation::kColon == token.fPunctuation) {
949 inMethod = false;
950 } else if (Punctuation::kLeftBrace == token.fPunctuation) {
951 inMethod = false;
Cary Clark73fa9722017-08-29 17:36:51 -0400952 } else if (Punctuation::kSemicolon == token.fPunctuation) {
953 inMethod = false;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400954 } else {
955 SkASSERT(0); // incomplete
956 }
957 }
958 if (inMember) {
959 SkASSERT(Punctuation::kSemicolon == token.fPunctuation);
960 typeStart = nullptr;
961 inMember = false;
962 }
Cary Clark8032b982017-07-28 11:04:54 -0400963 continue;
964 }
965 if (Definition::Type::kWord != token.fType) {
966 SkASSERT(0); // incomplete
967 }
968 if (MarkType::kMember == token.fMarkType) {
969 TextParser typeStr(token.fFileName, typeStart->fContentStart, token.fContentStart,
970 token.fLineCount);
971 typeStr.trimEnd();
Cary Clarkbad5ad72017-08-03 17:14:08 -0400972 longestType = SkTMax(longestType, (int) (typeStr.fEnd - typeStr.fStart));
Cary Clark8032b982017-07-28 11:04:54 -0400973 longestName = SkTMax(longestName, (int) (token.fContentEnd - token.fContentStart));
974 typeStart->fMemberStart = true;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400975 inMember = true;
976 continue;
977 }
978 if (MarkType::kMethod == token.fMarkType) {
979 inMethod = true;
Cary Clark8032b982017-07-28 11:04:54 -0400980 continue;
981 }
982 SkASSERT(MarkType::kNone == token.fMarkType);
Cary Clarkbad5ad72017-08-03 17:14:08 -0400983 if (typeStart) {
984 if (inMember) {
985 longestValue =
986 SkTMax(longestValue, (int) (token.fContentEnd - token.fContentStart));
987 }
988 } else {
Cary Clark8032b982017-07-28 11:04:54 -0400989 typeStart = &token;
990 }
991 }
992 fStructMemberTab = longestType + fIndent + 1 /* space before name */ ;
Cary Clarkbad5ad72017-08-03 17:14:08 -0400993 fStructValueTab = fStructMemberTab + longestName + 2 /* space ; */ ;
994 fStructCommentTab = fStructValueTab;
995 if (longestValue) {
996 fStructCommentTab += longestValue + 3 /* space = space */ ;
997 fStructValueTab -= 1 /* ; */ ;
998 }
Cary Clark8032b982017-07-28 11:04:54 -0400999 // iterate through bmh children and see which comments fit on include lines
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001000 this->structSetMembersShort(fBmhStructDef->fChildren);
Cary Clark8032b982017-07-28 11:04:54 -04001001}
1002
Cary Clark154beea2017-10-26 07:58:48 -04001003static bool find_start(const Definition* startDef, const char* start) {
1004 for (const auto& child : startDef->fTokens) {
1005 if (child.fContentStart == start) {
1006 return MarkType::kMethod == child.fMarkType;
1007 }
1008 if (child.fContentStart >= start) {
1009 break;
1010 }
1011 if (find_start(&child, start)) {
1012 return true;
1013 }
1014 }
1015 return false;
1016}
1017
Cary Clark73fa9722017-08-29 17:36:51 -04001018bool IncludeWriter::populate(Definition* def, ParentPair* prevPair, RootDefinition* root) {
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001019 if (!def->fTokens.size()) {
1020 return true;
1021 }
Cary Clark73fa9722017-08-29 17:36:51 -04001022 ParentPair pair = { def, prevPair };
Cary Clark8032b982017-07-28 11:04:54 -04001023 // write bulk of original include up to class, method, enum, etc., excepting preceding comment
1024 // find associated bmh object
1025 // write any associated comments in Doxygen form
1026 // skip include comment
1027 // if there is a series of same named methods, write one set of comments, then write all methods
1028 string methodName;
Kevin Lubick42846132018-01-05 10:11:11 -05001029 const Definition* method = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04001030 const Definition* clonedMethod = nullptr;
1031 const Definition* memberStart = nullptr;
Cary Clarkbad5ad72017-08-03 17:14:08 -04001032 const Definition* memberEnd = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04001033 fContinuation = nullptr;
1034 bool inStruct = false;
Cary Clarkbad5ad72017-08-03 17:14:08 -04001035 bool inConstructor = false;
Cary Clark884dd7d2017-10-11 10:37:52 -04001036 bool inInline = false;
Cary Clark154beea2017-10-26 07:58:48 -04001037 bool eatOperator = false;
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001038 bool sawConst = false;
1039 bool staticOnly = false;
Cary Clark154beea2017-10-26 07:58:48 -04001040 const Definition* requireDense = nullptr;
1041 const Definition* startDef = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -04001042 for (auto& child : def->fTokens) {
Cary Clark154beea2017-10-26 07:58:48 -04001043 if (KeyWord::kOperator == child.fKeyWord && method &&
1044 Definition::MethodType::kOperator == method->fMethodType) {
1045 eatOperator = true;
1046 continue;
1047 }
1048 if (eatOperator) {
1049 if (Bracket::kSquare == child.fBracket || Bracket::kParen == child.fBracket) {
1050 continue;
1051 }
1052 eatOperator = false;
1053 fContinuation = nullptr;
1054 if (KeyWord::kConst == child.fKeyWord) {
1055 continue;
1056 }
1057 }
Cary Clarkbad5ad72017-08-03 17:14:08 -04001058 if (memberEnd) {
1059 if (memberEnd != &child) {
1060 continue;
1061 }
Cary Clark154beea2017-10-26 07:58:48 -04001062 startDef = &child;
Cary Clarkbad5ad72017-08-03 17:14:08 -04001063 fStart = child.fContentStart + 1;
1064 memberEnd = nullptr;
1065 }
Cary Clark8032b982017-07-28 11:04:54 -04001066 if (child.fPrivate) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001067 if (MarkType::kMethod == child.fMarkType) {
1068 inInline = true;
1069 }
Cary Clark8032b982017-07-28 11:04:54 -04001070 continue;
1071 }
Cary Clark884dd7d2017-10-11 10:37:52 -04001072 if (inInline) {
1073 if (Definition::Type::kKeyWord == child.fType) {
1074 SkASSERT(MarkType::kMethod != child.fMarkType);
1075 continue;
1076 }
1077 if (Definition::Type::kPunctuation == child.fType) {
1078 if (Punctuation::kLeftBrace == child.fPunctuation) {
1079 inInline = false;
1080 } else {
1081 SkASSERT(Punctuation::kAsterisk == child.fPunctuation);
1082 }
1083 continue;
1084 }
1085 if (Definition::Type::kWord == child.fType) {
1086 string name(child.fContentStart, child.fContentEnd - child.fContentStart);
1087 SkASSERT(string::npos != name.find("::"));
1088 continue;
1089 }
1090 if (Definition::Type::kBracket == child.fType) {
1091 SkASSERT(Bracket::kParen == child.fBracket);
1092 continue;
1093 }
1094 }
Cary Clark8032b982017-07-28 11:04:54 -04001095 if (fContinuation) {
1096 if (Definition::Type::kKeyWord == child.fType) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001097 if (KeyWord::kFriend == child.fKeyWord ||
Cary Clark73fa9722017-08-29 17:36:51 -04001098 KeyWord::kSK_API == child.fKeyWord) {
Cary Clark8032b982017-07-28 11:04:54 -04001099 continue;
1100 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001101 const IncludeKey& includeKey = kKeyWords[(int) child.fKeyWord];
1102 if (KeyProperty::kNumber == includeKey.fProperty) {
1103 continue;
1104 }
Cary Clark8032b982017-07-28 11:04:54 -04001105 }
Cary Clark3cd22cc2017-12-01 11:49:58 -05001106 if (Definition::Type::kBracket == child.fType) {
1107 if (Bracket::kAngle == child.fBracket) {
Cary Clark8032b982017-07-28 11:04:54 -04001108 continue;
1109 }
Cary Clark3cd22cc2017-12-01 11:49:58 -05001110 if (Bracket::kParen == child.fBracket) {
1111 if (!clonedMethod) {
1112 if (inConstructor) {
1113 fContinuation = child.fContentStart;
1114 }
1115 continue;
Cary Clark8032b982017-07-28 11:04:54 -04001116 }
Cary Clark3cd22cc2017-12-01 11:49:58 -05001117 int alternate = 1;
1118 ptrdiff_t childLen = child.fContentEnd - child.fContentStart;
1119 SkASSERT(')' == child.fContentStart[childLen]);
1120 ++childLen;
1121 do {
1122 TextParser params(clonedMethod->fFileName, clonedMethod->fStart,
1123 clonedMethod->fContentStart, clonedMethod->fLineCount);
1124 params.skipToEndBracket('(');
1125 if (params.startsWith(child.fContentStart, childLen)) {
1126 this->methodOut(clonedMethod, child);
1127 break;
1128 }
1129 ++alternate;
1130 string alternateMethod = methodName + '_' + to_string(alternate);
1131 clonedMethod = root->find(alternateMethod,
1132 RootDefinition::AllowParens::kNo);
1133 } while (clonedMethod);
1134 if (!clonedMethod) {
1135 return this->reportError<bool>("cloned method not found");
1136 }
1137 clonedMethod = nullptr;
1138 continue;
Cary Clark8032b982017-07-28 11:04:54 -04001139 }
Cary Clark8032b982017-07-28 11:04:54 -04001140 }
1141 if (Definition::Type::kWord == child.fType) {
1142 if (clonedMethod) {
1143 continue;
1144 }
1145 size_t len = (size_t) (child.fContentEnd - child.fContentStart);
1146 const char operatorStr[] = "operator";
1147 size_t operatorLen = sizeof(operatorStr) - 1;
1148 if (len >= operatorLen && !strncmp(child.fContentStart, operatorStr, operatorLen)) {
1149 fContinuation = child.fContentEnd;
1150 continue;
1151 }
1152 }
1153 if (Definition::Type::kPunctuation == child.fType &&
1154 (Punctuation::kSemicolon == child.fPunctuation ||
Cary Clark6fc50412017-09-21 12:31:06 -04001155 Punctuation::kLeftBrace == child.fPunctuation ||
1156 (Punctuation::kColon == child.fPunctuation && inConstructor))) {
Cary Clark8032b982017-07-28 11:04:54 -04001157 SkASSERT(fContinuation[0] == '(');
1158 const char* continueEnd = child.fContentStart;
1159 while (continueEnd > fContinuation && isspace(continueEnd[-1])) {
1160 --continueEnd;
1161 }
1162 methodName += string(fContinuation, continueEnd - fContinuation);
Cary Clarkce101242017-09-01 15:51:02 -04001163 method = root->find(methodName, RootDefinition::AllowParens::kNo);
Cary Clark8032b982017-07-28 11:04:54 -04001164 if (!method) {
Cary Clark56356312018-02-08 14:45:18 -05001165 if (fBmhStructDef && fBmhStructDef->fDeprecated) {
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001166 fContinuation = nullptr;
Cary Clark56356312018-02-08 14:45:18 -05001167 continue;
1168 }
Cary Clark06c20f32018-03-20 15:53:27 -04001169 return child.reportError<bool>("method not found");
Cary Clark8032b982017-07-28 11:04:54 -04001170 }
Cary Clark579985c2017-07-31 11:48:27 -04001171 this->methodOut(method, child);
Cary Clark8032b982017-07-28 11:04:54 -04001172 continue;
1173 }
Cary Clarkd0530ba2017-09-14 11:25:39 -04001174 if (Definition::Type::kPunctuation == child.fType &&
1175 Punctuation::kAsterisk == child.fPunctuation &&
1176 clonedMethod) {
1177 continue;
1178 }
Cary Clarkbad5ad72017-08-03 17:14:08 -04001179 if (inConstructor) {
1180 continue;
1181 }
Cary Clark6fc50412017-09-21 12:31:06 -04001182 method = root->find(methodName + "()", RootDefinition::AllowParens::kNo);
Cary Clarkbad5ad72017-08-03 17:14:08 -04001183 if (method && MarkType::kDefinedBy == method->fMarkType) {
Cary Clark8032b982017-07-28 11:04:54 -04001184 method = method->fParent;
1185 }
1186 if (method) {
Cary Clark6fc50412017-09-21 12:31:06 -04001187 if (method->fCloned) {
1188 clonedMethod = method;
1189 continue;
1190 }
Cary Clark579985c2017-07-31 11:48:27 -04001191 this->methodOut(method, child);
Cary Clark8032b982017-07-28 11:04:54 -04001192 continue;
Cary Clark56356312018-02-08 14:45:18 -05001193 } else if (fBmhStructDef && fBmhStructDef->fDeprecated) {
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001194 fContinuation = nullptr;
1195 continue;
Cary Clark8032b982017-07-28 11:04:54 -04001196 }
Cary Clark06c20f32018-03-20 15:53:27 -04001197 return child.reportError<bool>("method not found");
Cary Clark8032b982017-07-28 11:04:54 -04001198 }
1199 if (Bracket::kSlashSlash == child.fBracket || Bracket::kSlashStar == child.fBracket) {
1200 if (!fDeferComment) {
1201 fDeferComment = &child;
1202 }
Cary Clarkbad5ad72017-08-03 17:14:08 -04001203 fLastComment = &child;
Cary Clark8032b982017-07-28 11:04:54 -04001204 continue;
Ben Wagner63fd7602017-10-09 15:45:33 -04001205 }
Cary Clark8032b982017-07-28 11:04:54 -04001206 if (MarkType::kMethod == child.fMarkType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001207 if (this->isInternalName(child)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001208 continue;
1209 }
Cary Clark8032b982017-07-28 11:04:54 -04001210 const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 :
Cary Clark154beea2017-10-26 07:58:48 -04001211 fAttrDeprecated ? fAttrDeprecated->fContentStart - 1 :
Cary Clark8032b982017-07-28 11:04:54 -04001212 child.fContentStart;
Cary Clark7cfcbca2018-01-04 16:11:51 -05001213 if (Definition::Type::kBracket == def->fType && Bracket::kDebugCode == def->fBracket) {
1214 auto tokenIter = def->fParent->fTokens.begin();
1215 std::advance(tokenIter, def->fParentIndex - 1);
1216 Definition* prior = &*tokenIter;
1217 if (Definition::Type::kBracket == def->fType &&
1218 Bracket::kSlashStar == prior->fBracket) {
1219 bodyEnd = prior->fContentStart - 1;
1220 }
1221 }
Cary Clark8032b982017-07-28 11:04:54 -04001222 // FIXME: roll end-trimming into writeBlockTrim call
1223 while (fStart < bodyEnd && ' ' >= bodyEnd[-1]) {
1224 --bodyEnd;
1225 }
1226 int blockSize = (int) (bodyEnd - fStart);
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001227 SkASSERT(blockSize >= 0);
Cary Clark8032b982017-07-28 11:04:54 -04001228 if (blockSize) {
Cary Clark7cfcbca2018-01-04 16:11:51 -05001229 string debugstr(fStart, blockSize);
Cary Clark8032b982017-07-28 11:04:54 -04001230 this->writeBlock(blockSize, fStart);
1231 }
Cary Clark154beea2017-10-26 07:58:48 -04001232 startDef = &child;
Cary Clark8032b982017-07-28 11:04:54 -04001233 fStart = child.fContentStart;
Cary Clark06c20f32018-03-20 15:53:27 -04001234 auto mapFind = fBmhParser->fMethodMap.find(child.fName);
1235 if (fBmhParser->fMethodMap.end() != mapFind) {
1236 inConstructor = false;
1237 method = &mapFind->second;
1238 } else {
1239 methodName = root->fName + "::" + child.fName;
1240 inConstructor = root->fName == child.fName;
1241 method = root->find(methodName, RootDefinition::AllowParens::kNo);
1242 }
Cary Clark8032b982017-07-28 11:04:54 -04001243 fContinuation = child.fContentEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001244 if (!method) {
1245 continue;
1246 }
1247 if (method->fCloned) {
1248 clonedMethod = method;
1249 continue;
1250 }
Cary Clark579985c2017-07-31 11:48:27 -04001251 this->methodOut(method, child);
Cary Clark73fa9722017-08-29 17:36:51 -04001252 if (fAttrDeprecated) {
Cary Clark154beea2017-10-26 07:58:48 -04001253 startDef = fAttrDeprecated;
Cary Clark73fa9722017-08-29 17:36:51 -04001254 fStart = fAttrDeprecated->fContentStart;
1255 fAttrDeprecated = nullptr;
1256 }
Cary Clark8032b982017-07-28 11:04:54 -04001257 continue;
Ben Wagner63fd7602017-10-09 15:45:33 -04001258 }
Cary Clark8032b982017-07-28 11:04:54 -04001259 if (Definition::Type::kKeyWord == child.fType) {
Cary Clark154beea2017-10-26 07:58:48 -04001260 if (fIndentNext) {
Cary Clark154beea2017-10-26 07:58:48 -04001261 // too soon
1262#if 0 // makes struct Lattice indent when it oughtn't
1263 if (KeyWord::kEnum == child.fKeyWord) {
1264 fIndent += 4;
1265 }
1266 if (KeyWord::kPublic != child.fKeyWord) {
1267 fIndentNext = false;
1268 }
1269#endif
1270 }
Cary Clark8032b982017-07-28 11:04:54 -04001271 switch (child.fKeyWord) {
1272 case KeyWord::kStruct:
Cary Clark73fa9722017-08-29 17:36:51 -04001273 case KeyWord::kClass:
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001274 fStructMemberTab = 0;
Cary Clark8032b982017-07-28 11:04:54 -04001275 // if struct contains members, compute their name and comment tabs
Cary Clark73fa9722017-08-29 17:36:51 -04001276 if (child.fChildren.size() > 0) {
1277 const ParentPair* testPair = &pair;
1278 while ((testPair = testPair->fPrev)) {
1279 if (KeyWord::kClass == testPair->fParent->fKeyWord) {
1280 inStruct = fInStruct = true;
1281 break;
1282 }
1283 }
1284 }
Cary Clark8032b982017-07-28 11:04:54 -04001285 if (fInStruct) {
Cary Clark154beea2017-10-26 07:58:48 -04001286 // try child; root+child; root->parent+child; etc.
1287 int trial = 0;
1288 const RootDefinition* search = root;
1289 const Definition* parent = search->fParent;
1290 do {
1291 string name;
1292 if (0 == trial) {
1293 name = child.fName;
1294 } else if (1 == trial) {
1295 name = root->fName + "::" + child.fName;
1296 } else {
1297 SkASSERT(parent);
1298 name = parent->fName + "::" + child.fName;
1299 search = parent->asRoot();
1300 parent = search->fParent;
1301 }
1302 fBmhStructDef = search->find(name, RootDefinition::AllowParens::kNo);
1303 } while (!fBmhStructDef && ++trial);
1304 root = const_cast<RootDefinition*>(fBmhStructDef->asRoot());
1305 SkASSERT(root);
Cary Clark8032b982017-07-28 11:04:54 -04001306 fIndent += 4;
Cary Clark8032b982017-07-28 11:04:54 -04001307 this->structSizeMembers(child);
1308 fIndent -= 4;
Cary Clark154beea2017-10-26 07:58:48 -04001309 SkASSERT(!fIndentNext);
1310 fIndentNext = true;
Cary Clark8032b982017-07-28 11:04:54 -04001311 }
Cary Clark8032b982017-07-28 11:04:54 -04001312 if (child.fChildren.size() > 0) {
1313 const char* bodyEnd = fDeferComment ? fDeferComment->fContentStart - 1 :
1314 child.fContentStart;
Cary Clark73fa9722017-08-29 17:36:51 -04001315 this->writeBlockTrim((int) (bodyEnd - fStart), fStart);
Cary Clark154beea2017-10-26 07:58:48 -04001316 if (fPendingMethod) {
1317 fIndent -= 4;
1318 fPendingMethod = false;
1319 }
1320 startDef = requireDense ? requireDense : &child;
1321 fStart = requireDense ? requireDense->fContentStart : child.fContentStart;
1322 requireDense = nullptr;
1323 if (!fInStruct && child.fName != root->fName) {
1324 root = &fBmhParser->fClassMap[child.fName];
1325 fRootTopic = root->fParent;
1326 SkASSERT(!root->fVisited);
1327 root->clearVisited();
1328 fIndent = 0;
1329 fBmhStructDef = root;
1330 }
Cary Clark8032b982017-07-28 11:04:54 -04001331 if (child.fName == root->fName) {
1332 if (Definition* parent = root->fParent) {
1333 if (MarkType::kTopic == parent->fMarkType ||
1334 MarkType::kSubtopic == parent->fMarkType) {
Cary Clarke4aa3712017-09-15 02:56:12 -04001335 const char* commentStart = root->fContentStart;
1336 const char* commentEnd = root->fChildren[0]->fStart;
Cary Clark8032b982017-07-28 11:04:54 -04001337 this->structOut(root, *root, commentStart, commentEnd);
1338 } else {
1339 SkASSERT(0); // incomplete
1340 }
1341 } else {
1342 SkASSERT(0); // incomplete
1343 }
1344 } else {
Cary Clark154beea2017-10-26 07:58:48 -04001345 SkASSERT(fInStruct);
1346 #if 0
1347 fBmhStructDef = root->find(child.fName, RootDefinition::AllowParens::kNo);
1348 if (nullptr == fBmhStructDef) {
1349 fBmhStructDef = root->find(root->fName + "::" + child.fName,
Cary Clarkce101242017-09-01 15:51:02 -04001350 RootDefinition::AllowParens::kNo);
Cary Clark8032b982017-07-28 11:04:54 -04001351 }
Cary Clark154beea2017-10-26 07:58:48 -04001352 if (!fBmhStructDef) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001353 this->lf(2);
1354 fIndent = 0;
1355 this->writeBlock((int) (fStart - bodyEnd), bodyEnd);
1356 this->lfcr();
1357 continue;
1358 }
Cary Clark154beea2017-10-26 07:58:48 -04001359 #endif
Cary Clark8032b982017-07-28 11:04:54 -04001360 Definition* codeBlock = nullptr;
Cary Clark154beea2017-10-26 07:58:48 -04001361 Definition* nextBlock = nullptr;
1362 for (auto test : fBmhStructDef->fChildren) {
Cary Clark8032b982017-07-28 11:04:54 -04001363 if (MarkType::kCode == test->fMarkType) {
1364 SkASSERT(!codeBlock); // FIXME: check enum for correct order earlier
1365 codeBlock = test;
1366 continue;
1367 }
1368 if (codeBlock) {
Cary Clark154beea2017-10-26 07:58:48 -04001369 nextBlock = test;
Cary Clark8032b982017-07-28 11:04:54 -04001370 break;
1371 }
1372 }
Cary Clark73fa9722017-08-29 17:36:51 -04001373 // FIXME: trigger error earlier if inner #Struct or #Class is missing #Code
Cary Clark56356312018-02-08 14:45:18 -05001374 if (!fBmhStructDef->fDeprecated) {
1375 SkASSERT(codeBlock);
1376 SkASSERT(nextBlock); // FIXME: check enum for correct order earlier
Cary Clark78c110e2018-02-09 16:49:09 -05001377 const char* commentStart = codeBlock->fTerminator;
1378 const char* commentEnd = nextBlock->fStart;
1379 fIndentNext = true;
1380 this->structOut(root, *fBmhStructDef, commentStart, commentEnd);
Cary Clark56356312018-02-08 14:45:18 -05001381 }
Cary Clark8032b982017-07-28 11:04:54 -04001382 }
1383 fDeferComment = nullptr;
1384 } else {
1385 ; // empty forward reference, nothing to do here
1386 }
1387 break;
1388 case KeyWord::kEnum: {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001389 fInEnum = true;
Cary Clark8032b982017-07-28 11:04:54 -04001390 this->enumHeaderOut(root, child);
1391 this->enumSizeItems(child);
1392 } break;
1393 case KeyWord::kConst:
1394 case KeyWord::kConstExpr:
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001395 sawConst = !memberStart || staticOnly;
1396 if (!memberStart) {
1397 memberStart = &child;
1398 staticOnly = true;
1399 }
1400 break;
Cary Clark8032b982017-07-28 11:04:54 -04001401 case KeyWord::kStatic:
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001402 if (!memberStart) {
1403 memberStart = &child;
1404 staticOnly = true;
1405 }
1406 break;
Cary Clark8032b982017-07-28 11:04:54 -04001407 case KeyWord::kInt:
Cary Clarkd0530ba2017-09-14 11:25:39 -04001408 case KeyWord::kUint8_t:
1409 case KeyWord::kUint16_t:
Cary Clark8032b982017-07-28 11:04:54 -04001410 case KeyWord::kUint32_t:
Cary Clarkd0530ba2017-09-14 11:25:39 -04001411 case KeyWord::kUint64_t:
Cary Clark73fa9722017-08-29 17:36:51 -04001412 case KeyWord::kUnsigned:
Cary Clark8032b982017-07-28 11:04:54 -04001413 case KeyWord::kSize_t:
1414 case KeyWord::kFloat:
1415 case KeyWord::kBool:
Cary Clark56356312018-02-08 14:45:18 -05001416 case KeyWord::kChar:
Cary Clark8032b982017-07-28 11:04:54 -04001417 case KeyWord::kVoid:
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001418 staticOnly = false;
Cary Clark8032b982017-07-28 11:04:54 -04001419 if (!memberStart) {
1420 memberStart = &child;
1421 }
1422 break;
1423 case KeyWord::kPublic:
1424 case KeyWord::kPrivate:
1425 case KeyWord::kProtected:
1426 case KeyWord::kFriend:
Cary Clark73fa9722017-08-29 17:36:51 -04001427 case KeyWord::kInline:
1428 case KeyWord::kSK_API:
Cary Clarkbbfda252018-03-09 15:32:01 -05001429 case KeyWord::kTemplate:
Cary Clarkbad5ad72017-08-03 17:14:08 -04001430 case KeyWord::kTypedef:
Cary Clark8032b982017-07-28 11:04:54 -04001431 break;
Cary Clark154beea2017-10-26 07:58:48 -04001432 case KeyWord::kSK_BEGIN_REQUIRE_DENSE:
1433 requireDense = &child;
1434 break;
Cary Clark8032b982017-07-28 11:04:54 -04001435 default:
1436 SkASSERT(0);
1437 }
Cary Clarkd98f78c2018-04-26 08:32:37 -04001438 if (KeyWord::kUint8_t == child.fKeyWord || KeyWord::kUint32_t == child.fKeyWord) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001439 continue;
Cary Clark8032b982017-07-28 11:04:54 -04001440 } else {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001441 if (fInEnum && KeyWord::kClass == child.fChildren[0]->fKeyWord) {
Cary Clark73fa9722017-08-29 17:36:51 -04001442 if (!this->populate(child.fChildren[0], &pair, root)) {
Cary Clarkbad5ad72017-08-03 17:14:08 -04001443 return false;
1444 }
Cary Clark154beea2017-10-26 07:58:48 -04001445 } else {
1446 if (!this->populate(&child, &pair, root)) {
1447 return false;
1448 }
1449 if (KeyWord::kClass == child.fKeyWord || KeyWord::kStruct == child.fKeyWord) {
Cary Clark154beea2017-10-26 07:58:48 -04001450 if (fInStruct) {
1451 fInStruct = false;
1452 do {
1453 SkASSERT(root);
1454 root = const_cast<RootDefinition*>(root->fParent->asRoot());
1455 } while (MarkType::kTopic == root->fMarkType ||
1456 MarkType::kSubtopic == root->fMarkType);
1457 SkASSERT(MarkType::kStruct == root->fMarkType ||
1458 MarkType::kClass == root->fMarkType);
1459 fPendingMethod = false;
1460 if (startDef) {
1461 fPendingMethod = find_start(startDef, fStart);
1462 }
1463 fOutdentNext = !fPendingMethod;
1464 }
1465 }
Cary Clark8032b982017-07-28 11:04:54 -04001466 }
1467 }
1468 continue;
Ben Wagner63fd7602017-10-09 15:45:33 -04001469 }
Cary Clark8032b982017-07-28 11:04:54 -04001470 if (Definition::Type::kBracket == child.fType) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001471 if (KeyWord::kEnum == child.fParent->fKeyWord ||
Cary Clarkbad5ad72017-08-03 17:14:08 -04001472 (KeyWord::kClass == child.fParent->fKeyWord && child.fParent->fParent &&
1473 KeyWord::kEnum == child.fParent->fParent->fKeyWord)) {
Cary Clarkd0530ba2017-09-14 11:25:39 -04001474 SkASSERT(Bracket::kBrace == child.fBracket);
Cary Clark8032b982017-07-28 11:04:54 -04001475 this->enumMembersOut(root, child);
1476 this->writeString("};");
1477 this->lf(2);
Cary Clark154beea2017-10-26 07:58:48 -04001478 startDef = child.fParent;
Cary Clark8032b982017-07-28 11:04:54 -04001479 fStart = child.fParent->fContentEnd;
1480 SkASSERT(';' == fStart[0]);
1481 ++fStart;
1482 fDeferComment = nullptr;
1483 fInEnum = false;
Cary Clark154beea2017-10-26 07:58:48 -04001484 if (fIndentNext) {
1485// fIndent -= 4;
1486 fIndentNext = false;
1487 }
Cary Clark8032b982017-07-28 11:04:54 -04001488 continue;
Cary Clark73fa9722017-08-29 17:36:51 -04001489 }
1490 if (fAttrDeprecated) {
1491 continue;
1492 }
Cary Clark8032b982017-07-28 11:04:54 -04001493 fDeferComment = nullptr;
Cary Clark154beea2017-10-26 07:58:48 -04001494 if (KeyWord::kClass == def->fKeyWord || KeyWord::kStruct == def->fKeyWord) {
1495 fIndentNext = true;
1496 }
Cary Clark73fa9722017-08-29 17:36:51 -04001497 if (!this->populate(&child, &pair, root)) {
Cary Clark8032b982017-07-28 11:04:54 -04001498 return false;
1499 }
1500 continue;
1501 }
1502 if (Definition::Type::kWord == child.fType) {
1503 if (MarkType::kMember == child.fMarkType) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001504 if (!memberStart) {
1505 auto iter = def->fTokens.begin();
1506 std::advance(iter, child.fParentIndex - 1);
1507 memberStart = &*iter;
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001508 staticOnly = false;
Cary Clark154beea2017-10-26 07:58:48 -04001509 if (!fStructMemberTab) {
Cary Clark884dd7d2017-10-11 10:37:52 -04001510 SkASSERT(KeyWord::kStruct == def->fParent->fKeyWord);
Cary Clark154beea2017-10-26 07:58:48 -04001511 fIndent += 4;
1512 this->structSizeMembers(*def->fParent);
1513 fIndent -= 4;
1514// SkASSERT(!fIndentNext);
1515 fIndentNext = true;
Cary Clark884dd7d2017-10-11 10:37:52 -04001516 }
1517 }
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001518 SkASSERT(fBmhStructDef);
1519 if (!fBmhStructDef->fDeprecated) {
1520 memberEnd = this->structMemberOut(memberStart, child);
1521 startDef = &child;
1522 fStart = child.fContentEnd + 1;
1523 fDeferComment = nullptr;
1524 }
Cary Clark56356312018-02-08 14:45:18 -05001525 } else if (MarkType::kNone == child.fMarkType && sawConst
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001526 && fEnumDef && !fEnumDef->fDeprecated) {
1527 const Definition* bmhConst = nullptr;
1528 string match;
1529 if (root) {
1530 match = root->fName + "::";
1531 }
1532 match += string(child.fContentStart, child.fContentEnd - child.fContentStart);
1533 for (auto enumChild : fEnumDef->fChildren) {
1534 if (MarkType::kConst == enumChild->fMarkType && enumChild->fName == match) {
1535 bmhConst = enumChild;
1536 break;
1537 }
1538 }
1539 if (bmhConst) {
1540 this->constOut(memberStart, child, bmhConst);
1541 fDeferComment = nullptr;
1542 sawConst = false;
1543 }
1544 }
Cary Clark8032b982017-07-28 11:04:54 -04001545 if (child.fMemberStart) {
1546 memberStart = &child;
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001547 staticOnly = false;
Cary Clark8032b982017-07-28 11:04:54 -04001548 }
Cary Clark89b14562018-03-19 09:04:10 -04001549 if (kAttrDeprecatedLen == (size_t) (child.fContentEnd - child.fContentStart) &&
1550 !strncmp(gAttrDeprecated, child.fStart, kAttrDeprecatedLen)) {
Cary Clark73fa9722017-08-29 17:36:51 -04001551 fAttrDeprecated = &child;
1552 }
Cary Clark8032b982017-07-28 11:04:54 -04001553 continue;
1554 }
1555 if (Definition::Type::kPunctuation == child.fType) {
1556 if (Punctuation::kSemicolon == child.fPunctuation) {
1557 memberStart = nullptr;
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001558 sawConst = false;
1559 staticOnly = false;
Cary Clark8032b982017-07-28 11:04:54 -04001560 if (inStruct) {
1561 fInStruct = false;
1562 }
1563 continue;
1564 }
1565 if (Punctuation::kLeftBrace == child.fPunctuation ||
1566 Punctuation::kColon == child.fPunctuation ||
1567 Punctuation::kAsterisk == child.fPunctuation
1568 ) {
1569 continue;
1570 }
1571 }
1572 }
1573 return true;
1574}
1575
1576bool IncludeWriter::populate(BmhParser& bmhParser) {
1577 bool allPassed = true;
1578 for (auto& includeMapper : fIncludeMap) {
1579 size_t lastSlash = includeMapper.first.rfind('/');
Cary Clarkd0530ba2017-09-14 11:25:39 -04001580 if (string::npos == lastSlash) {
1581 lastSlash = includeMapper.first.rfind('\\');
1582 }
Cary Clark8032b982017-07-28 11:04:54 -04001583 if (string::npos == lastSlash || lastSlash >= includeMapper.first.length() - 1) {
1584 return this->reportError<bool>("malformed include name");
1585 }
1586 string fileName = includeMapper.first.substr(lastSlash + 1);
1587 if (".h" != fileName.substr(fileName.length() - 2)) {
1588 return this->reportError<bool>("expected fileName.h");
1589 }
1590 string skClassName = fileName.substr(0, fileName.length() - 2);
1591 fOut = fopen(fileName.c_str(), "wb");
1592 if (!fOut) {
1593 SkDebugf("could not open output file %s\n", fileName.c_str());
1594 return false;
1595 }
1596 if (bmhParser.fClassMap.end() == bmhParser.fClassMap.find(skClassName)) {
1597 return this->reportError<bool>("could not find bmh class");
1598 }
1599 fBmhParser = &bmhParser;
1600 RootDefinition* root = &bmhParser.fClassMap[skClassName];
1601 fRootTopic = root->fParent;
1602 root->clearVisited();
Cary Clark2d4bf5f2018-04-16 08:37:38 -04001603 fFileName = includeMapper.second.fFileName;
Cary Clark8032b982017-07-28 11:04:54 -04001604 fStart = includeMapper.second.fContentStart;
1605 fEnd = includeMapper.second.fContentEnd;
Cary Clark2f466242017-12-11 16:03:17 -05001606 fAnonymousEnumCount = 1;
Cary Clark73fa9722017-08-29 17:36:51 -04001607 allPassed &= this->populate(&includeMapper.second, nullptr, root);
Cary Clark8032b982017-07-28 11:04:54 -04001608 this->writeBlock((int) (fEnd - fStart), fStart);
1609 fIndent = 0;
1610 this->lfcr();
1611 this->writePending();
1612 fclose(fOut);
Cary Clark7cfcbca2018-01-04 16:11:51 -05001613 fflush(fOut);
1614 size_t slash = fFileName.find_last_of('/');
1615 if (string::npos == slash) {
1616 slash = 0;
1617 }
1618 size_t back = fFileName.find_last_of('\\');
1619 if (string::npos == back) {
1620 back = 0;
1621 }
1622 string dir = fFileName.substr(0, SkTMax(slash, back) + 1);
1623 string readname = dir + fileName;
1624 if (this->writtenFileDiffers(fileName, readname)) {
1625 SkDebugf("wrote updated %s\n", fileName.c_str());
1626 } else {
1627 remove(fileName.c_str());
1628 }
Cary Clark8032b982017-07-28 11:04:54 -04001629 }
1630 return allPassed;
1631}
1632
1633// change Xxx_Xxx to xxx xxx
1634static string ConvertRef(const string str, bool first) {
1635 string substitute;
1636 for (char c : str) {
1637 if ('_' == c) {
1638 c = ' '; // change Xxx_Xxx to xxx xxx
1639 } else if (isupper(c) && !first) {
1640 c = tolower(c);
1641 }
1642 substitute += c;
1643 first = false;
1644 }
1645 return substitute;
1646}
1647
Cary Clark8032b982017-07-28 11:04:54 -04001648string IncludeWriter::resolveMethod(const char* start, const char* end, bool first) {
1649 string methodname(start, end - start);
Cary Clark579985c2017-07-31 11:48:27 -04001650 if (string::npos != methodname.find("()")) {
1651 return "";
1652 }
Cary Clark8032b982017-07-28 11:04:54 -04001653 string substitute;
1654 auto rootDefIter = fBmhParser->fMethodMap.find(methodname);
1655 if (fBmhParser->fMethodMap.end() != rootDefIter) {
1656 substitute = methodname + "()";
1657 } else {
Cary Clark1eace2d2017-07-31 07:52:43 -04001658 RootDefinition* parent = nullptr;
1659 for (auto candidate : fRootTopic->fChildren) {
1660 if (MarkType::kClass == candidate->fMarkType
1661 || MarkType::kStruct == candidate->fMarkType) {
1662 parent = candidate->asRoot();
1663 break;
1664 }
1665 }
1666 SkASSERT(parent);
Cary Clarkce101242017-09-01 15:51:02 -04001667 auto defRef = parent->find(parent->fName + "::" + methodname,
1668 RootDefinition::AllowParens::kNo);
Cary Clark8032b982017-07-28 11:04:54 -04001669 if (defRef && MarkType::kMethod == defRef->fMarkType) {
1670 substitute = methodname + "()";
1671 }
1672 }
Cary Clark579985c2017-07-31 11:48:27 -04001673 if (fMethodDef && methodname == fMethodDef->fName) {
1674 TextParser report(fBmhMethod);
1675 report.reportError("method should not include references to itself");
1676 return "";
1677 }
Cary Clark6fc50412017-09-21 12:31:06 -04001678 if (fBmhMethod) {
1679 for (auto child : fBmhMethod->fChildren) {
1680 if (MarkType::kParam != child->fMarkType) {
1681 continue;
1682 }
1683 if (methodname == child->fName) {
1684 return "";
1685 }
1686 }
1687 }
Cary Clark8032b982017-07-28 11:04:54 -04001688 return substitute;
1689}
1690
Cary Clarkce101242017-09-01 15:51:02 -04001691string IncludeWriter::resolveRef(const char* start, const char* end, bool first,
1692 RefType* refType) {
Ben Wagner63fd7602017-10-09 15:45:33 -04001693 // look up Xxx_Xxx
Cary Clark8032b982017-07-28 11:04:54 -04001694 string undername(start, end - start);
Cary Clarkce101242017-09-01 15:51:02 -04001695 for (const auto& external : fBmhParser->fExternals) {
1696 if (external.fName == undername) {
1697 *refType = RefType::kExternal;
1698 return external.fName;
1699 }
1700 }
1701 *refType = RefType::kNormal;
Cary Clark8032b982017-07-28 11:04:54 -04001702 SkASSERT(string::npos == undername.find(' '));
1703 const Definition* rootDef = nullptr;
Cary Clark78c110e2018-02-09 16:49:09 -05001704 string substitute;
Cary Clark8032b982017-07-28 11:04:54 -04001705 {
1706 auto rootDefIter = fBmhParser->fTopicMap.find(undername);
1707 if (fBmhParser->fTopicMap.end() != rootDefIter) {
1708 rootDef = rootDefIter->second;
1709 } else {
1710 string prefixedName = fRootTopic->fName + '_' + undername;
1711 rootDefIter = fBmhParser->fTopicMap.find(prefixedName);
1712 if (fBmhParser->fTopicMap.end() != rootDefIter) {
1713 rootDef = rootDefIter->second;
Cary Clark154beea2017-10-26 07:58:48 -04001714 } else if (fBmhStructDef) {
1715 string localPrefix = fBmhStructDef->fFiddle + '_' + undername;
Cary Clarkbad5ad72017-08-03 17:14:08 -04001716 rootDefIter = fBmhParser->fTopicMap.find(localPrefix);
1717 if (fBmhParser->fTopicMap.end() != rootDefIter) {
1718 rootDef = rootDefIter->second;
1719 }
Cary Clark78c110e2018-02-09 16:49:09 -05001720 if (!rootDef) {
1721 size_t doubleColon = fBmhStructDef->fName.rfind("::");
1722 if (string::npos != doubleColon && undername
1723 == fBmhStructDef->fName.substr(doubleColon + 2)) {
1724 substitute = fBmhStructDef->fName;
1725 }
1726 }
1727 }
1728 if (!rootDef && !substitute.length()) {
Cary Clark8032b982017-07-28 11:04:54 -04001729 auto aliasIter = fBmhParser->fAliasMap.find(undername);
1730 if (fBmhParser->fAliasMap.end() != aliasIter) {
Cary Clark78c110e2018-02-09 16:49:09 -05001731 rootDef = aliasIter->second;
Cary Clark186d08f2018-04-03 08:43:27 -04001732 } else if (fInEnum && fEnumDef && this->findEnumSubtopic(undername, &rootDef)) {
1733 ;
Cary Clark8032b982017-07-28 11:04:54 -04001734 } else if (!first) {
Cary Clark186d08f2018-04-03 08:43:27 -04001735 this->fChar = start;
Cary Clark579985c2017-07-31 11:48:27 -04001736 this->reportError("reference unfound");
1737 return "";
Cary Clark8032b982017-07-28 11:04:54 -04001738 }
1739 }
1740 }
1741 }
Cary Clark8032b982017-07-28 11:04:54 -04001742 if (rootDef) {
Cary Clark78c110e2018-02-09 16:49:09 -05001743 MarkType rootType = rootDef->fMarkType;
1744 bool isTopic = MarkType::kSubtopic == rootType || MarkType::kTopic == rootType;
1745 auto substituteParent = MarkType::kAlias == rootType ? rootDef->fParent :
1746 isTopic ? rootDef : nullptr;
1747 if (substituteParent) {
1748 for (auto child : substituteParent->fChildren) {
1749 if (MarkType::kSubstitute == child->fMarkType) {
1750 substitute = string(child->fContentStart,
1751 (int) (child->fContentEnd - child->fContentStart));
Cary Clarkd0530ba2017-09-14 11:25:39 -04001752 break;
Cary Clark8032b982017-07-28 11:04:54 -04001753 }
Cary Clark8032b982017-07-28 11:04:54 -04001754 }
1755 }
1756 if (!substitute.length()) {
Cary Clark78c110e2018-02-09 16:49:09 -05001757 string match = rootDef->fName;
1758 size_t index;
1759 while (string::npos != (index = match.find('_'))) {
1760 match.erase(index, 1);
1761 }
1762 string skmatch = "Sk" + match;
1763 auto parent = substituteParent ? substituteParent : rootDef;
1764 for (auto child : parent->fChildren) {
1765 // there may be more than one
1766 // prefer the one mostly closely matching in text
1767 if ((MarkType::kClass == child->fMarkType ||
1768 MarkType::kStruct == child->fMarkType ||
1769 (MarkType::kEnum == child->fMarkType && !child->fAnonymous) ||
1770 MarkType::kEnumClass == child->fMarkType) && (match == child->fName ||
1771 skmatch == child->fName)) {
1772 substitute = child->fName;
1773 break;
1774 }
1775 }
1776 }
1777 if (!substitute.length()) {
1778 for (auto child : rootDef->fChildren) {
1779 // there may be more than one
1780 // if so, it's a bug since it's unknown which is the right one
1781 if (MarkType::kClass == child->fMarkType ||
1782 MarkType::kStruct == child->fMarkType ||
1783 (MarkType::kEnum == child->fMarkType && !child->fAnonymous) ||
1784 MarkType::kEnumClass == child->fMarkType) {
1785 SkASSERT("" == substitute);
1786 substitute = child->fName;
1787 if (MarkType::kEnum == child->fMarkType) {
1788 size_t parentClassEnd = substitute.find("::");
1789 SkASSERT(string::npos != parentClassEnd);
1790 string subEnd = substitute.substr(parentClassEnd + 2);
1791 if (fInEnum) {
1792 substitute = subEnd;
1793 }
1794 if (subEnd == undername) {
1795 break;
1796 }
1797 }
1798 }
1799 }
1800 }
1801 if (!substitute.length()) {
1802 const Definition* parent = rootDef;
1803 do {
1804 parent = parent->fParent;
1805 } while (parent && (MarkType::kSubtopic == parent->fMarkType
1806 || MarkType::kTopic == parent->fMarkType));
Cary Clark8032b982017-07-28 11:04:54 -04001807 if (parent) {
1808 if (MarkType::kClass == parent->fMarkType ||
1809 MarkType::kStruct == parent->fMarkType ||
Cary Clarkd0530ba2017-09-14 11:25:39 -04001810 (MarkType::kEnum == parent->fMarkType && !parent->fAnonymous) ||
Cary Clark8032b982017-07-28 11:04:54 -04001811 MarkType::kEnumClass == parent->fMarkType) {
1812 if (parent->fParent != fRootTopic) {
1813 substitute = parent->fName;
Cary Clark2a8c48b2018-02-15 17:31:24 -05001814 substitute += ' ';
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001815 substitute += ConvertRef(rootDef->fName, false);
Cary Clark8032b982017-07-28 11:04:54 -04001816 } else {
1817 substitute += ConvertRef(undername, first);
1818 }
1819 }
1820 }
1821 }
1822 }
Cary Clark1eace2d2017-07-31 07:52:43 -04001823 // Ensure first word after period is capitalized if substitute is lower cased.
1824 if (first && isupper(start[0]) && substitute.length() > 0 && islower(substitute[0])) {
1825 substitute[0] = start[0];
1826 }
Cary Clark8032b982017-07-28 11:04:54 -04001827 return substitute;
1828}
Cary Clark579985c2017-07-31 11:48:27 -04001829
Cary Clark8032b982017-07-28 11:04:54 -04001830int IncludeWriter::lookupMethod(const PunctuationState punctuation, const Word word,
Cary Clark6fc50412017-09-21 12:31:06 -04001831 const int lastSpace, const int run, int lastWrite, const char* data,
1832 bool hasIndirection) {
Cary Clark579985c2017-07-31 11:48:27 -04001833 int wordStart = lastSpace;
1834 while (' ' >= data[wordStart]) {
1835 ++wordStart;
1836 }
1837 const int wordEnd = PunctuationState::kDelimiter == punctuation ||
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001838 PunctuationState::kParen == punctuation ||
Cary Clark8032b982017-07-28 11:04:54 -04001839 PunctuationState::kPeriod == punctuation ? run - 1 : run;
Cary Clark6fc50412017-09-21 12:31:06 -04001840 string temp;
1841 if (hasIndirection && '(' != data[wordEnd - 1] && ')' != data[wordEnd - 1]) {
1842 // FIXME: hard-coded to assume a.b or a->b is a.b() or a->b().
1843 // need to check class a for member b to see if this is so
1844 TextParser parser(fFileName, &data[wordStart], &data[wordEnd], fLineCount);
1845 const char* indirection = parser.anyOf(".>");
1846 if (&data[wordEnd] <= &indirection[2] || 'f' != indirection[1] ||
1847 !isupper(indirection[2])) {
1848 temp = string(&data[wordStart], wordEnd - wordStart) + "()";
1849 }
1850 } else {
1851 temp = this->resolveMethod(&data[wordStart], &data[wordEnd], Word::kFirst == word);
1852 }
Cary Clark8032b982017-07-28 11:04:54 -04001853 if (temp.length()) {
Cary Clark579985c2017-07-31 11:48:27 -04001854 if (wordStart > lastWrite) {
1855 SkASSERT(data[wordStart - 1] >= ' ');
Cary Clark8032b982017-07-28 11:04:54 -04001856 if (' ' == data[lastWrite]) {
1857 this->writeSpace();
1858 }
Cary Clark579985c2017-07-31 11:48:27 -04001859 this->writeBlockTrim(wordStart - lastWrite, &data[lastWrite]);
1860 if (' ' == data[wordStart - 1]) {
Cary Clark8032b982017-07-28 11:04:54 -04001861 this->writeSpace();
1862 }
1863 }
1864 SkASSERT(temp[temp.length() - 1] > ' ');
1865 this->writeString(temp.c_str());
Cary Clark579985c2017-07-31 11:48:27 -04001866 lastWrite = wordEnd;
Cary Clark8032b982017-07-28 11:04:54 -04001867 }
1868 return lastWrite;
1869}
1870
1871int IncludeWriter::lookupReference(const PunctuationState punctuation, const Word word,
1872 const int start, const int run, int lastWrite, const char last, const char* data) {
1873 const int end = PunctuationState::kDelimiter == punctuation ||
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001874 PunctuationState::kParen == punctuation ||
Cary Clark8032b982017-07-28 11:04:54 -04001875 PunctuationState::kPeriod == punctuation ? run - 1 : run;
Cary Clarkce101242017-09-01 15:51:02 -04001876 RefType refType = RefType::kUndefined;
1877 string resolved = string(&data[start], (size_t) (end - start));
1878 string temp = this->resolveRef(&data[start], &data[end], Word::kFirst == word, &refType);
Cary Clark8032b982017-07-28 11:04:54 -04001879 if (!temp.length()) {
1880 if (Word::kFirst != word && '_' != last) {
Cary Clarkce101242017-09-01 15:51:02 -04001881 temp = ConvertRef(resolved, false);
Cary Clark8032b982017-07-28 11:04:54 -04001882 }
Ben Wagner63fd7602017-10-09 15:45:33 -04001883 }
Cary Clark8032b982017-07-28 11:04:54 -04001884 if (temp.length()) {
1885 if (start > lastWrite) {
1886 SkASSERT(data[start - 1] >= ' ');
1887 if (' ' == data[lastWrite]) {
1888 this->writeSpace();
1889 }
1890 this->writeBlockTrim(start - lastWrite, &data[lastWrite]);
1891 if (' ' == data[start - 1]) {
1892 this->writeSpace();
1893 }
1894 }
1895 SkASSERT(temp[temp.length() - 1] > ' ');
1896 this->writeString(temp.c_str());
1897 lastWrite = end;
1898 }
1899 return lastWrite;
1900}
1901
1902/* returns true if rewriteBlock wrote linefeeds */
Cary Clarkd0530ba2017-09-14 11:25:39 -04001903IncludeWriter::Wrote IncludeWriter::rewriteBlock(int size, const char* data, Phrase phrase) {
Cary Clark8032b982017-07-28 11:04:54 -04001904 bool wroteLineFeeds = false;
1905 while (size > 0 && data[0] <= ' ') {
1906 --size;
1907 ++data;
1908 }
1909 while (size > 0 && data[size - 1] <= ' ') {
1910 --size;
1911 }
1912 if (0 == size) {
1913 return Wrote::kNone;
1914 }
1915 int run = 0;
1916 Word word = Word::kStart;
Cary Clarkd0530ba2017-09-14 11:25:39 -04001917 PunctuationState punctuation = Phrase::kNo == phrase ?
1918 PunctuationState::kStart : PunctuationState::kSpace;
Cary Clark8032b982017-07-28 11:04:54 -04001919 int start = 0;
1920 int lastWrite = 0;
1921 int lineFeeds = 0;
1922 int lastPrintable = 0;
Cary Clark579985c2017-07-31 11:48:27 -04001923 int lastSpace = -1;
Cary Clark8032b982017-07-28 11:04:54 -04001924 char c = 0;
Kevin Lubick42846132018-01-05 10:11:11 -05001925 char last = 0;
Cary Clark6fc50412017-09-21 12:31:06 -04001926 bool embeddedIndirection = false;
Cary Clark579985c2017-07-31 11:48:27 -04001927 bool embeddedSymbol = false;
Cary Clark8032b982017-07-28 11:04:54 -04001928 bool hasLower = false;
1929 bool hasUpper = false;
Cary Clark6fc50412017-09-21 12:31:06 -04001930 bool hasIndirection = false;
Cary Clark8032b982017-07-28 11:04:54 -04001931 bool hasSymbol = false;
1932 while (run < size) {
1933 last = c;
1934 c = data[run];
1935 SkASSERT(' ' <= c || '\n' == c);
1936 if (lineFeeds && ' ' < c) {
1937 if (lastPrintable >= lastWrite) {
1938 if (' ' == data[lastWrite]) {
1939 this->writeSpace();
Cary Clarka560c472017-11-27 10:44:06 -05001940 lastWrite++;
Cary Clark8032b982017-07-28 11:04:54 -04001941 }
1942 this->writeBlock(lastPrintable - lastWrite + 1, &data[lastWrite]);
1943 }
1944 if (lineFeeds > 1) {
1945 this->lf(2);
1946 }
1947 this->lfcr(); // defer the indent until non-whitespace is seen
1948 lastWrite = run;
1949 lineFeeds = 0;
1950 }
1951 if (' ' < c) {
1952 lastPrintable = run;
1953 }
1954 switch (c) {
1955 case '\n':
1956 ++lineFeeds;
1957 wroteLineFeeds = true;
1958 case ' ':
1959 switch (word) {
1960 case Word::kStart:
1961 break;
1962 case Word::kUnderline:
1963 case Word::kCap:
1964 case Word::kFirst:
1965 if (!hasLower) {
1966 break;
1967 }
1968 lastWrite = this->lookupReference(punctuation, word, start, run,
1969 lastWrite, last, data);
1970 break;
1971 case Word::kMixed:
Cary Clark579985c2017-07-31 11:48:27 -04001972 if (hasUpper && hasLower && !hasSymbol && lastSpace > 0) {
1973 lastWrite = this->lookupMethod(punctuation, word, lastSpace, run,
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001974 lastWrite, data, hasIndirection);
Cary Clark8032b982017-07-28 11:04:54 -04001975 }
1976 break;
1977 default:
1978 SkASSERT(0);
1979 }
Cary Clark1eace2d2017-07-31 07:52:43 -04001980 punctuation = PunctuationState::kPeriod == punctuation ||
Ben Wagner63fd7602017-10-09 15:45:33 -04001981 (PunctuationState::kStart == punctuation && ' ' >= last) ?
Cary Clark1eace2d2017-07-31 07:52:43 -04001982 PunctuationState::kStart : PunctuationState::kSpace;
Cary Clark8032b982017-07-28 11:04:54 -04001983 word = Word::kStart;
Cary Clark6fc50412017-09-21 12:31:06 -04001984 embeddedIndirection = false;
Cary Clark579985c2017-07-31 11:48:27 -04001985 embeddedSymbol = false;
Cary Clark8032b982017-07-28 11:04:54 -04001986 hasLower = false;
1987 hasUpper = false;
Cary Clark6fc50412017-09-21 12:31:06 -04001988 hasIndirection = false;
Cary Clark8032b982017-07-28 11:04:54 -04001989 hasSymbol = false;
Cary Clark579985c2017-07-31 11:48:27 -04001990 lastSpace = run;
Cary Clark8032b982017-07-28 11:04:54 -04001991 break;
Cary Clarkc68ba1d2018-02-20 10:35:29 -05001992 case '.': case ',': case ';': case ':': case ')':
Cary Clark8032b982017-07-28 11:04:54 -04001993 switch (word) {
1994 case Word::kStart:
1995 punctuation = PunctuationState::kDelimiter;
1996 case Word::kCap:
1997 case Word::kFirst:
1998 case Word::kUnderline:
1999 case Word::kMixed:
2000 if (PunctuationState::kDelimiter == punctuation ||
2001 PunctuationState::kPeriod == punctuation) {
2002 word = Word::kMixed;
2003 }
Cary Clarkc68ba1d2018-02-20 10:35:29 -05002004 punctuation = '.' == c ? PunctuationState::kPeriod :
2005 PunctuationState::kDelimiter;
Cary Clark8032b982017-07-28 11:04:54 -04002006 break;
2007 default:
2008 SkASSERT(0);
2009 }
Cary Clarkc68ba1d2018-02-20 10:35:29 -05002010 ('.' == c ? embeddedIndirection : embeddedSymbol) = true;
Cary Clark8032b982017-07-28 11:04:54 -04002011 break;
Cary Clark6fc50412017-09-21 12:31:06 -04002012 case '>':
2013 if ('-' == last) {
2014 embeddedIndirection = true;
2015 break;
2016 }
Cary Clark8032b982017-07-28 11:04:54 -04002017 case '\'': // possessive apostrophe isn't treated as delimiting punctation
Cary Clarkce101242017-09-01 15:51:02 -04002018 case '\"': // quote is passed straight through
Cary Clark8032b982017-07-28 11:04:54 -04002019 case '=':
2020 case '!': // assumed not to be punctuation, but a programming symbol
Cary Clark6fc50412017-09-21 12:31:06 -04002021 case '&': case '<': case '{': case '}': case '/': case '*': case '[': case ']':
Cary Clark8032b982017-07-28 11:04:54 -04002022 word = Word::kMixed;
Cary Clark579985c2017-07-31 11:48:27 -04002023 embeddedSymbol = true;
Cary Clark8032b982017-07-28 11:04:54 -04002024 break;
2025 case '(':
2026 if (' ' == last) {
Cary Clarkc68ba1d2018-02-20 10:35:29 -05002027 punctuation = PunctuationState::kParen;
Cary Clark8032b982017-07-28 11:04:54 -04002028 } else {
2029 word = Word::kMixed;
2030 }
Cary Clark579985c2017-07-31 11:48:27 -04002031 embeddedSymbol = true;
Cary Clark8032b982017-07-28 11:04:54 -04002032 break;
Cary Clark8032b982017-07-28 11:04:54 -04002033 case '_':
2034 switch (word) {
2035 case Word::kStart:
2036 word = Word::kMixed;
2037 break;
2038 case Word::kCap:
2039 case Word::kFirst:
2040 case Word::kUnderline:
2041 word = Word::kUnderline;
2042 break;
2043 case Word::kMixed:
2044 break;
2045 default:
2046 SkASSERT(0);
2047 }
Cary Clark579985c2017-07-31 11:48:27 -04002048 hasSymbol |= embeddedSymbol;
Cary Clark8032b982017-07-28 11:04:54 -04002049 break;
Cary Clark73fa9722017-08-29 17:36:51 -04002050 case '+':
2051 // hackery to allow C++
2052 SkASSERT('C' == last || '+' == last); // FIXME: don't allow + outside of #Formula
2053 break;
Cary Clark8032b982017-07-28 11:04:54 -04002054 case 'A': case 'B': case 'C': case 'D': case 'E':
2055 case 'F': case 'G': case 'H': case 'I': case 'J':
2056 case 'K': case 'L': case 'M': case 'N': case 'O':
2057 case 'P': case 'Q': case 'R': case 'S': case 'T':
2058 case 'U': case 'V': case 'W': case 'X': case 'Y':
2059 case 'Z':
2060 switch (word) {
2061 case Word::kStart:
2062 word = PunctuationState::kStart == punctuation ? Word::kFirst : Word::kCap;
2063 start = run;
2064 break;
2065 case Word::kCap:
2066 case Word::kFirst:
Cary Clark154beea2017-10-26 07:58:48 -04002067 if (!isupper(last) && '~' != last) {
Cary Clark8032b982017-07-28 11:04:54 -04002068 word = Word::kMixed;
2069 }
2070 break;
2071 case Word::kUnderline:
2072 // some word in Xxx_XXX_Xxx can be all upper, but all can't: XXX_XXX
2073 if ('_' != last && !isupper(last)) {
2074 word = Word::kMixed;
2075 }
2076 break;
2077 case Word::kMixed:
2078 break;
2079 default:
2080 SkASSERT(0);
2081 }
2082 hasUpper = true;
2083 if (PunctuationState::kPeriod == punctuation ||
2084 PunctuationState::kDelimiter == punctuation) {
2085 word = Word::kMixed;
Cary Clark8032b982017-07-28 11:04:54 -04002086 }
Cary Clark6fc50412017-09-21 12:31:06 -04002087 hasIndirection |= embeddedIndirection;
Cary Clark579985c2017-07-31 11:48:27 -04002088 hasSymbol |= embeddedSymbol;
Cary Clark8032b982017-07-28 11:04:54 -04002089 break;
2090 case 'a': case 'b': case 'c': case 'd': case 'e':
2091 case 'f': case 'g': case 'h': case 'i': case 'j':
2092 case 'k': case 'l': case 'm': case 'n': case 'o':
2093 case 'p': case 'q': case 'r': case 's': case 't':
2094 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -04002095 case 'z':
Cary Clark8032b982017-07-28 11:04:54 -04002096 case '0': case '1': case '2': case '3': case '4':
2097 case '5': case '6': case '7': case '8': case '9':
2098 case '-':
2099 switch (word) {
2100 case Word::kStart:
2101 word = Word::kMixed;
2102 break;
2103 case Word::kMixed:
2104 case Word::kCap:
2105 case Word::kFirst:
2106 case Word::kUnderline:
2107 break;
2108 default:
2109 SkASSERT(0);
2110 }
2111 hasLower = true;
2112 punctuation = PunctuationState::kStart;
Cary Clark6fc50412017-09-21 12:31:06 -04002113 hasIndirection |= embeddedIndirection;
Cary Clark579985c2017-07-31 11:48:27 -04002114 hasSymbol |= embeddedSymbol;
Cary Clark8032b982017-07-28 11:04:54 -04002115 break;
Cary Clark154beea2017-10-26 07:58:48 -04002116 case '~':
2117 SkASSERT(Word::kStart == word);
2118 word = PunctuationState::kStart == punctuation ? Word::kFirst : Word::kCap;
2119 start = run;
2120 hasUpper = true;
2121 hasIndirection |= embeddedIndirection;
2122 hasSymbol |= embeddedSymbol;
2123 break;
Cary Clark8032b982017-07-28 11:04:54 -04002124 default:
2125 SkASSERT(0);
2126 }
2127 ++run;
2128 }
2129 if ((word == Word::kCap || word == Word::kFirst || word == Word::kUnderline) && hasLower) {
2130 lastWrite = this->lookupReference(punctuation, word, start, run, lastWrite, last, data);
Cary Clark579985c2017-07-31 11:48:27 -04002131 } else if (word == Word::kMixed && hasUpper && hasLower && !hasSymbol && lastSpace > 0) {
Cary Clark6fc50412017-09-21 12:31:06 -04002132 lastWrite = this->lookupMethod(punctuation, word, lastSpace, run, lastWrite, data,
2133 hasIndirection && !hasSymbol);
Cary Clark8032b982017-07-28 11:04:54 -04002134 }
2135 if (run > lastWrite) {
2136 if (' ' == data[lastWrite]) {
2137 this->writeSpace();
2138 }
2139 this->writeBlock(run - lastWrite, &data[lastWrite]);
2140 }
2141 return wroteLineFeeds ? Wrote::kLF : Wrote::kChars;
2142}