blob: 75a01f78dc2c5e0eb2de81657919df6af0975320 [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 Clarka90ea222018-10-16 10:30:28 -0400203 case MarkType::kFilter:
204 break;
Cary Clark8032b982017-07-28 11:04:54 -0400205 case MarkType::kFormula:
Cary Clarkce101242017-09-01 15:51:02 -0400206 fInFormula = true;
Cary Clark8032b982017-07-28 11:04:54 -0400207 break;
208 case MarkType::kFunction:
209 break;
210 case MarkType::kHeight:
211 break;
Cary Clarkf895a422018-02-27 09:54:21 -0500212 case MarkType::kIllustration:
213 break;
Cary Clark8032b982017-07-28 11:04:54 -0400214 case MarkType::kImage:
215 break;
Cary Clark4855f782018-02-06 09:41:53 -0500216 case MarkType::kIn:
217 break;
Cary Clark8032b982017-07-28 11:04:54 -0400218 case MarkType::kLegend:
219 break;
Cary Clark4855f782018-02-06 09:41:53 -0500220 case MarkType::kLine:
221 break;
Cary Clarkce101242017-09-01 15:51:02 -0400222 case MarkType::kLink:
223 break;
Cary Clark8032b982017-07-28 11:04:54 -0400224 case MarkType::kList:
225 break;
Cary Clark154beea2017-10-26 07:58:48 -0400226 case MarkType::kLiteral:
227 break;
Cary Clarkce101242017-09-01 15:51:02 -0400228 case MarkType::kMarkChar:
229 break;
Cary Clark8032b982017-07-28 11:04:54 -0400230 case MarkType::kMember:
231 break;
232 case MarkType::kMethod: {
233 string method_name = def->methodName();
Cary Clarkce101242017-09-01 15:51:02 -0400234 if (all_lower(method_name)) {
235 method_name += "()";
236 }
Cary Clarka560c472017-11-27 10:44:06 -0500237 if (!def->isClone() && Definition::MethodType::kOperator != def->fMethodType) {
Cary Clark8032b982017-07-28 11:04:54 -0400238 this->wordCheck(method_name);
239 }
Cary Clark8032b982017-07-28 11:04:54 -0400240 fMethod = def;
241 } break;
Cary Clarkce101242017-09-01 15:51:02 -0400242 case MarkType::kNoExample:
243 break;
Cary Clark682c58d2018-05-16 07:07:07 -0400244 case MarkType::kNoJustify:
245 break;
Cary Clark154beea2017-10-26 07:58:48 -0400246 case MarkType::kOutdent:
247 break;
Cary Clark8032b982017-07-28 11:04:54 -0400248 case MarkType::kParam: {
Cary Clark8032b982017-07-28 11:04:54 -0400249 TextParser paramParser(def->fFileName, def->fStart, def->fContentStart,
250 def->fLineCount);
251 paramParser.skipWhiteSpace();
252 SkASSERT(paramParser.startsWith("#Param"));
253 paramParser.next(); // skip hash
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400254 paramParser.skipToNonName(); // skip Param
Cary Clark8032b982017-07-28 11:04:54 -0400255 paramParser.skipSpace();
256 const char* paramName = paramParser.fChar;
257 paramParser.skipToSpace();
258 fInCode = true;
259 this->wordCheck(paramParser.fChar - paramName, paramName);
260 fInCode = false;
Cary Clark682c58d2018-05-16 07:07:07 -0400261 } break;
262 case MarkType::kPhraseDef:
263 break;
264 case MarkType::kPhraseParam:
265 break;
266 case MarkType::kPhraseRef:
267 break;
Cary Clark8032b982017-07-28 11:04:54 -0400268 case MarkType::kPlatform:
269 break;
Cary Clark4855f782018-02-06 09:41:53 -0500270 case MarkType::kPopulate:
271 break;
Cary Clarkce101242017-09-01 15:51:02 -0400272 case MarkType::kPrivate:
273 break;
Cary Clark8032b982017-07-28 11:04:54 -0400274 case MarkType::kReturn:
275 break;
276 case MarkType::kRow:
277 break;
278 case MarkType::kSeeAlso:
279 break;
Cary Clark4855f782018-02-06 09:41:53 -0500280 case MarkType::kSet:
281 break;
Cary Clark8032b982017-07-28 11:04:54 -0400282 case MarkType::kStdOut: {
283 fInStdOut = true;
284 TextParser code(def);
285 code.skipSpace();
286 while (!code.eof()) {
287 const char* end = code.trimmedLineEnd();
288 this->wordCheck(end - code.fChar, code.fChar);
289 code.skipToLineStart();
290 }
291 fInStdOut = false;
292 } break;
293 case MarkType::kStruct:
294 fRoot = def->asRoot();
295 this->wordCheck(def->fName);
296 break;
Cary Clarkce101242017-09-01 15:51:02 -0400297 case MarkType::kSubstitute:
298 break;
Cary Clark8032b982017-07-28 11:04:54 -0400299 case MarkType::kSubtopic:
Cary Clark682c58d2018-05-16 07:07:07 -0400300 // TODO: add a tag that allows subtopic labels in illustrations to skip spellcheck?
301 if (string::npos == fFileName.find("illustrations.bmh")) {
302 this->printCheck(printable, PrintCheck::kAllowNumbers);
303 }
Cary Clark8032b982017-07-28 11:04:54 -0400304 break;
305 case MarkType::kTable:
306 break;
307 case MarkType::kTemplate:
308 break;
309 case MarkType::kText:
310 break;
Cary Clark8032b982017-07-28 11:04:54 -0400311 case MarkType::kToDo:
312 break;
313 case MarkType::kTopic:
Cary Clark682c58d2018-05-16 07:07:07 -0400314 this->printCheck(printable, PrintCheck::kWordsOnly);
Cary Clark8032b982017-07-28 11:04:54 -0400315 break;
Cary Clark8032b982017-07-28 11:04:54 -0400316 case MarkType::kTypedef:
317 break;
318 case MarkType::kUnion:
319 break;
Cary Clarkce101242017-09-01 15:51:02 -0400320 case MarkType::kVolatile:
321 break;
Cary Clark8032b982017-07-28 11:04:54 -0400322 case MarkType::kWidth:
323 break;
324 default:
325 SkASSERT(0); // handle everything
326 break;
327 }
328 this->childCheck(def, textStart);
329 switch (def->fMarkType) { // post child work, at least for tables
330 case MarkType::kCode:
331 fInCode = false;
332 break;
333 case MarkType::kColumn:
334 break;
335 case MarkType::kDescription:
336 fInDescription = false;
337 break;
338 case MarkType::kEnum:
339 case MarkType::kEnumClass:
340 break;
341 case MarkType::kExample:
342 break;
Cary Clarkce101242017-09-01 15:51:02 -0400343 case MarkType::kFormula:
344 fInFormula = false;
345 break;
Cary Clark8032b982017-07-28 11:04:54 -0400346 case MarkType::kLegend:
347 break;
348 case MarkType::kMethod:
349 fMethod = nullptr;
350 break;
351 case MarkType::kConst:
352 fInConst = false;
353 case MarkType::kParam:
Cary Clark8032b982017-07-28 11:04:54 -0400354 break;
355 case MarkType::kReturn:
356 case MarkType::kSeeAlso:
357 break;
358 case MarkType::kRow:
359 break;
360 case MarkType::kStruct:
361 fRoot = fRoot->rootParent();
362 break;
363 case MarkType::kTable:
364 break;
365 default:
366 break;
367 }
368 return true;
369}
370
371bool SpellCheck::checkable(MarkType markType) {
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400372 return BmhParser::Resolvable::kYes == fBmhParser.kMarkProps[(int) markType].fResolve;
Cary Clark8032b982017-07-28 11:04:54 -0400373}
374
Cary Clark682c58d2018-05-16 07:07:07 -0400375void SpellCheck::childCheck(Definition* def, const char* start) {
Cary Clark8032b982017-07-28 11:04:54 -0400376 const char* end;
377 fLineCount = def->fLineCount;
378 if (def->isRoot()) {
Cary Clark682c58d2018-05-16 07:07:07 -0400379 fRoot = def->asRoot();
Cary Clark8032b982017-07-28 11:04:54 -0400380 }
381 for (auto& child : def->fChildren) {
382 end = child->fStart;
383 if (this->checkable(def->fMarkType)) {
384 this->leafCheck(start, end);
385 }
386 this->check(child);
387 start = child->fTerminator;
388 }
389 if (this->checkable(def->fMarkType)) {
390 end = def->fContentEnd;
391 this->leafCheck(start, end);
392 }
393}
394
395void SpellCheck::leafCheck(const char* start, const char* end) {
Cary Clarkce101242017-09-01 15:51:02 -0400396 const char* chPtr = start;
397 int inAngles = 0;
398 int inParens = 0;
399 bool inQuotes = false;
400 bool allLower = true;
Cary Clark80247e52018-07-11 16:18:41 -0400401 char prePriorCh = 0;
Cary Clarkce101242017-09-01 15:51:02 -0400402 char priorCh = 0;
403 char lastCh = 0;
404 const char* wordStart = nullptr;
405 const char* wordEnd = nullptr;
406 const char* possibleEnd = nullptr;
Cary Clark4855f782018-02-06 09:41:53 -0500407 fLocalLine = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400408 do {
Cary Clarkce101242017-09-01 15:51:02 -0400409 if (wordStart && wordEnd) {
410 if (!allLower || (!inQuotes && '\"' != lastCh && !inParens
411 && ')' != lastCh && !inAngles && '>' != lastCh)) {
412 string word(wordStart, (possibleEnd ? possibleEnd : wordEnd) - wordStart);
Cary Clark80247e52018-07-11 16:18:41 -0400413 if ("e" != word || !isdigit(prePriorCh) || ('+' != lastCh &&
414 '-' != lastCh && !isdigit(lastCh))) {
415 this->wordCheck(word);
416 }
Cary Clarkce101242017-09-01 15:51:02 -0400417 }
418 wordStart = nullptr;
419 }
420 if (chPtr == end) {
Cary Clark8032b982017-07-28 11:04:54 -0400421 break;
422 }
Cary Clarkce101242017-09-01 15:51:02 -0400423 switch (*chPtr) {
424 case '>':
425 if (isalpha(lastCh)) {
426 --inAngles;
427 SkASSERT(inAngles >= 0);
428 }
429 wordEnd = chPtr;
430 break;
431 case '(':
432 ++inParens;
433 possibleEnd = chPtr;
434 break;
435 case ')':
436 --inParens;
437 if ('(' == lastCh) {
438 wordEnd = chPtr + 1;
439 } else {
440 wordEnd = chPtr;
441 }
Cary Clark7fc1d122017-10-09 14:07:42 -0400442 SkASSERT(inParens >= 0 || fInStdOut);
Cary Clarkce101242017-09-01 15:51:02 -0400443 break;
444 case '\"':
445 inQuotes = !inQuotes;
446 wordEnd = chPtr;
447 SkASSERT(inQuotes == !wordStart);
448 break;
449 case 'A': case 'B': case 'C': case 'D': case 'E':
450 case 'F': case 'G': case 'H': case 'I': case 'J':
451 case 'K': case 'L': case 'M': case 'N': case 'O':
452 case 'P': case 'Q': case 'R': case 'S': case 'T':
453 case 'U': case 'V': case 'W': case 'X': case 'Y':
454 case 'Z':
455 allLower = false;
456 case 'a': case 'b': case 'c': case 'd': case 'e':
457 case 'f': case 'g': case 'h': case 'i': case 'j':
458 case 'k': case 'l': case 'm': case 'n': case 'o':
459 case 'p': case 'q': case 'r': case 's': case 't':
460 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -0400461 case 'z':
Cary Clarkce101242017-09-01 15:51:02 -0400462 if (!wordStart) {
463 wordStart = chPtr;
464 wordEnd = nullptr;
465 possibleEnd = nullptr;
466 allLower = 'a' <= *chPtr;
467 if ('<' == lastCh || ('<' == priorCh && '/' == lastCh)) {
468 ++inAngles;
469 }
470 }
471 break;
472 case '0': case '1': case '2': case '3': case '4':
473 case '5': case '6': case '7': case '8': case '9':
Ben Wagner63fd7602017-10-09 15:45:33 -0400474 case '_':
Cary Clarkce101242017-09-01 15:51:02 -0400475 allLower = false;
476 case '-': // note that dash doesn't clear allLower
477 break;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400478 case '!':
479 if (!inQuotes) {
480 wordEnd = chPtr;
481 }
482 break;
Cary Clark4855f782018-02-06 09:41:53 -0500483 case '\n':
484 ++fLocalLine;
485 // fall through
Cary Clarkce101242017-09-01 15:51:02 -0400486 default:
487 wordEnd = chPtr;
488 break;
489 }
Cary Clark80247e52018-07-11 16:18:41 -0400490 prePriorCh = priorCh;
Cary Clarkce101242017-09-01 15:51:02 -0400491 priorCh = lastCh;
492 lastCh = *chPtr;
493 } while (++chPtr <= end);
Cary Clark8032b982017-07-28 11:04:54 -0400494}
495
Cary Clark682c58d2018-05-16 07:07:07 -0400496void SpellCheck::printCheck(string str, PrintCheck allowed) {
Cary Clark8032b982017-07-28 11:04:54 -0400497 string word;
498 for (std::stringstream stream(str); stream >> word; ) {
Cary Clark682c58d2018-05-16 07:07:07 -0400499 if (PrintCheck::kAllowNumbers == allowed && (std::isdigit(word.back()) || 'x' == word.back())) {
500 // allow ###x for RGB_888x
501 if ((size_t) std::count_if(word.begin(), word.end() - 1,
502 [](unsigned char c){ return std::isdigit(c); } ) == word.length() - 1) {
503 continue;
504 }
505 }
Cary Clark8032b982017-07-28 11:04:54 -0400506 wordCheck(word);
507 }
508}
509
Cary Clarkce101242017-09-01 15:51:02 -0400510static bool stringCompare(const std::pair<string, CheckEntry>& i, const std::pair<string, CheckEntry>& j) {
511 return i.first.compare(j.first) < 0;
512}
513
514void SpellCheck::report(SkCommandLineFlags::StringArray report) {
515 vector<std::pair<string, CheckEntry>> elems(fWords.begin(), fWords.end());
516 std::sort(elems.begin(), elems.end(), stringCompare);
517 if (report.contains("once")) {
518 for (auto iter : elems) {
Cary Clark5538c132018-06-14 12:28:14 -0400519 if (iter.second.fOverride) {
Cary Clarkce101242017-09-01 15:51:02 -0400520 continue;
521 }
522 if (iter.second.fCount == 1) {
Cary Clark4855f782018-02-06 09:41:53 -0500523 string fullName = this->ReportFilename(iter.second.fFile);
524 SkDebugf("%s(%d): %s\n", fullName.c_str(), iter.second.fLine,
Cary Clarkce101242017-09-01 15:51:02 -0400525 iter.first.c_str());
526 }
Cary Clark8032b982017-07-28 11:04:54 -0400527 }
Cary Clarkce101242017-09-01 15:51:02 -0400528 SkDebugf("\n");
Cary Clarkd0530ba2017-09-14 11:25:39 -0400529 return;
Cary Clarkce101242017-09-01 15:51:02 -0400530 }
531 if (report.contains("all")) {
532 int column = 0;
Cary Clarkd0530ba2017-09-14 11:25:39 -0400533 char lastInitial = 'a';
Cary Clark154beea2017-10-26 07:58:48 -0400534 int count = 0;
Cary Clarkce101242017-09-01 15:51:02 -0400535 for (auto iter : elems) {
Cary Clark5538c132018-06-14 12:28:14 -0400536 if (iter.second.fOverride) {
Cary Clarkce101242017-09-01 15:51:02 -0400537 continue;
538 }
539 string check = iter.first.c_str();
540 bool allLower = true;
541 for (auto c : check) {
542 if (isupper(c)) {
543 allLower = false;
544 break;
545 }
546 }
547 if (!allLower) {
548 continue;
549 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400550 if (column + check.length() > 100 || check[0] != lastInitial) {
Cary Clarkce101242017-09-01 15:51:02 -0400551 SkDebugf("\n");
552 column = 0;
553 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400554 if (check[0] != lastInitial) {
555 SkDebugf("\n");
556 lastInitial = check[0];
557 }
Cary Clarkce101242017-09-01 15:51:02 -0400558 SkDebugf("%s ", check.c_str());
559 column += check.length();
Cary Clark154beea2017-10-26 07:58:48 -0400560 ++count;
Cary Clark8032b982017-07-28 11:04:54 -0400561 }
Cary Clark154beea2017-10-26 07:58:48 -0400562 SkDebugf("\n\ncount = %d\n", count);
Cary Clarkd0530ba2017-09-14 11:25:39 -0400563 return;
Cary Clarkce101242017-09-01 15:51:02 -0400564 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400565 int index = 0;
566 const char* mispelled = report[0];
567 for (auto iter : elems) {
Cary Clark5538c132018-06-14 12:28:14 -0400568 if (iter.second.fOverride) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400569 continue;
570 }
571 string check = iter.first.c_str();
572 while (check.compare(mispelled) > 0) {
573 SkDebugf("%s not found\n", mispelled);
574 if (report.count() == ++index) {
Cary Clarkce101242017-09-01 15:51:02 -0400575 break;
576 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400577 }
578 if (report.count() == index) {
579 break;
580 }
581 if (check.compare(mispelled) == 0) {
Cary Clark4855f782018-02-06 09:41:53 -0500582 string fullName = this->ReportFilename(iter.second.fFile);
583 SkDebugf("%s(%d): %s\n", fullName.c_str(), iter.second.fLine,
Cary Clarkd0530ba2017-09-14 11:25:39 -0400584 iter.first.c_str());
585 if (report.count() == ++index) {
586 break;
Cary Clarkce101242017-09-01 15:51:02 -0400587 }
Cary Clark8032b982017-07-28 11:04:54 -0400588 }
589 }
590}
591
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400592void SpellCheck::wordCheck(string str) {
Cary Clarkce101242017-09-01 15:51:02 -0400593 if ("nullptr" == str) {
594 return; // doesn't seem worth it, treating nullptr as a word in need of correction
595 }
Cary Clark8032b982017-07-28 11:04:54 -0400596 bool hasColon = false;
597 bool hasDot = false;
598 bool hasParen = false;
599 bool hasUnderscore = false;
600 bool sawDash = false;
601 bool sawDigit = false;
602 bool sawSpecial = false;
603 SkASSERT(str.length() > 0);
604 SkASSERT(isalpha(str[0]) || '~' == str[0]);
605 for (char ch : str) {
606 if (isalpha(ch) || '-' == ch) {
607 sawDash |= '-' == ch;
608 continue;
609 }
610 bool isColon = ':' == ch;
611 hasColon |= isColon;
612 bool isDot = '.' == ch;
613 hasDot |= isDot;
Cary Clarkbc5697d2017-10-04 14:31:33 -0400614 bool isParen = '(' == ch || ')' == ch || '~' == ch || '=' == ch || '!' == ch ||
615 '[' == ch || ']' == ch;
Cary Clark8032b982017-07-28 11:04:54 -0400616 hasParen |= isParen;
617 bool isUnderscore = '_' == ch;
618 hasUnderscore |= isUnderscore;
619 if (isColon || isDot || isUnderscore || isParen) {
620 continue;
621 }
622 if (isdigit(ch)) {
623 sawDigit = true;
624 continue;
625 }
626 if ('&' == ch || ',' == ch || ' ' == ch) {
627 sawSpecial = true;
628 continue;
629 }
630 SkASSERT(0);
631 }
632 if (sawSpecial && !hasParen) {
633 SkASSERT(0);
634 }
635 bool inCode = fInCode;
636 if (hasUnderscore && isupper(str[0]) && ('S' != str[0] || 'K' != str[1])
Ben Wagner63fd7602017-10-09 15:45:33 -0400637 && !hasColon && !hasDot && !hasParen && !fInStdOut && !inCode && !fInConst
Cary Clark8032b982017-07-28 11:04:54 -0400638 && !sawDigit && !sawSpecial && !sawDash) {
639 std::istringstream ss(str);
640 string token;
641 while (std::getline(ss, token, '_')) {
Cary Clarka560c472017-11-27 10:44:06 -0500642 if (token.length()) {
643 this->wordCheck(token);
644 }
Cary Clark8032b982017-07-28 11:04:54 -0400645 }
646 return;
647 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400648 if (!hasColon && !hasDot && !hasParen && !hasUnderscore
Cary Clark8032b982017-07-28 11:04:54 -0400649 && !fInStdOut && !inCode && !fInConst && !sawDigit
650 && islower(str[0]) && isupper(str[1])) {
651 inCode = true;
652 }
Cary Clarkce101242017-09-01 15:51:02 -0400653 bool methodParam = false;
654 if (fMethod) {
655 for (auto child : fMethod->fChildren) {
656 if (MarkType::kParam == child->fMarkType && str == child->fName) {
657 methodParam = true;
658 break;
659 }
660 }
661 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400662 auto& mappy = hasColon ? fColons :
Cary Clark8032b982017-07-28 11:04:54 -0400663 hasDot ? fDots :
664 hasParen ? fParens :
665 hasUnderscore ? fUnderscores :
Cary Clarkce101242017-09-01 15:51:02 -0400666 fInStdOut || fInFormula || inCode || fInConst || methodParam ? fCode :
Cary Clark8032b982017-07-28 11:04:54 -0400667 sawDigit ? fDigits : fWords;
668 auto iter = mappy.find(str);
669 if (mappy.end() != iter) {
Cary Clark5538c132018-06-14 12:28:14 -0400670 if (iter->second.fOverride && !fOverride) {
671 iter->second.fFile = fFileName;
672 iter->second.fLine = fLineCount + fLocalLine;
673 iter->second.fOverride = false;
674 }
Cary Clark8032b982017-07-28 11:04:54 -0400675 iter->second.fCount += 1;
676 } else {
677 CheckEntry* entry = &mappy[str];
678 entry->fFile = fFileName;
Cary Clark4855f782018-02-06 09:41:53 -0500679 entry->fLine = fLineCount + fLocalLine;
Cary Clark8032b982017-07-28 11:04:54 -0400680 entry->fCount = 1;
Cary Clark5538c132018-06-14 12:28:14 -0400681 entry->fOverride = fOverride;
Cary Clark8032b982017-07-28 11:04:54 -0400682 }
683}
684
685void SpellCheck::wordCheck(ptrdiff_t len, const char* ch) {
686 leafCheck(ch, ch + len);
687}