blob: c8de4a3953ee143392619ea1aa4f86d917951a76 [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
Cary Clark2da9fb82018-11-01 09:29:36 -04008#include "bmhParser.h"
Cary Clark8032b982017-07-28 11:04:54 -04009
Cary Clark2da9fb82018-11-01 09:29:36 -040010#include "SkCommandLineFlags.h"
Cary Clark8032b982017-07-28 11:04:54 -040011#include "SkOSFile.h"
12#include "SkOSPath.h"
13
Ben Wagner63fd7602017-10-09 15:45:33 -040014/*
Cary Clark8032b982017-07-28 11:04:54 -040015things to do
16if cap word is beginning of sentence, add it to table as lower-case
17 word must have only a single initial capital
18
19if word is camel cased, look for :: matches on suffix
20
21when function crosses lines, whole thing isn't seen as a 'word' e.g., search for largeArc in path
22
23words in external not seen
Cary Clark80247e52018-07-11 16:18:41 -040024
25look for x-bit but allow x bits
Cary Clarkd2ca79c2018-08-10 13:09:13 -040026
27don't treat 'pos' or 'glyphs' as spell-checkable as in 'RunBuffer.pos' or 'RunBuffer.glyphs'
Cary Clark8032b982017-07-28 11:04:54 -040028 */
Cary Clark80247e52018-07-11 16:18:41 -040029
Cary Clark8032b982017-07-28 11:04:54 -040030struct CheckEntry {
31 string fFile;
32 int fLine;
33 int fCount;
Cary Clark5538c132018-06-14 12:28:14 -040034 bool fOverride;
Cary Clark8032b982017-07-28 11:04:54 -040035};
36
37class SpellCheck : public ParserCommon {
38public:
39 SpellCheck(const BmhParser& bmh) : ParserCommon()
40 , fBmhParser(bmh) {
41 this->reset();
42 }
43 bool check(const char* match);
Cary Clarkce101242017-09-01 15:51:02 -040044 void report(SkCommandLineFlags::StringArray report);
Cary Clark8032b982017-07-28 11:04:54 -040045private:
46 enum class TableState {
47 kNone,
48 kRow,
49 kColumn,
50 };
51
Cary Clark682c58d2018-05-16 07:07:07 -040052 enum class PrintCheck {
53 kWordsOnly,
54 kAllowNumbers,
55 };
56
Cary Clark8032b982017-07-28 11:04:54 -040057 bool check(Definition* );
58 bool checkable(MarkType markType);
Cary Clark682c58d2018-05-16 07:07:07 -040059 void childCheck(Definition* def, const char* start);
Cary Clark8032b982017-07-28 11:04:54 -040060 void leafCheck(const char* start, const char* end);
61 bool parseFromFile(const char* path) override { return true; }
Cary Clark682c58d2018-05-16 07:07:07 -040062 void printCheck(string str, PrintCheck);
Cary Clark8032b982017-07-28 11:04:54 -040063
64 void reset() override {
65 INHERITED::resetCommon();
66 fMethod = nullptr;
67 fRoot = nullptr;
Cary Clark8032b982017-07-28 11:04:54 -040068 fInCode = false;
69 fInConst = false;
Cary Clarkce101242017-09-01 15:51:02 -040070 fInFormula = false;
Cary Clark8032b982017-07-28 11:04:54 -040071 fInDescription = false;
72 fInStdOut = false;
Cary Clark5538c132018-06-14 12:28:14 -040073 fOverride = false;
Cary Clark8032b982017-07-28 11:04:54 -040074 }
75
Cary Clark2d4bf5f2018-04-16 08:37:38 -040076 void wordCheck(string str);
Cary Clark8032b982017-07-28 11:04:54 -040077 void wordCheck(ptrdiff_t len, const char* ch);
78
79 unordered_map<string, CheckEntry> fCode;
80 unordered_map<string, CheckEntry> fColons;
81 unordered_map<string, CheckEntry> fDigits;
82 unordered_map<string, CheckEntry> fDots;
83 unordered_map<string, CheckEntry> fParens; // also hold destructors, operators
84 unordered_map<string, CheckEntry> fUnderscores;
85 unordered_map<string, CheckEntry> fWords;
86 const BmhParser& fBmhParser;
87 Definition* fMethod;
88 RootDefinition* fRoot;
Cary Clark4855f782018-02-06 09:41:53 -050089 int fLocalLine;
Cary Clark8032b982017-07-28 11:04:54 -040090 bool fInCode;
91 bool fInConst;
92 bool fInDescription;
Cary Clarkce101242017-09-01 15:51:02 -040093 bool fInFormula;
Cary Clark8032b982017-07-28 11:04:54 -040094 bool fInStdOut;
Cary Clark5538c132018-06-14 12:28:14 -040095 bool fOverride;
Cary Clark8032b982017-07-28 11:04:54 -040096 typedef ParserCommon INHERITED;
97};
98
99/* This doesn't perform a traditional spell or grammar check, although
100 maybe it should. Instead it looks for words used uncommonly and lower
101 case words that match capitalized words that are not sentence starters.
102 It also looks for articles preceeding capitalized words and their
103 modifiers to try to maintain a consistent voice.
104 Maybe also look for passive verbs (e.g. 'is') and suggest active ones?
105 */
Cary Clarkce101242017-09-01 15:51:02 -0400106void BmhParser::spellCheck(const char* match, SkCommandLineFlags::StringArray report) const {
Cary Clark8032b982017-07-28 11:04:54 -0400107 SpellCheck checker(*this);
108 checker.check(match);
Cary Clarkce101242017-09-01 15:51:02 -0400109 checker.report(report);
Cary Clark8032b982017-07-28 11:04:54 -0400110}
111
Cary Clark2f466242017-12-11 16:03:17 -0500112void BmhParser::spellStatus(const char* statusFile, SkCommandLineFlags::StringArray report) const {
113 SpellCheck checker(*this);
114 StatusIter iter(statusFile, ".bmh", StatusFilter::kInProgress);
Cary Clark7724d3f2018-09-14 09:12:38 -0400115 string file;
Cary Clark61313f32018-10-08 14:57:48 -0400116 iter.next(&file, nullptr);
Cary Clark2f466242017-12-11 16:03:17 -0500117 string match = iter.baseDir();
118 checker.check(match.c_str());
119 checker.report(report);
120}
121
Cary Clark8032b982017-07-28 11:04:54 -0400122bool SpellCheck::check(const char* match) {
123 for (const auto& topic : fBmhParser.fTopicMap) {
124 Definition* topicDef = topic.second;
125 if (topicDef->fParent) {
126 continue;
127 }
128 if (!topicDef->isRoot()) {
129 return this->reportError<bool>("expected root topic");
130 }
131 fRoot = topicDef->asRoot();
132 if (string::npos == fRoot->fFileName.rfind(match)) {
133 continue;
134 }
Cary Clark5538c132018-06-14 12:28:14 -0400135 fOverride = string::npos != fRoot->fFileName.rfind("undocumented.bmh")
136 || string::npos != fRoot->fFileName.rfind("markup.bmh")
137 || string::npos != fRoot->fFileName.rfind("usingBookmaker.bmh");
138 this->check(topicDef);
Cary Clark8032b982017-07-28 11:04:54 -0400139 }
140 return true;
141}
142
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400143static bool all_lower(string str) {
Cary Clarkce101242017-09-01 15:51:02 -0400144 for (auto c : str) {
145 if (!islower(c)) {
146 return false;
147 }
148 }
149 return true;
150}
151
Cary Clark8032b982017-07-28 11:04:54 -0400152bool SpellCheck::check(Definition* def) {
153 fFileName = def->fFileName;
154 fLineCount = def->fLineCount;
155 string printable = def->printableName();
156 const char* textStart = def->fContentStart;
Cary Clark8032b982017-07-28 11:04:54 -0400157 switch (def->fMarkType) {
158 case MarkType::kAlias:
159 break;
160 case MarkType::kAnchor:
161 break;
162 case MarkType::kBug:
163 break;
164 case MarkType::kClass:
165 this->wordCheck(def->fName);
166 break;
167 case MarkType::kCode:
168 fInCode = true;
169 break;
170 case MarkType::kColumn:
171 break;
172 case MarkType::kComment:
173 break;
174 case MarkType::kConst: {
175 fInConst = true;
Cary Clark8032b982017-07-28 11:04:54 -0400176 this->wordCheck(def->fName);
177 const char* lineEnd = strchr(textStart, '\n');
178 this->wordCheck(lineEnd - textStart, textStart);
179 textStart = lineEnd;
180 } break;
181 case MarkType::kDefine:
182 break;
Cary Clark8032b982017-07-28 11:04:54 -0400183 case MarkType::kDescription:
184 fInDescription = true;
185 break;
Cary Clark682c58d2018-05-16 07:07:07 -0400186 case MarkType::kDetails:
Cary Clark8032b982017-07-28 11:04:54 -0400187 break;
Cary Clarkac47b882018-01-11 10:35:44 -0500188 case MarkType::kDuration:
189 break;
Cary Clark8032b982017-07-28 11:04:54 -0400190 case MarkType::kEnum:
191 case MarkType::kEnumClass:
192 this->wordCheck(def->fName);
193 break;
Cary Clark8032b982017-07-28 11:04:54 -0400194 case MarkType::kExample:
195 break;
196 case MarkType::kExternal:
197 break;
Cary Clark0d225392018-06-07 09:59:07 -0400198 case MarkType::kFile:
199 break;
Cary Clarka90ea222018-10-16 10:30:28 -0400200 case MarkType::kFilter:
201 break;
Cary Clark8032b982017-07-28 11:04:54 -0400202 case MarkType::kFormula:
Cary Clarkce101242017-09-01 15:51:02 -0400203 fInFormula = true;
Cary Clark8032b982017-07-28 11:04:54 -0400204 break;
205 case MarkType::kFunction:
206 break;
207 case MarkType::kHeight:
208 break;
Cary Clarkf895a422018-02-27 09:54:21 -0500209 case MarkType::kIllustration:
210 break;
Cary Clark8032b982017-07-28 11:04:54 -0400211 case MarkType::kImage:
212 break;
Cary Clark4855f782018-02-06 09:41:53 -0500213 case MarkType::kIn:
214 break;
Cary Clark8032b982017-07-28 11:04:54 -0400215 case MarkType::kLegend:
216 break;
Cary Clark4855f782018-02-06 09:41:53 -0500217 case MarkType::kLine:
218 break;
Cary Clarkce101242017-09-01 15:51:02 -0400219 case MarkType::kLink:
220 break;
Cary Clark8032b982017-07-28 11:04:54 -0400221 case MarkType::kList:
222 break;
Cary Clark154beea2017-10-26 07:58:48 -0400223 case MarkType::kLiteral:
224 break;
Cary Clarkce101242017-09-01 15:51:02 -0400225 case MarkType::kMarkChar:
226 break;
Cary Clark8032b982017-07-28 11:04:54 -0400227 case MarkType::kMember:
228 break;
229 case MarkType::kMethod: {
230 string method_name = def->methodName();
Cary Clarkce101242017-09-01 15:51:02 -0400231 if (all_lower(method_name)) {
232 method_name += "()";
233 }
Cary Clarka560c472017-11-27 10:44:06 -0500234 if (!def->isClone() && Definition::MethodType::kOperator != def->fMethodType) {
Cary Clark8032b982017-07-28 11:04:54 -0400235 this->wordCheck(method_name);
236 }
Cary Clark8032b982017-07-28 11:04:54 -0400237 fMethod = def;
238 } break;
Cary Clarkce101242017-09-01 15:51:02 -0400239 case MarkType::kNoExample:
240 break;
Cary Clark682c58d2018-05-16 07:07:07 -0400241 case MarkType::kNoJustify:
242 break;
Cary Clark154beea2017-10-26 07:58:48 -0400243 case MarkType::kOutdent:
244 break;
Cary Clark8032b982017-07-28 11:04:54 -0400245 case MarkType::kParam: {
Cary Clark8032b982017-07-28 11:04:54 -0400246 TextParser paramParser(def->fFileName, def->fStart, def->fContentStart,
247 def->fLineCount);
248 paramParser.skipWhiteSpace();
249 SkASSERT(paramParser.startsWith("#Param"));
250 paramParser.next(); // skip hash
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400251 paramParser.skipToNonName(); // skip Param
Cary Clark8032b982017-07-28 11:04:54 -0400252 paramParser.skipSpace();
253 const char* paramName = paramParser.fChar;
254 paramParser.skipToSpace();
255 fInCode = true;
256 this->wordCheck(paramParser.fChar - paramName, paramName);
257 fInCode = false;
Cary Clark682c58d2018-05-16 07:07:07 -0400258 } break;
259 case MarkType::kPhraseDef:
260 break;
261 case MarkType::kPhraseParam:
262 break;
263 case MarkType::kPhraseRef:
264 break;
Cary Clark8032b982017-07-28 11:04:54 -0400265 case MarkType::kPlatform:
266 break;
Cary Clark4855f782018-02-06 09:41:53 -0500267 case MarkType::kPopulate:
268 break;
Cary Clark8032b982017-07-28 11:04:54 -0400269 case MarkType::kReturn:
270 break;
271 case MarkType::kRow:
272 break;
273 case MarkType::kSeeAlso:
274 break;
Cary Clark4855f782018-02-06 09:41:53 -0500275 case MarkType::kSet:
276 break;
Cary Clark8032b982017-07-28 11:04:54 -0400277 case MarkType::kStdOut: {
278 fInStdOut = true;
279 TextParser code(def);
280 code.skipSpace();
281 while (!code.eof()) {
282 const char* end = code.trimmedLineEnd();
283 this->wordCheck(end - code.fChar, code.fChar);
284 code.skipToLineStart();
285 }
286 fInStdOut = false;
287 } break;
288 case MarkType::kStruct:
289 fRoot = def->asRoot();
290 this->wordCheck(def->fName);
291 break;
Cary Clarkce101242017-09-01 15:51:02 -0400292 case MarkType::kSubstitute:
293 break;
Cary Clark8032b982017-07-28 11:04:54 -0400294 case MarkType::kSubtopic:
Cary Clark682c58d2018-05-16 07:07:07 -0400295 // TODO: add a tag that allows subtopic labels in illustrations to skip spellcheck?
296 if (string::npos == fFileName.find("illustrations.bmh")) {
297 this->printCheck(printable, PrintCheck::kAllowNumbers);
298 }
Cary Clark8032b982017-07-28 11:04:54 -0400299 break;
300 case MarkType::kTable:
301 break;
302 case MarkType::kTemplate:
303 break;
304 case MarkType::kText:
305 break;
Cary Clark8032b982017-07-28 11:04:54 -0400306 case MarkType::kToDo:
307 break;
308 case MarkType::kTopic:
Cary Clark682c58d2018-05-16 07:07:07 -0400309 this->printCheck(printable, PrintCheck::kWordsOnly);
Cary Clark8032b982017-07-28 11:04:54 -0400310 break;
Cary Clark8032b982017-07-28 11:04:54 -0400311 case MarkType::kTypedef:
312 break;
313 case MarkType::kUnion:
314 break;
Cary Clarkce101242017-09-01 15:51:02 -0400315 case MarkType::kVolatile:
316 break;
Cary Clark8032b982017-07-28 11:04:54 -0400317 case MarkType::kWidth:
318 break;
319 default:
320 SkASSERT(0); // handle everything
321 break;
322 }
323 this->childCheck(def, textStart);
324 switch (def->fMarkType) { // post child work, at least for tables
325 case MarkType::kCode:
326 fInCode = false;
327 break;
328 case MarkType::kColumn:
329 break;
330 case MarkType::kDescription:
331 fInDescription = false;
332 break;
333 case MarkType::kEnum:
334 case MarkType::kEnumClass:
335 break;
336 case MarkType::kExample:
337 break;
Cary Clarkce101242017-09-01 15:51:02 -0400338 case MarkType::kFormula:
339 fInFormula = false;
340 break;
Cary Clark8032b982017-07-28 11:04:54 -0400341 case MarkType::kLegend:
342 break;
343 case MarkType::kMethod:
344 fMethod = nullptr;
345 break;
346 case MarkType::kConst:
347 fInConst = false;
348 case MarkType::kParam:
Cary Clark8032b982017-07-28 11:04:54 -0400349 break;
350 case MarkType::kReturn:
351 case MarkType::kSeeAlso:
352 break;
353 case MarkType::kRow:
354 break;
355 case MarkType::kStruct:
356 fRoot = fRoot->rootParent();
357 break;
358 case MarkType::kTable:
359 break;
360 default:
361 break;
362 }
363 return true;
364}
365
366bool SpellCheck::checkable(MarkType markType) {
Cary Clark2da9fb82018-11-01 09:29:36 -0400367 return Resolvable::kYes == fBmhParser.kMarkProps[(int) markType].fResolve;
Cary Clark8032b982017-07-28 11:04:54 -0400368}
369
Cary Clark682c58d2018-05-16 07:07:07 -0400370void SpellCheck::childCheck(Definition* def, const char* start) {
Cary Clark8032b982017-07-28 11:04:54 -0400371 const char* end;
372 fLineCount = def->fLineCount;
373 if (def->isRoot()) {
Cary Clark682c58d2018-05-16 07:07:07 -0400374 fRoot = def->asRoot();
Cary Clark8032b982017-07-28 11:04:54 -0400375 }
376 for (auto& child : def->fChildren) {
377 end = child->fStart;
378 if (this->checkable(def->fMarkType)) {
379 this->leafCheck(start, end);
380 }
381 this->check(child);
382 start = child->fTerminator;
383 }
384 if (this->checkable(def->fMarkType)) {
385 end = def->fContentEnd;
386 this->leafCheck(start, end);
387 }
388}
389
390void SpellCheck::leafCheck(const char* start, const char* end) {
Cary Clarkce101242017-09-01 15:51:02 -0400391 const char* chPtr = start;
392 int inAngles = 0;
393 int inParens = 0;
394 bool inQuotes = false;
395 bool allLower = true;
Cary Clark80247e52018-07-11 16:18:41 -0400396 char prePriorCh = 0;
Cary Clarkce101242017-09-01 15:51:02 -0400397 char priorCh = 0;
398 char lastCh = 0;
399 const char* wordStart = nullptr;
400 const char* wordEnd = nullptr;
401 const char* possibleEnd = nullptr;
Cary Clark4855f782018-02-06 09:41:53 -0500402 fLocalLine = 0;
Cary Clark8032b982017-07-28 11:04:54 -0400403 do {
Cary Clarkce101242017-09-01 15:51:02 -0400404 if (wordStart && wordEnd) {
405 if (!allLower || (!inQuotes && '\"' != lastCh && !inParens
406 && ')' != lastCh && !inAngles && '>' != lastCh)) {
407 string word(wordStart, (possibleEnd ? possibleEnd : wordEnd) - wordStart);
Cary Clark80247e52018-07-11 16:18:41 -0400408 if ("e" != word || !isdigit(prePriorCh) || ('+' != lastCh &&
409 '-' != lastCh && !isdigit(lastCh))) {
410 this->wordCheck(word);
411 }
Cary Clarkce101242017-09-01 15:51:02 -0400412 }
413 wordStart = nullptr;
414 }
415 if (chPtr == end) {
Cary Clark8032b982017-07-28 11:04:54 -0400416 break;
417 }
Cary Clarkce101242017-09-01 15:51:02 -0400418 switch (*chPtr) {
419 case '>':
420 if (isalpha(lastCh)) {
421 --inAngles;
422 SkASSERT(inAngles >= 0);
423 }
424 wordEnd = chPtr;
425 break;
426 case '(':
427 ++inParens;
428 possibleEnd = chPtr;
429 break;
430 case ')':
431 --inParens;
432 if ('(' == lastCh) {
433 wordEnd = chPtr + 1;
434 } else {
435 wordEnd = chPtr;
436 }
Cary Clark7fc1d122017-10-09 14:07:42 -0400437 SkASSERT(inParens >= 0 || fInStdOut);
Cary Clarkce101242017-09-01 15:51:02 -0400438 break;
439 case '\"':
440 inQuotes = !inQuotes;
441 wordEnd = chPtr;
442 SkASSERT(inQuotes == !wordStart);
443 break;
444 case 'A': case 'B': case 'C': case 'D': case 'E':
445 case 'F': case 'G': case 'H': case 'I': case 'J':
446 case 'K': case 'L': case 'M': case 'N': case 'O':
447 case 'P': case 'Q': case 'R': case 'S': case 'T':
448 case 'U': case 'V': case 'W': case 'X': case 'Y':
449 case 'Z':
450 allLower = false;
451 case 'a': case 'b': case 'c': case 'd': case 'e':
452 case 'f': case 'g': case 'h': case 'i': case 'j':
453 case 'k': case 'l': case 'm': case 'n': case 'o':
454 case 'p': case 'q': case 'r': case 's': case 't':
455 case 'u': case 'v': case 'w': case 'x': case 'y':
Ben Wagner63fd7602017-10-09 15:45:33 -0400456 case 'z':
Cary Clarkce101242017-09-01 15:51:02 -0400457 if (!wordStart) {
458 wordStart = chPtr;
459 wordEnd = nullptr;
460 possibleEnd = nullptr;
461 allLower = 'a' <= *chPtr;
462 if ('<' == lastCh || ('<' == priorCh && '/' == lastCh)) {
463 ++inAngles;
464 }
465 }
466 break;
467 case '0': case '1': case '2': case '3': case '4':
468 case '5': case '6': case '7': case '8': case '9':
Ben Wagner63fd7602017-10-09 15:45:33 -0400469 case '_':
Cary Clarkce101242017-09-01 15:51:02 -0400470 allLower = false;
471 case '-': // note that dash doesn't clear allLower
472 break;
Cary Clarkd2ca79c2018-08-10 13:09:13 -0400473 case '!':
474 if (!inQuotes) {
475 wordEnd = chPtr;
476 }
477 break;
Cary Clark4855f782018-02-06 09:41:53 -0500478 case '\n':
479 ++fLocalLine;
480 // fall through
Cary Clarkce101242017-09-01 15:51:02 -0400481 default:
482 wordEnd = chPtr;
483 break;
484 }
Cary Clark80247e52018-07-11 16:18:41 -0400485 prePriorCh = priorCh;
Cary Clarkce101242017-09-01 15:51:02 -0400486 priorCh = lastCh;
487 lastCh = *chPtr;
488 } while (++chPtr <= end);
Cary Clark8032b982017-07-28 11:04:54 -0400489}
490
Cary Clark682c58d2018-05-16 07:07:07 -0400491void SpellCheck::printCheck(string str, PrintCheck allowed) {
Cary Clark8032b982017-07-28 11:04:54 -0400492 string word;
493 for (std::stringstream stream(str); stream >> word; ) {
Cary Clark682c58d2018-05-16 07:07:07 -0400494 if (PrintCheck::kAllowNumbers == allowed && (std::isdigit(word.back()) || 'x' == word.back())) {
495 // allow ###x for RGB_888x
496 if ((size_t) std::count_if(word.begin(), word.end() - 1,
497 [](unsigned char c){ return std::isdigit(c); } ) == word.length() - 1) {
498 continue;
499 }
500 }
Cary Clark8032b982017-07-28 11:04:54 -0400501 wordCheck(word);
502 }
503}
504
Cary Clarkce101242017-09-01 15:51:02 -0400505static bool stringCompare(const std::pair<string, CheckEntry>& i, const std::pair<string, CheckEntry>& j) {
506 return i.first.compare(j.first) < 0;
507}
508
509void SpellCheck::report(SkCommandLineFlags::StringArray report) {
510 vector<std::pair<string, CheckEntry>> elems(fWords.begin(), fWords.end());
511 std::sort(elems.begin(), elems.end(), stringCompare);
512 if (report.contains("once")) {
513 for (auto iter : elems) {
Cary Clark5538c132018-06-14 12:28:14 -0400514 if (iter.second.fOverride) {
Cary Clarkce101242017-09-01 15:51:02 -0400515 continue;
516 }
517 if (iter.second.fCount == 1) {
Cary Clark4855f782018-02-06 09:41:53 -0500518 string fullName = this->ReportFilename(iter.second.fFile);
519 SkDebugf("%s(%d): %s\n", fullName.c_str(), iter.second.fLine,
Cary Clarkce101242017-09-01 15:51:02 -0400520 iter.first.c_str());
521 }
Cary Clark8032b982017-07-28 11:04:54 -0400522 }
Cary Clarkce101242017-09-01 15:51:02 -0400523 SkDebugf("\n");
Cary Clarkd0530ba2017-09-14 11:25:39 -0400524 return;
Cary Clarkce101242017-09-01 15:51:02 -0400525 }
526 if (report.contains("all")) {
527 int column = 0;
Cary Clarkd0530ba2017-09-14 11:25:39 -0400528 char lastInitial = 'a';
Cary Clark154beea2017-10-26 07:58:48 -0400529 int count = 0;
Cary Clarkce101242017-09-01 15:51:02 -0400530 for (auto iter : elems) {
Cary Clark5538c132018-06-14 12:28:14 -0400531 if (iter.second.fOverride) {
Cary Clarkce101242017-09-01 15:51:02 -0400532 continue;
533 }
534 string check = iter.first.c_str();
535 bool allLower = true;
536 for (auto c : check) {
537 if (isupper(c)) {
538 allLower = false;
539 break;
540 }
541 }
542 if (!allLower) {
543 continue;
544 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400545 if (column + check.length() > 100 || check[0] != lastInitial) {
Cary Clarkce101242017-09-01 15:51:02 -0400546 SkDebugf("\n");
547 column = 0;
548 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400549 if (check[0] != lastInitial) {
550 SkDebugf("\n");
551 lastInitial = check[0];
552 }
Cary Clarkce101242017-09-01 15:51:02 -0400553 SkDebugf("%s ", check.c_str());
554 column += check.length();
Cary Clark154beea2017-10-26 07:58:48 -0400555 ++count;
Cary Clark8032b982017-07-28 11:04:54 -0400556 }
Cary Clark154beea2017-10-26 07:58:48 -0400557 SkDebugf("\n\ncount = %d\n", count);
Cary Clarkd0530ba2017-09-14 11:25:39 -0400558 return;
Cary Clarkce101242017-09-01 15:51:02 -0400559 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400560 int index = 0;
561 const char* mispelled = report[0];
562 for (auto iter : elems) {
Cary Clark5538c132018-06-14 12:28:14 -0400563 if (iter.second.fOverride) {
Cary Clarkd0530ba2017-09-14 11:25:39 -0400564 continue;
565 }
566 string check = iter.first.c_str();
567 while (check.compare(mispelled) > 0) {
568 SkDebugf("%s not found\n", mispelled);
569 if (report.count() == ++index) {
Cary Clarkce101242017-09-01 15:51:02 -0400570 break;
571 }
Cary Clarkd0530ba2017-09-14 11:25:39 -0400572 }
573 if (report.count() == index) {
574 break;
575 }
576 if (check.compare(mispelled) == 0) {
Cary Clark4855f782018-02-06 09:41:53 -0500577 string fullName = this->ReportFilename(iter.second.fFile);
578 SkDebugf("%s(%d): %s\n", fullName.c_str(), iter.second.fLine,
Cary Clarkd0530ba2017-09-14 11:25:39 -0400579 iter.first.c_str());
580 if (report.count() == ++index) {
581 break;
Cary Clarkce101242017-09-01 15:51:02 -0400582 }
Cary Clark8032b982017-07-28 11:04:54 -0400583 }
584 }
585}
586
Cary Clark2d4bf5f2018-04-16 08:37:38 -0400587void SpellCheck::wordCheck(string str) {
Cary Clarkce101242017-09-01 15:51:02 -0400588 if ("nullptr" == str) {
589 return; // doesn't seem worth it, treating nullptr as a word in need of correction
590 }
Cary Clark8032b982017-07-28 11:04:54 -0400591 bool hasColon = false;
592 bool hasDot = false;
593 bool hasParen = false;
594 bool hasUnderscore = false;
595 bool sawDash = false;
596 bool sawDigit = false;
597 bool sawSpecial = false;
598 SkASSERT(str.length() > 0);
599 SkASSERT(isalpha(str[0]) || '~' == str[0]);
600 for (char ch : str) {
601 if (isalpha(ch) || '-' == ch) {
602 sawDash |= '-' == ch;
603 continue;
604 }
605 bool isColon = ':' == ch;
606 hasColon |= isColon;
607 bool isDot = '.' == ch;
608 hasDot |= isDot;
Cary Clarkbc5697d2017-10-04 14:31:33 -0400609 bool isParen = '(' == ch || ')' == ch || '~' == ch || '=' == ch || '!' == ch ||
610 '[' == ch || ']' == ch;
Cary Clark8032b982017-07-28 11:04:54 -0400611 hasParen |= isParen;
612 bool isUnderscore = '_' == ch;
613 hasUnderscore |= isUnderscore;
614 if (isColon || isDot || isUnderscore || isParen) {
615 continue;
616 }
617 if (isdigit(ch)) {
618 sawDigit = true;
619 continue;
620 }
621 if ('&' == ch || ',' == ch || ' ' == ch) {
622 sawSpecial = true;
623 continue;
624 }
625 SkASSERT(0);
626 }
627 if (sawSpecial && !hasParen) {
628 SkASSERT(0);
629 }
630 bool inCode = fInCode;
631 if (hasUnderscore && isupper(str[0]) && ('S' != str[0] || 'K' != str[1])
Ben Wagner63fd7602017-10-09 15:45:33 -0400632 && !hasColon && !hasDot && !hasParen && !fInStdOut && !inCode && !fInConst
Cary Clark8032b982017-07-28 11:04:54 -0400633 && !sawDigit && !sawSpecial && !sawDash) {
634 std::istringstream ss(str);
635 string token;
636 while (std::getline(ss, token, '_')) {
Cary Clarka560c472017-11-27 10:44:06 -0500637 if (token.length()) {
638 this->wordCheck(token);
639 }
Cary Clark8032b982017-07-28 11:04:54 -0400640 }
641 return;
642 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400643 if (!hasColon && !hasDot && !hasParen && !hasUnderscore
Cary Clark8032b982017-07-28 11:04:54 -0400644 && !fInStdOut && !inCode && !fInConst && !sawDigit
645 && islower(str[0]) && isupper(str[1])) {
646 inCode = true;
647 }
Cary Clarkce101242017-09-01 15:51:02 -0400648 bool methodParam = false;
649 if (fMethod) {
650 for (auto child : fMethod->fChildren) {
651 if (MarkType::kParam == child->fMarkType && str == child->fName) {
652 methodParam = true;
653 break;
654 }
655 }
656 }
Ben Wagner63fd7602017-10-09 15:45:33 -0400657 auto& mappy = hasColon ? fColons :
Cary Clark8032b982017-07-28 11:04:54 -0400658 hasDot ? fDots :
659 hasParen ? fParens :
660 hasUnderscore ? fUnderscores :
Cary Clarkce101242017-09-01 15:51:02 -0400661 fInStdOut || fInFormula || inCode || fInConst || methodParam ? fCode :
Cary Clark8032b982017-07-28 11:04:54 -0400662 sawDigit ? fDigits : fWords;
663 auto iter = mappy.find(str);
664 if (mappy.end() != iter) {
Cary Clark5538c132018-06-14 12:28:14 -0400665 if (iter->second.fOverride && !fOverride) {
666 iter->second.fFile = fFileName;
667 iter->second.fLine = fLineCount + fLocalLine;
668 iter->second.fOverride = false;
669 }
Cary Clark8032b982017-07-28 11:04:54 -0400670 iter->second.fCount += 1;
671 } else {
672 CheckEntry* entry = &mappy[str];
673 entry->fFile = fFileName;
Cary Clark4855f782018-02-06 09:41:53 -0500674 entry->fLine = fLineCount + fLocalLine;
Cary Clark8032b982017-07-28 11:04:54 -0400675 entry->fCount = 1;
Cary Clark5538c132018-06-14 12:28:14 -0400676 entry->fOverride = fOverride;
Cary Clark8032b982017-07-28 11:04:54 -0400677 }
678}
679
680void SpellCheck::wordCheck(ptrdiff_t len, const char* ch) {
681 leafCheck(ch, ch + len);
682}