blob: f5786b9daaf537d4606877a330a2b8c6e44303f4 [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
10#include "SkOSFile.h"
11#include "SkOSPath.h"
12
Ben Wagner63fd7602017-10-09 15:45:33 -040013/*
Cary Clark8032b982017-07-28 11:04:54 -040014things to do
15if cap word is beginning of sentence, add it to table as lower-case
16 word must have only a single initial capital
17
18if word is camel cased, look for :: matches on suffix
19
20when function crosses lines, whole thing isn't seen as a 'word' e.g., search for largeArc in path
21
22words in external not seen
Cary Clark80247e52018-07-11 16:18:41 -040023
24look for x-bit but allow x bits
Cary Clarkd2ca79c2018-08-10 13:09:13 -040025
26don't treat 'pos' or 'glyphs' as spell-checkable as in 'RunBuffer.pos' or 'RunBuffer.glyphs'
Cary Clark8032b982017-07-28 11:04:54 -040027 */
Cary Clark80247e52018-07-11 16:18:41 -040028
Cary Clark8032b982017-07-28 11:04:54 -040029struct CheckEntry {
30 string fFile;
31 int fLine;
32 int fCount;
Cary Clark5538c132018-06-14 12:28:14 -040033 bool fOverride;
Cary Clark8032b982017-07-28 11:04:54 -040034};
35
36class SpellCheck : public ParserCommon {
37public:
38 SpellCheck(const BmhParser& bmh) : ParserCommon()
39 , fBmhParser(bmh) {
40 this->reset();
41 }
42 bool check(const char* match);
Cary Clarkce101242017-09-01 15:51:02 -040043 void report(SkCommandLineFlags::StringArray report);
Cary Clark8032b982017-07-28 11:04:54 -040044private:
45 enum class TableState {
46 kNone,
47 kRow,
48 kColumn,
49 };
50
Cary Clark682c58d2018-05-16 07:07:07 -040051 enum class PrintCheck {
52 kWordsOnly,
53 kAllowNumbers,
54 };
55
Cary Clark8032b982017-07-28 11:04:54 -040056 bool check(Definition* );
57 bool checkable(MarkType markType);
Cary Clark682c58d2018-05-16 07:07:07 -040058 void childCheck(Definition* def, const char* start);
Cary Clark8032b982017-07-28 11:04:54 -040059 void leafCheck(const char* start, const char* end);
60 bool parseFromFile(const char* path) override { return true; }
Cary Clark682c58d2018-05-16 07:07:07 -040061 void printCheck(string str, PrintCheck);
Cary Clark8032b982017-07-28 11:04:54 -040062
63 void reset() override {
64 INHERITED::resetCommon();
65 fMethod = nullptr;
66 fRoot = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -040067 fInCode = false;
68 fInConst = false;
Cary Clarkce101242017-09-01 15:51:02 -040069 fInFormula = false;
Cary Clark8032b982017-07-28 11:04:54 -040070 fInDescription = false;
71 fInStdOut = false;
Cary Clark5538c132018-06-14 12:28:14 -040072 fOverride = false;
Cary Clark8032b982017-07-28 11:04:54 -040073 }
74
Cary Clark2d4bf5f2018-04-16 08:37:38 -040075 void wordCheck(string str);
Cary Clark8032b982017-07-28 11:04:54 -040076 void wordCheck(ptrdiff_t len, const char* ch);
77
78 unordered_map<string, CheckEntry> fCode;
79 unordered_map<string, CheckEntry> fColons;
80 unordered_map<string, CheckEntry> fDigits;
81 unordered_map<string, CheckEntry> fDots;
82 unordered_map<string, CheckEntry> fParens; // also hold destructors, operators
83 unordered_map<string, CheckEntry> fUnderscores;
84 unordered_map<string, CheckEntry> fWords;
85 const BmhParser& fBmhParser;
86 Definition* fMethod;
87 RootDefinition* fRoot;
Cary Clark4855f782018-02-06 09:41:53 -050088 int fLocalLine;
Cary Clark8032b982017-07-28 11:04:54 -040089 bool fInCode;
90 bool fInConst;
91 bool fInDescription;
Cary Clarkce101242017-09-01 15:51:02 -040092 bool fInFormula;
Cary Clark8032b982017-07-28 11:04:54 -040093 bool fInStdOut;
Cary Clark5538c132018-06-14 12:28:14 -040094 bool fOverride;
Cary Clark8032b982017-07-28 11:04:54 -040095 typedef ParserCommon INHERITED;
96};
97
98/* This doesn't perform a traditional spell or grammar check, although
99 maybe it should. Instead it looks for words used uncommonly and lower
100 case words that match capitalized words that are not sentence starters.
101 It also looks for articles preceeding capitalized words and their
102 modifiers to try to maintain a consistent voice.
103 Maybe also look for passive verbs (e.g. 'is') and suggest active ones?
104 */
Cary Clarkce101242017-09-01 15:51:02 -0400105void BmhParser::spellCheck(const char* match, SkCommandLineFlags::StringArray report) const {
Cary Clark8032b982017-07-28 11:04:54 -0400106 SpellCheck checker(*this);
107 checker.check(match);
Cary Clarkce101242017-09-01 15:51:02 -0400108 checker.report(report);
Cary Clark8032b982017-07-28 11:04:54 -0400109}
110
Cary Clark2f466242017-12-11 16:03:17 -0500111void BmhParser::spellStatus(const char* statusFile, SkCommandLineFlags::StringArray report) const {
112 SpellCheck checker(*this);
113 StatusIter iter(statusFile, ".bmh", StatusFilter::kInProgress);
Cary Clark7724d3f2018-09-14 09:12:38 -0400114 string file;
Cary Clark61313f32018-10-08 14:57:48 -0400115 iter.next(&file, nullptr);
Cary Clark2f466242017-12-11 16:03:17 -0500116 string match = iter.baseDir();
117 checker.check(match.c_str());
118 checker.report(report);
119}
120
Cary Clark8032b982017-07-28 11:04:54 -0400121bool SpellCheck::check(const char* match) {
122 for (const auto& topic : fBmhParser.fTopicMap) {
123 Definition* topicDef = topic.second;
124 if (topicDef->fParent) {
125 continue;
126 }
127 if (!topicDef->isRoot()) {
128 return this->reportError<bool>("expected root topic");
129 }
130 fRoot = topicDef->asRoot();
131 if (string::npos == fRoot->fFileName.rfind(match)) {
132 continue;
133 }
Cary Clark5538c132018-06-14 12:28:14 -0400134 fOverride = string::npos != fRoot->fFileName.rfind("undocumented.bmh")
135 || string::npos != fRoot->fFileName.rfind("markup.bmh")
136 || string::npos != fRoot->fFileName.rfind("usingBookmaker.bmh");
137 this->check(topicDef);
Cary Clark8032b982017-07-28 11:04:54 -0400138 }
139 return true;
140}
141
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400142static bool all_lower(string str) {
Cary Clarkce101242017-09-01 15:51:02 -0400143 for (auto c : str) {
144 if (!islower(c)) {
145 return false;
146 }
147 }
148 return true;
149}
150
Cary Clark8032b982017-07-28 11:04:54 -0400151bool SpellCheck::check(Definition* def) {
152 fFileName = def->fFileName;
153 fLineCount = def->fLineCount;
154 string printable = def->printableName();
155 const char* textStart = def->fContentStart;
Cary Clark8032b982017-07-28 11:04:54 -0400156 switch (def->fMarkType) {
157 case MarkType::kAlias:
158 break;
159 case MarkType::kAnchor:
160 break;
161 case MarkType::kBug:
162 break;
163 case MarkType::kClass:
164 this->wordCheck(def->fName);
165 break;
166 case MarkType::kCode:
167 fInCode = true;
168 break;
169 case MarkType::kColumn:
170 break;
171 case MarkType::kComment:
172 break;
173 case MarkType::kConst: {
174 fInConst = true;
Cary Clark8032b982017-07-28 11:04:54 -0400175 this->wordCheck(def->fName);
176 const char* lineEnd = strchr(textStart, '\n');
177 this->wordCheck(lineEnd - textStart, textStart);
178 textStart = lineEnd;
179 } break;
180 case MarkType::kDefine:
181 break;
Cary Clark8032b982017-07-28 11:04:54 -0400182 case MarkType::kDeprecated:
183 break;
184 case MarkType::kDescription:
185 fInDescription = true;
186 break;
Cary Clark682c58d2018-05-16 07:07:07 -0400187 case MarkType::kDetails:
Cary Clark8032b982017-07-28 11:04:54 -0400188 break;
Cary Clarkac47b882018-01-11 10:35:44 -0500189 case MarkType::kDuration:
190 break;
Cary Clark8032b982017-07-28 11:04:54 -0400191 case MarkType::kEnum:
192 case MarkType::kEnumClass:
193 this->wordCheck(def->fName);
194 break;
Cary Clark8032b982017-07-28 11:04:54 -0400195 case MarkType::kExample:
196 break;
Cary Clarkce101242017-09-01 15:51:02 -0400197 case MarkType::kExperimental:
198 break;
Cary Clark8032b982017-07-28 11:04:54 -0400199 case MarkType::kExternal:
200 break;
Cary Clark0d225392018-06-07 09:59:07 -0400201 case MarkType::kFile:
202 break;
Cary Clark8032b982017-07-28 11:04:54 -0400203 case MarkType::kFormula:
Cary Clarkce101242017-09-01 15:51:02 -0400204 fInFormula = true;
Cary Clark8032b982017-07-28 11:04:54 -0400205 break;
206 case MarkType::kFunction:
207 break;
208 case MarkType::kHeight:
209 break;
Cary Clarkf895a422018-02-27 09:54:21 -0500210 case MarkType::kIllustration:
211 break;
Cary Clark8032b982017-07-28 11:04:54 -0400212 case MarkType::kImage:
213 break;
Cary Clark4855f782018-02-06 09:41:53 -0500214 case MarkType::kIn:
215 break;
Cary Clark8032b982017-07-28 11:04:54 -0400216 case MarkType::kLegend:
217 break;
Cary Clark4855f782018-02-06 09:41:53 -0500218 case MarkType::kLine:
219 break;
Cary Clarkce101242017-09-01 15:51:02 -0400220 case MarkType::kLink:
221 break;
Cary Clark8032b982017-07-28 11:04:54 -0400222 case MarkType::kList:
223 break;
Cary Clark154beea2017-10-26 07:58:48 -0400224 case MarkType::kLiteral:
225 break;
Cary Clarkce101242017-09-01 15:51:02 -0400226 case MarkType::kMarkChar:
227 break;
Cary Clark8032b982017-07-28 11:04:54 -0400228 case MarkType::kMember:
229 break;
230 case MarkType::kMethod: {
231 string method_name = def->methodName();
Cary Clarkce101242017-09-01 15:51:02 -0400232 if (all_lower(method_name)) {
233 method_name += "()";
234 }
Cary Clarka560c472017-11-27 10:44:06 -0500235 if (!def->isClone() && Definition::MethodType::kOperator != def->fMethodType) {
Cary Clark8032b982017-07-28 11:04:54 -0400236 this->wordCheck(method_name);
237 }
Cary Clark8032b982017-07-28 11:04:54 -0400238 fMethod = def;
239 } break;
Cary Clarkce101242017-09-01 15:51:02 -0400240 case MarkType::kNoExample:
241 break;
Cary Clark682c58d2018-05-16 07:07:07 -0400242 case MarkType::kNoJustify:
243 break;
Cary Clark154beea2017-10-26 07:58:48 -0400244 case MarkType::kOutdent:
245 break;
Cary Clark8032b982017-07-28 11:04:54 -0400246 case MarkType::kParam: {
Cary Clark8032b982017-07-28 11:04:54 -0400247 TextParser paramParser(def->fFileName, def->fStart, def->fContentStart,
248 def->fLineCount);
249 paramParser.skipWhiteSpace();
250 SkASSERT(paramParser.startsWith("#Param"));
251 paramParser.next(); // skip hash
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400252 paramParser.skipToNonName(); // skip Param
Cary Clark8032b982017-07-28 11:04:54 -0400253 paramParser.skipSpace();
254 const char* paramName = paramParser.fChar;
255 paramParser.skipToSpace();
256 fInCode = true;
257 this->wordCheck(paramParser.fChar - paramName, paramName);
258 fInCode = false;
Cary Clark682c58d2018-05-16 07:07:07 -0400259 } break;
260 case MarkType::kPhraseDef:
261 break;
262 case MarkType::kPhraseParam:
263 break;
264 case MarkType::kPhraseRef:
265 break;
Cary Clark8032b982017-07-28 11:04:54 -0400266 case MarkType::kPlatform:
267 break;
Cary Clark4855f782018-02-06 09:41:53 -0500268 case MarkType::kPopulate:
269 break;
Cary Clarkce101242017-09-01 15:51:02 -0400270 case MarkType::kPrivate:
271 break;
Cary Clark8032b982017-07-28 11:04:54 -0400272 case MarkType::kReturn:
273 break;
274 case MarkType::kRow:
275 break;
276 case MarkType::kSeeAlso:
277 break;
Cary Clark4855f782018-02-06 09:41:53 -0500278 case MarkType::kSet:
279 break;
Cary Clark8032b982017-07-28 11:04:54 -0400280 case MarkType::kStdOut: {
281 fInStdOut = true;
282 TextParser code(def);
283 code.skipSpace();
284 while (!code.eof()) {
285 const char* end = code.trimmedLineEnd();
286 this->wordCheck(end - code.fChar, code.fChar);
287 code.skipToLineStart();
288 }
289 fInStdOut = false;
290 } break;
291 case MarkType::kStruct:
292 fRoot = def->asRoot();
293 this->wordCheck(def->fName);
294 break;
Cary Clarkce101242017-09-01 15:51:02 -0400295 case MarkType::kSubstitute:
296 break;
Cary Clark8032b982017-07-28 11:04:54 -0400297 case MarkType::kSubtopic:
Cary Clark682c58d2018-05-16 07:07:07 -0400298 // TODO: add a tag that allows subtopic labels in illustrations to skip spellcheck?
299 if (string::npos == fFileName.find("illustrations.bmh")) {
300 this->printCheck(printable, PrintCheck::kAllowNumbers);
301 }
Cary Clark8032b982017-07-28 11:04:54 -0400302 break;
303 case MarkType::kTable:
304 break;
305 case MarkType::kTemplate:
306 break;
307 case MarkType::kText:
308 break;
Cary Clark8032b982017-07-28 11:04:54 -0400309 case MarkType::kToDo:
310 break;
311 case MarkType::kTopic:
Cary Clark682c58d2018-05-16 07:07:07 -0400312 this->printCheck(printable, PrintCheck::kWordsOnly);
Cary Clark8032b982017-07-28 11:04:54 -0400313 break;
Cary Clark8032b982017-07-28 11:04:54 -0400314 case MarkType::kTypedef:
315 break;
316 case MarkType::kUnion:
317 break;
Cary Clarkce101242017-09-01 15:51:02 -0400318 case MarkType::kVolatile:
319 break;
Cary Clark8032b982017-07-28 11:04:54 -0400320 case MarkType::kWidth:
321 break;
322 default:
323 SkASSERT(0); // handle everything
324 break;
325 }
326 this->childCheck(def, textStart);
327 switch (def->fMarkType) { // post child work, at least for tables
328 case MarkType::kCode:
329 fInCode = false;
330 break;
331 case MarkType::kColumn:
332 break;
333 case MarkType::kDescription:
334 fInDescription = false;
335 break;
336 case MarkType::kEnum:
337 case MarkType::kEnumClass:
338 break;
339 case MarkType::kExample:
340 break;
Cary Clarkce101242017-09-01 15:51:02 -0400341 case MarkType::kFormula:
342 fInFormula = false;
343 break;
Cary Clark8032b982017-07-28 11:04:54 -0400344 case MarkType::kLegend:
345 break;
346 case MarkType::kMethod:
347 fMethod = nullptr;
348 break;
349 case MarkType::kConst:
350 fInConst = false;
351 case MarkType::kParam:
Cary Clark8032b982017-07-28 11:04:54 -0400352 break;
353 case MarkType::kReturn:
354 case MarkType::kSeeAlso:
355 break;
356 case MarkType::kRow:
357 break;
358 case MarkType::kStruct:
359 fRoot = fRoot->rootParent();
360 break;
361 case MarkType::kTable:
362 break;
363 default:
364 break;
365 }
366 return true;
367}
368
369bool SpellCheck::checkable(MarkType markType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400370 return BmhParser::Resolvable::kYes == fBmhParser.kMarkProps[(int) markType].fResolve;
Cary Clark8032b982017-07-28 11:04:54 -0400371}
372
Cary Clark682c58d2018-05-16 07:07:07 -0400373void SpellCheck::childCheck(Definition* def, const char* start) {
Cary Clark8032b982017-07-28 11:04:54 -0400374 const char* end;
375 fLineCount = def->fLineCount;
376 if (def->isRoot()) {
Cary Clark682c58d2018-05-16 07:07:07 -0400377 fRoot = def->asRoot();
Cary Clark8032b982017-07-28 11:04:54 -0400378 }
379 for (auto& child : def->fChildren) {
380 end = child->fStart;
381 if (this->checkable(def->fMarkType)) {
382 this->leafCheck(start, end);
383 }
384 this->check(child);
385 start = child->fTerminator;
386 }
387 if (this->checkable(def->fMarkType)) {
388 end = def->fContentEnd;
389 this->leafCheck(start, end);
390 }
391}
392
393void SpellCheck::leafCheck(const char* start, const char* end) {
Cary Clarkce101242017-09-01 15:51:02 -0400394 const char* chPtr = start;
395 int inAngles = 0;
396 int inParens = 0;
397 bool inQuotes = false;
398 bool allLower = true;
Cary Clark80247e52018-07-11 16:18:41 -0400399 char prePriorCh = 0;
Cary Clarkce101242017-09-01 15:51:02 -0400400 char priorCh = 0;
401 char lastCh = 0;
402 const char* wordStart = nullptr;
403 const char* wordEnd = nullptr;
404 const char* possibleEnd = nullptr;
Cary Clark4855f782018-02-06 09:41:53 -0500405 fLocalLine = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400406 do {
Cary Clarkce101242017-09-01 15:51:02 -0400407 if (wordStart && wordEnd) {
408 if (!allLower || (!inQuotes && '\"' != lastCh && !inParens
409 && ')' != lastCh && !inAngles && '>' != lastCh)) {
410 string word(wordStart, (possibleEnd ? possibleEnd : wordEnd) - wordStart);
Cary Clark80247e52018-07-11 16:18:41 -0400411 if ("e" != word || !isdigit(prePriorCh) || ('+' != lastCh &&
412 '-' != lastCh && !isdigit(lastCh))) {
413 this->wordCheck(word);
414 }
Cary Clarkce101242017-09-01 15:51:02 -0400415 }
416 wordStart = nullptr;
417 }
418 if (chPtr == end) {
Cary Clark8032b982017-07-28 11:04:54 -0400419 break;
420 }
Cary Clarkce101242017-09-01 15:51:02 -0400421 switch (*chPtr) {
422 case '>':
423 if (isalpha(lastCh)) {
424 --inAngles;
425 SkASSERT(inAngles >= 0);
426 }
427 wordEnd = chPtr;
428 break;
429 case '(':
430 ++inParens;
431 possibleEnd = chPtr;
432 break;
433 case ')':
434 --inParens;
435 if ('(' == lastCh) {
436 wordEnd = chPtr + 1;
437 } else {
438 wordEnd = chPtr;
439 }
Cary Clark7fc1d122017-10-09 14:07:42 -0400440 SkASSERT(inParens >= 0 || fInStdOut);
Cary Clarkce101242017-09-01 15:51:02 -0400441 break;
442 case '\"':
443 inQuotes = !inQuotes;
444 wordEnd = chPtr;
445 SkASSERT(inQuotes == !wordStart);
446 break;
447 case 'A': case 'B': case 'C': case 'D': case 'E':
448 case 'F': case 'G': case 'H': case 'I': case 'J':
449 case 'K': case 'L': case 'M': case 'N': case 'O':
450 case 'P': case 'Q': case 'R': case 'S': case 'T':
451 case 'U': case 'V': case 'W': case 'X': case 'Y':
452 case 'Z':
453 allLower = false;
454 case 'a': case 'b': case 'c': case 'd': case 'e':
455 case 'f': case 'g': case 'h': case 'i': case 'j':
456 case 'k': case 'l': case 'm': case 'n': case 'o':
457 case 'p': case 'q': case 'r': case 's': case 't':
458 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -0400459 case 'z':
Cary Clarkce101242017-09-01 15:51:02 -0400460 if (!wordStart) {
461 wordStart = chPtr;
462 wordEnd = nullptr;
463 possibleEnd = nullptr;
464 allLower = 'a' <= *chPtr;
465 if ('<' == lastCh || ('<' == priorCh && '/' == lastCh)) {
466 ++inAngles;
467 }
468 }
469 break;
470 case '0': case '1': case '2': case '3': case '4':
471 case '5': case '6': case '7': case '8': case '9':
Ben Wagner63fd7602017-10-09 15:45:33 -0400472 case '_':
Cary Clarkce101242017-09-01 15:51:02 -0400473 allLower = false;
474 case '-': // note that dash doesn't clear allLower
475 break;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400476 case '!':
477 if (!inQuotes) {
478 wordEnd = chPtr;
479 }
480 break;
Cary Clark4855f782018-02-06 09:41:53 -0500481 case '\n':
482 ++fLocalLine;
483 // fall through
Cary Clarkce101242017-09-01 15:51:02 -0400484 default:
485 wordEnd = chPtr;
486 break;
487 }
Cary Clark80247e52018-07-11 16:18:41 -0400488 prePriorCh = priorCh;
Cary Clarkce101242017-09-01 15:51:02 -0400489 priorCh = lastCh;
490 lastCh = *chPtr;
491 } while (++chPtr <= end);
Cary Clark8032b982017-07-28 11:04:54 -0400492}
493
Cary Clark682c58d2018-05-16 07:07:07 -0400494void SpellCheck::printCheck(string str, PrintCheck allowed) {
Cary Clark8032b982017-07-28 11:04:54 -0400495 string word;
496 for (std::stringstream stream(str); stream >> word; ) {
Cary Clark682c58d2018-05-16 07:07:07 -0400497 if (PrintCheck::kAllowNumbers == allowed && (std::isdigit(word.back()) || 'x' == word.back())) {
498 // allow ###x for RGB_888x
499 if ((size_t) std::count_if(word.begin(), word.end() - 1,
500 [](unsigned char c){ return std::isdigit(c); } ) == word.length() - 1) {
501 continue;
502 }
503 }
Cary Clark8032b982017-07-28 11:04:54 -0400504 wordCheck(word);
505 }
506}
507
Cary Clarkce101242017-09-01 15:51:02 -0400508static bool stringCompare(const std::pair<string, CheckEntry>& i, const std::pair<string, CheckEntry>& j) {
509 return i.first.compare(j.first) < 0;
510}
511
512void SpellCheck::report(SkCommandLineFlags::StringArray report) {
513 vector<std::pair<string, CheckEntry>> elems(fWords.begin(), fWords.end());
514 std::sort(elems.begin(), elems.end(), stringCompare);
515 if (report.contains("once")) {
516 for (auto iter : elems) {
Cary Clark5538c132018-06-14 12:28:14 -0400517 if (iter.second.fOverride) {
Cary Clarkce101242017-09-01 15:51:02 -0400518 continue;
519 }
520 if (iter.second.fCount == 1) {
Cary Clark4855f782018-02-06 09:41:53 -0500521 string fullName = this->ReportFilename(iter.second.fFile);
522 SkDebugf("%s(%d): %s\n", fullName.c_str(), iter.second.fLine,
Cary Clarkce101242017-09-01 15:51:02 -0400523 iter.first.c_str());
524 }
Cary Clark8032b982017-07-28 11:04:54 -0400525 }
Cary Clarkce101242017-09-01 15:51:02 -0400526 SkDebugf("\n");
Cary Clarkd0530ba2017-09-14 11:25:39 -0400527 return;
Cary Clarkce101242017-09-01 15:51:02 -0400528 }
529 if (report.contains("all")) {
530 int column = 0;
Cary Clarkd0530ba2017-09-14 11:25:39 -0400531 char lastInitial = 'a';
Cary Clark154beea2017-10-26 07:58:48 -0400532 int count = 0;
Cary Clarkce101242017-09-01 15:51:02 -0400533 for (auto iter : elems) {
Cary Clark5538c132018-06-14 12:28:14 -0400534 if (iter.second.fOverride) {
Cary Clarkce101242017-09-01 15:51:02 -0400535 continue;
536 }
537 string check = iter.first.c_str();
538 bool allLower = true;
539 for (auto c : check) {
540 if (isupper(c)) {
541 allLower = false;
542 break;
543 }
544 }
545 if (!allLower) {
546 continue;
547 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400548 if (column + check.length() > 100 || check[0] != lastInitial) {
Cary Clarkce101242017-09-01 15:51:02 -0400549 SkDebugf("\n");
550 column = 0;
551 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400552 if (check[0] != lastInitial) {
553 SkDebugf("\n");
554 lastInitial = check[0];
555 }
Cary Clarkce101242017-09-01 15:51:02 -0400556 SkDebugf("%s ", check.c_str());
557 column += check.length();
Cary Clark154beea2017-10-26 07:58:48 -0400558 ++count;
Cary Clark8032b982017-07-28 11:04:54 -0400559 }
Cary Clark154beea2017-10-26 07:58:48 -0400560 SkDebugf("\n\ncount = %d\n", count);
Cary Clarkd0530ba2017-09-14 11:25:39 -0400561 return;
Cary Clarkce101242017-09-01 15:51:02 -0400562 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400563 int index = 0;
564 const char* mispelled = report[0];
565 for (auto iter : elems) {
Cary Clark5538c132018-06-14 12:28:14 -0400566 if (iter.second.fOverride) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400567 continue;
568 }
569 string check = iter.first.c_str();
570 while (check.compare(mispelled) > 0) {
571 SkDebugf("%s not found\n", mispelled);
572 if (report.count() == ++index) {
Cary Clarkce101242017-09-01 15:51:02 -0400573 break;
574 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400575 }
576 if (report.count() == index) {
577 break;
578 }
579 if (check.compare(mispelled) == 0) {
Cary Clark4855f782018-02-06 09:41:53 -0500580 string fullName = this->ReportFilename(iter.second.fFile);
581 SkDebugf("%s(%d): %s\n", fullName.c_str(), iter.second.fLine,
Cary Clarkd0530ba2017-09-14 11:25:39 -0400582 iter.first.c_str());
583 if (report.count() == ++index) {
584 break;
Cary Clarkce101242017-09-01 15:51:02 -0400585 }
Cary Clark8032b982017-07-28 11:04:54 -0400586 }
587 }
588}
589
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400590void SpellCheck::wordCheck(string str) {
Cary Clarkce101242017-09-01 15:51:02 -0400591 if ("nullptr" == str) {
592 return; // doesn't seem worth it, treating nullptr as a word in need of correction
593 }
Cary Clark8032b982017-07-28 11:04:54 -0400594 bool hasColon = false;
595 bool hasDot = false;
596 bool hasParen = false;
597 bool hasUnderscore = false;
598 bool sawDash = false;
599 bool sawDigit = false;
600 bool sawSpecial = false;
601 SkASSERT(str.length() > 0);
602 SkASSERT(isalpha(str[0]) || '~' == str[0]);
603 for (char ch : str) {
604 if (isalpha(ch) || '-' == ch) {
605 sawDash |= '-' == ch;
606 continue;
607 }
608 bool isColon = ':' == ch;
609 hasColon |= isColon;
610 bool isDot = '.' == ch;
611 hasDot |= isDot;
Cary Clarkbc5697d2017-10-04 14:31:33 -0400612 bool isParen = '(' == ch || ')' == ch || '~' == ch || '=' == ch || '!' == ch ||
613 '[' == ch || ']' == ch;
Cary Clark8032b982017-07-28 11:04:54 -0400614 hasParen |= isParen;
615 bool isUnderscore = '_' == ch;
616 hasUnderscore |= isUnderscore;
617 if (isColon || isDot || isUnderscore || isParen) {
618 continue;
619 }
620 if (isdigit(ch)) {
621 sawDigit = true;
622 continue;
623 }
624 if ('&' == ch || ',' == ch || ' ' == ch) {
625 sawSpecial = true;
626 continue;
627 }
628 SkASSERT(0);
629 }
630 if (sawSpecial && !hasParen) {
631 SkASSERT(0);
632 }
633 bool inCode = fInCode;
634 if (hasUnderscore && isupper(str[0]) && ('S' != str[0] || 'K' != str[1])
Ben Wagner63fd7602017-10-09 15:45:33 -0400635 && !hasColon && !hasDot && !hasParen && !fInStdOut && !inCode && !fInConst
Cary Clark8032b982017-07-28 11:04:54 -0400636 && !sawDigit && !sawSpecial && !sawDash) {
637 std::istringstream ss(str);
638 string token;
639 while (std::getline(ss, token, '_')) {
Cary Clarka560c472017-11-27 10:44:06 -0500640 if (token.length()) {
641 this->wordCheck(token);
642 }
Cary Clark8032b982017-07-28 11:04:54 -0400643 }
644 return;
645 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400646 if (!hasColon && !hasDot && !hasParen && !hasUnderscore
Cary Clark8032b982017-07-28 11:04:54 -0400647 && !fInStdOut && !inCode && !fInConst && !sawDigit
648 && islower(str[0]) && isupper(str[1])) {
649 inCode = true;
650 }
Cary Clarkce101242017-09-01 15:51:02 -0400651 bool methodParam = false;
652 if (fMethod) {
653 for (auto child : fMethod->fChildren) {
654 if (MarkType::kParam == child->fMarkType && str == child->fName) {
655 methodParam = true;
656 break;
657 }
658 }
659 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400660 auto& mappy = hasColon ? fColons :
Cary Clark8032b982017-07-28 11:04:54 -0400661 hasDot ? fDots :
662 hasParen ? fParens :
663 hasUnderscore ? fUnderscores :
Cary Clarkce101242017-09-01 15:51:02 -0400664 fInStdOut || fInFormula || inCode || fInConst || methodParam ? fCode :
Cary Clark8032b982017-07-28 11:04:54 -0400665 sawDigit ? fDigits : fWords;
666 auto iter = mappy.find(str);
667 if (mappy.end() != iter) {
Cary Clark5538c132018-06-14 12:28:14 -0400668 if (iter->second.fOverride && !fOverride) {
669 iter->second.fFile = fFileName;
670 iter->second.fLine = fLineCount + fLocalLine;
671 iter->second.fOverride = false;
672 }
Cary Clark8032b982017-07-28 11:04:54 -0400673 iter->second.fCount += 1;
674 } else {
675 CheckEntry* entry = &mappy[str];
676 entry->fFile = fFileName;
Cary Clark4855f782018-02-06 09:41:53 -0500677 entry->fLine = fLineCount + fLocalLine;
Cary Clark8032b982017-07-28 11:04:54 -0400678 entry->fCount = 1;
Cary Clark5538c132018-06-14 12:28:14 -0400679 entry->fOverride = fOverride;
Cary Clark8032b982017-07-28 11:04:54 -0400680 }
681}
682
683void SpellCheck::wordCheck(ptrdiff_t len, const char* ch) {
684 leafCheck(ch, ch + len);
685}