Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(123)

Side by Side Diff: third_party/WebKit/Source/core/editing/spellcheck/TextCheckingHelper.cpp

Issue 2211813002: Revert removal of grammar checking and marking code (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Minor fix Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. 2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) 3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 6 * modification, are permitted provided that the following conditions
7 * are met: 7 * are met:
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
(...skipping 23 matching lines...) Expand all
34 #include "core/editing/iterators/WordAwareIterator.h" 34 #include "core/editing/iterators/WordAwareIterator.h"
35 #include "core/editing/markers/DocumentMarkerController.h" 35 #include "core/editing/markers/DocumentMarkerController.h"
36 #include "core/frame/LocalFrame.h" 36 #include "core/frame/LocalFrame.h"
37 #include "core/frame/Settings.h" 37 #include "core/frame/Settings.h"
38 #include "core/page/SpellCheckerClient.h" 38 #include "core/page/SpellCheckerClient.h"
39 #include "platform/text/TextBreakIterator.h" 39 #include "platform/text/TextBreakIterator.h"
40 #include "platform/text/TextCheckerClient.h" 40 #include "platform/text/TextCheckerClient.h"
41 41
42 namespace blink { 42 namespace blink {
43 43
44 static void findBadGrammars(TextCheckerClient& client, const UChar* text, int st art, int length, Vector<TextCheckingResult>& results)
45 {
46 int checkLocation = start;
47 int checkLength = length;
48
49 while (0 < checkLength) {
50 int badGrammarLocation = -1;
51 int badGrammarLength = 0;
52 Vector<GrammarDetail> badGrammarDetails;
53 client.checkGrammarOfString(String(text + checkLocation, checkLength), b adGrammarDetails, &badGrammarLocation, &badGrammarLength);
54 if (!badGrammarLength)
55 break;
56 DCHECK_LE(0, badGrammarLocation);
57 DCHECK_LE(badGrammarLocation, checkLength);
58 DCHECK_LT(0, badGrammarLength);
59 DCHECK_LE(badGrammarLocation + badGrammarLength, checkLength);
60 TextCheckingResult badGrammar;
61 badGrammar.decoration = TextDecorationTypeGrammar;
62 badGrammar.location = checkLocation + badGrammarLocation;
63 badGrammar.length = badGrammarLength;
64 badGrammar.details.swap(badGrammarDetails);
65 results.append(badGrammar);
66
67 checkLocation += (badGrammarLocation + badGrammarLength);
68 checkLength -= (badGrammarLocation + badGrammarLength);
69 }
70 }
71
44 static void findMisspellings(TextCheckerClient& client, const UChar* text, int s tart, int length, Vector<TextCheckingResult>& results) 72 static void findMisspellings(TextCheckerClient& client, const UChar* text, int s tart, int length, Vector<TextCheckingResult>& results)
45 { 73 {
46 TextBreakIterator* iterator = wordBreakIterator(text + start, length); 74 TextBreakIterator* iterator = wordBreakIterator(text + start, length);
47 if (!iterator) 75 if (!iterator)
48 return; 76 return;
49 int wordStart = iterator->current(); 77 int wordStart = iterator->current();
50 while (0 <= wordStart) { 78 while (0 <= wordStart) {
51 int wordEnd = iterator->next(); 79 int wordEnd = iterator->next();
52 if (wordEnd < 0) 80 if (wordEnd < 0)
53 break; 81 break;
(...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after
279 } 307 }
280 } 308 }
281 309
282 currentChunkOffset += length; 310 currentChunkOffset += length;
283 it.advance(); 311 it.advance();
284 } 312 }
285 313
286 return firstMisspelling; 314 return firstMisspelling;
287 } 315 }
288 316
317 String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool& outIsSpelling, int& outFirstFoundOffset, GrammarDetail& outGrammarDetail)
318 {
319 if (!unifiedTextCheckerEnabled())
320 return "";
321
322 String firstFoundItem;
323 String misspelledWord;
324 String badGrammarPhrase;
325
326 // Initialize out parameters; these will be updated if we find something to return.
327 outIsSpelling = true;
328 outFirstFoundOffset = 0;
329 outGrammarDetail.location = -1;
330 outGrammarDetail.length = 0;
331 outGrammarDetail.guesses.clear();
332 outGrammarDetail.userDescription = "";
333
334 // Expand the search range to encompass entire paragraphs, since text checki ng needs that much context.
335 // Determine the character offset from the start of the paragraph to the sta rt of the original search range,
336 // since we will want to ignore results in this area.
337 Position paragraphStart = startOfParagraph(createVisiblePosition(m_start)).t oParentAnchoredPosition();
338 Position paragraphEnd = m_end;
339 int totalRangeLength = TextIterator::rangeLength(paragraphStart, paragraphEn d);
340 paragraphEnd = endOfParagraph(createVisiblePosition(m_start)).toParentAnchor edPosition();
341
342 int rangeStartOffset = TextIterator::rangeLength(paragraphStart, m_start);
343 int totalLengthProcessed = 0;
344
345 bool firstIteration = true;
346 bool lastIteration = false;
347 while (totalLengthProcessed < totalRangeLength) {
348 // Iterate through the search range by paragraphs, checking each one for spelling and grammar.
349 int currentLength = TextIterator::rangeLength(paragraphStart, paragraphE nd);
350 int currentStartOffset = firstIteration ? rangeStartOffset : 0;
351 int currentEndOffset = currentLength;
352 if (inSameParagraph(createVisiblePosition(paragraphStart), createVisible Position(m_end))) {
353 // Determine the character offset from the end of the original searc h range to the end of the paragraph,
354 // since we will want to ignore results in this area.
355 currentEndOffset = TextIterator::rangeLength(paragraphStart, m_end);
356 lastIteration = true;
357 }
358 if (currentStartOffset < currentEndOffset) {
359 String paragraphString = plainText(EphemeralRange(paragraphStart, pa ragraphEnd));
360 if (paragraphString.length() > 0) {
361 bool foundGrammar = false;
362 int spellingLocation = 0;
363 int grammarPhraseLocation = 0;
364 int grammarDetailLocation = 0;
365 unsigned grammarDetailIndex = 0;
366
367 Vector<TextCheckingResult> results;
368 TextCheckingTypeMask checkingTypes = TextCheckingTypeSpelling | TextCheckingTypeGrammar;
369 checkTextOfParagraph(m_client->textChecker(), paragraphString, c heckingTypes, results);
370
371 for (unsigned i = 0; i < results.size(); i++) {
372 const TextCheckingResult* result = &results[i];
373 if (result->decoration == TextDecorationTypeSpelling && resu lt->location >= currentStartOffset && result->location + result->length <= curre ntEndOffset) {
374 DCHECK_GT(result->length, 0);
375 DCHECK_GE(result->location, 0);
376 spellingLocation = result->location;
377 misspelledWord = paragraphString.substring(result->locat ion, result->length);
378 DCHECK(misspelledWord.length());
379 break;
380 }
381 if (result->decoration == TextDecorationTypeGrammar && resul t->location < currentEndOffset && result->location + result->length > currentSta rtOffset) {
382 DCHECK_GT(result->length, 0);
383 DCHECK_GE(result->location, 0);
384 // We can't stop after the first grammar result, since t here might still be a spelling result after
385 // it begins but before the first detail in it, but we c an stop if we find a second grammar result.
386 if (foundGrammar)
387 break;
388 for (unsigned j = 0; j < result->details.size(); j++) {
389 const GrammarDetail* detail = &result->details[j];
390 DCHECK_GT(detail->length, 0);
391 DCHECK_GE(detail->location, 0);
392 if (result->location + detail->location >= currentSt artOffset && result->location + detail->location + detail->length <= currentEndO ffset && (!foundGrammar || result->location + detail->location < grammarDetailLo cation)) {
393 grammarDetailIndex = j;
394 grammarDetailLocation = result->location + detai l->location;
395 foundGrammar = true;
396 }
397 }
398 if (foundGrammar) {
399 grammarPhraseLocation = result->location;
400 outGrammarDetail = result->details[grammarDetailInde x];
401 badGrammarPhrase = paragraphString.substring(result- >location, result->length);
402 DCHECK(badGrammarPhrase.length());
403 }
404 }
405 }
406
407 if (!misspelledWord.isEmpty() && (badGrammarPhrase.isEmpty() || spellingLocation <= grammarDetailLocation)) {
408 int spellingOffset = spellingLocation - currentStartOffset;
409 if (!firstIteration)
410 spellingOffset += TextIterator::rangeLength(m_start, par agraphStart);
411 outIsSpelling = true;
412 outFirstFoundOffset = spellingOffset;
413 firstFoundItem = misspelledWord;
414 break;
415 }
416 if (!badGrammarPhrase.isEmpty()) {
417 int grammarPhraseOffset = grammarPhraseLocation - currentSta rtOffset;
418 if (!firstIteration)
419 grammarPhraseOffset += TextIterator::rangeLength(m_start , paragraphStart);
420 outIsSpelling = false;
421 outFirstFoundOffset = grammarPhraseOffset;
422 firstFoundItem = badGrammarPhrase;
423 break;
424 }
425 }
426 }
427 if (lastIteration || totalLengthProcessed + currentLength >= totalRangeL ength)
428 break;
429 VisiblePosition newParagraphStart = startOfNextParagraph(createVisiblePo sition(paragraphEnd));
430 paragraphStart = newParagraphStart.toParentAnchoredPosition();
431 paragraphEnd = endOfParagraph(newParagraphStart).toParentAnchoredPositio n();
432 firstIteration = false;
433 totalLengthProcessed += currentLength;
434 }
435 return firstFoundItem;
436 }
437
438 int TextCheckingHelper::findFirstGrammarDetail(const Vector<GrammarDetail>& gram marDetails, int badGrammarPhraseLocation, int startOffset, int endOffset, bool m arkAll) const
439 {
440 // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
441 // Optionally add a DocumentMarker for each detail in the range.
442 int earliestDetailLocationSoFar = -1;
443 int earliestDetailIndex = -1;
444 for (unsigned i = 0; i < grammarDetails.size(); i++) {
445 const GrammarDetail* detail = &grammarDetails[i];
446 DCHECK_GT(detail->length, 0);
447 DCHECK_GE(detail->location, 0);
448
449 int detailStartOffsetInParagraph = badGrammarPhraseLocation + detail->lo cation;
450
451 // Skip this detail if it starts before the original search range
452 if (detailStartOffsetInParagraph < startOffset)
453 continue;
454
455 // Skip this detail if it starts after the original search range
456 if (detailStartOffsetInParagraph >= endOffset)
457 continue;
458
459 if (markAll) {
460 const EphemeralRange badGrammarRange = calculateCharacterSubrange(Ep hemeralRange(m_start, m_end), badGrammarPhraseLocation - startOffset + detail->l ocation, detail->length);
461 badGrammarRange.document().markers().addMarker(badGrammarRange.start Position(), badGrammarRange.endPosition(), DocumentMarker::Grammar, detail->user Description);
462 }
463
464 // Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order)
465 if (earliestDetailIndex < 0 || earliestDetailLocationSoFar > detail->loc ation) {
466 earliestDetailIndex = i;
467 earliestDetailLocationSoFar = detail->location;
468 }
469 }
470
471 return earliestDetailIndex;
472 }
473
474 String TextCheckingHelper::findFirstBadGrammar(GrammarDetail& outGrammarDetail, int& outGrammarPhraseOffset, bool markAll)
475 {
476 // Initialize out parameters; these will be updated if we find something to return.
477 outGrammarDetail.location = -1;
478 outGrammarDetail.length = 0;
479 outGrammarDetail.guesses.clear();
480 outGrammarDetail.userDescription = "";
481 outGrammarPhraseOffset = 0;
482
483 String firstBadGrammarPhrase;
484
485 // Expand the search range to encompass entire paragraphs, since grammar che cking needs that much context.
486 // Determine the character offset from the start of the paragraph to the sta rt of the original search range,
487 // since we will want to ignore results in this area.
488 TextCheckingParagraph paragraph(EphemeralRange(m_start, m_end));
489
490 // Start checking from beginning of paragraph, but skip past results that oc cur before the start of the original search range.
491 int startOffset = 0;
492 while (startOffset < paragraph.checkingEnd()) {
493 Vector<GrammarDetail> grammarDetails;
494 int badGrammarPhraseLocation = -1;
495 int badGrammarPhraseLength = 0;
496 m_client->textChecker().checkGrammarOfString(paragraph.textSubstring(sta rtOffset), grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength);
497
498 if (!badGrammarPhraseLength) {
499 DCHECK_EQ(badGrammarPhraseLocation, -1);
500 return String();
501 }
502
503 DCHECK_GE(badGrammarPhraseLocation, 0);
504 badGrammarPhraseLocation += startOffset;
505
506
507 // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
508 int badGrammarIndex = findFirstGrammarDetail(grammarDetails, badGrammarP hraseLocation, paragraph.checkingStart(), paragraph.checkingEnd(), markAll);
509 if (badGrammarIndex >= 0) {
510 DCHECK_LT(static_cast<unsigned>(badGrammarIndex), grammarDetails.siz e());
511 outGrammarDetail = grammarDetails[badGrammarIndex];
512 }
513
514 // If we found a detail in range, then we have found the first bad phras e (unless we found one earlier but
515 // kept going so we could mark all instances).
516 if (badGrammarIndex >= 0 && firstBadGrammarPhrase.isEmpty()) {
517 outGrammarPhraseOffset = badGrammarPhraseLocation - paragraph.checki ngStart();
518 firstBadGrammarPhrase = paragraph.textSubstring(badGrammarPhraseLoca tion, badGrammarPhraseLength);
519
520 // Found one. We're done now, unless we're marking each instance.
521 if (!markAll)
522 break;
523 }
524
525 // These results were all between the start of the paragraph and the sta rt of the search range; look
526 // beyond this phrase.
527 startOffset = badGrammarPhraseLocation + badGrammarPhraseLength;
528 }
529
530 return firstBadGrammarPhrase;
531 }
532
289 bool TextCheckingHelper::markAllMisspellings() 533 bool TextCheckingHelper::markAllMisspellings()
290 { 534 {
291 // Use the "markAll" feature of findFirstMisspelling. Ignore the return valu e and the "out parameter"; 535 // Use the "markAll" feature of findFirstMisspelling. Ignore the return valu e and the "out parameter";
292 // all we need to do is mark every instance. 536 // all we need to do is mark every instance.
293 int ignoredOffset; 537 int ignoredOffset;
294 return findFirstMisspelling(ignoredOffset, true).isEmpty(); 538 return findFirstMisspelling(ignoredOffset, true).isEmpty();
295 } 539 }
296 540
541 void TextCheckingHelper::markAllBadGrammar()
542 {
543 // Use the "markAll" feature of findFirstBadGrammar. Ignore the return value and "out parameters"; all we need to
544 // do is mark every instance.
545 GrammarDetail ignoredGrammarDetail;
546 int ignoredOffset;
547 findFirstBadGrammar(ignoredGrammarDetail, ignoredOffset, true);
548 }
549
297 bool TextCheckingHelper::unifiedTextCheckerEnabled() const 550 bool TextCheckingHelper::unifiedTextCheckerEnabled() const
298 { 551 {
299 DCHECK(m_start.isNotNull()); 552 DCHECK(m_start.isNotNull());
300 Document& doc = m_start.computeContainerNode()->document(); 553 Document& doc = m_start.computeContainerNode()->document();
301 return blink::unifiedTextCheckerEnabled(doc.frame()); 554 return blink::unifiedTextCheckerEnabled(doc.frame());
302 } 555 }
303 556
304 void checkTextOfParagraph(TextCheckerClient& client, const String& text, TextChe ckingTypeMask checkingTypes, Vector<TextCheckingResult>& results) 557 void checkTextOfParagraph(TextCheckerClient& client, const String& text, TextChe ckingTypeMask checkingTypes, Vector<TextCheckingResult>& results)
305 { 558 {
306 Vector<UChar> characters; 559 Vector<UChar> characters;
307 text.appendTo(characters); 560 text.appendTo(characters);
308 unsigned length = text.length(); 561 unsigned length = text.length();
309 562
310 Vector<TextCheckingResult> spellingResult; 563 Vector<TextCheckingResult> spellingResult;
311 if (checkingTypes & TextCheckingTypeSpelling) 564 if (checkingTypes & TextCheckingTypeSpelling)
312 findMisspellings(client, characters.data(), 0, length, spellingResult); 565 findMisspellings(client, characters.data(), 0, length, spellingResult);
313 566
314 if (spellingResult.size()) 567 Vector<TextCheckingResult> grammarResult;
315 results.swap(spellingResult); 568 if (checkingTypes & TextCheckingTypeGrammar) {
569 // Only checks grammartical error before the first misspellings
570 int grammarCheckLength = length;
571 for (const auto& spelling : spellingResult) {
572 if (spelling.location < grammarCheckLength)
573 grammarCheckLength = spelling.location;
574 }
575
576 findBadGrammars(client, characters.data(), 0, grammarCheckLength, gramma rResult);
577 }
578
579 if (grammarResult.size())
580 results.swap(grammarResult);
581
582 if (spellingResult.size()) {
583 if (results.isEmpty())
584 results.swap(spellingResult);
585 else
586 results.appendVector(spellingResult);
587 }
316 } 588 }
317 589
318 bool unifiedTextCheckerEnabled(const LocalFrame* frame) 590 bool unifiedTextCheckerEnabled(const LocalFrame* frame)
319 { 591 {
320 if (!frame) 592 if (!frame)
321 return false; 593 return false;
322 594
323 const Settings* settings = frame->settings(); 595 const Settings* settings = frame->settings();
324 if (!settings) 596 if (!settings)
325 return false; 597 return false;
326 598
327 return settings->unifiedTextCheckerEnabled(); 599 return settings->unifiedTextCheckerEnabled();
328 } 600 }
329 601
330 } // namespace blink 602 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698