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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/editing/spellcheck/TextCheckingHelper.cpp
diff --git a/third_party/WebKit/Source/core/editing/spellcheck/TextCheckingHelper.cpp b/third_party/WebKit/Source/core/editing/spellcheck/TextCheckingHelper.cpp
index b0e55b7e7e62695cf380d3db75fb31947120cd34..7ba54bf4aacd1a37851e81d99304929ca4abce93 100644
--- a/third_party/WebKit/Source/core/editing/spellcheck/TextCheckingHelper.cpp
+++ b/third_party/WebKit/Source/core/editing/spellcheck/TextCheckingHelper.cpp
@@ -41,6 +41,34 @@
namespace blink {
+static void findBadGrammars(TextCheckerClient& client, const UChar* text, int start, int length, Vector<TextCheckingResult>& results)
+{
+ int checkLocation = start;
+ int checkLength = length;
+
+ while (0 < checkLength) {
+ int badGrammarLocation = -1;
+ int badGrammarLength = 0;
+ Vector<GrammarDetail> badGrammarDetails;
+ client.checkGrammarOfString(String(text + checkLocation, checkLength), badGrammarDetails, &badGrammarLocation, &badGrammarLength);
+ if (!badGrammarLength)
+ break;
+ DCHECK_LE(0, badGrammarLocation);
+ DCHECK_LE(badGrammarLocation, checkLength);
+ DCHECK_LT(0, badGrammarLength);
+ DCHECK_LE(badGrammarLocation + badGrammarLength, checkLength);
+ TextCheckingResult badGrammar;
+ badGrammar.decoration = TextDecorationTypeGrammar;
+ badGrammar.location = checkLocation + badGrammarLocation;
+ badGrammar.length = badGrammarLength;
+ badGrammar.details.swap(badGrammarDetails);
+ results.append(badGrammar);
+
+ checkLocation += (badGrammarLocation + badGrammarLength);
+ checkLength -= (badGrammarLocation + badGrammarLength);
+ }
+}
+
static void findMisspellings(TextCheckerClient& client, const UChar* text, int start, int length, Vector<TextCheckingResult>& results)
{
TextBreakIterator* iterator = wordBreakIterator(text + start, length);
@@ -286,6 +314,222 @@ String TextCheckingHelper::findFirstMisspelling(int& firstMisspellingOffset, boo
return firstMisspelling;
}
+String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool& outIsSpelling, int& outFirstFoundOffset, GrammarDetail& outGrammarDetail)
+{
+ if (!unifiedTextCheckerEnabled())
+ return "";
+
+ String firstFoundItem;
+ String misspelledWord;
+ String badGrammarPhrase;
+
+ // Initialize out parameters; these will be updated if we find something to return.
+ outIsSpelling = true;
+ outFirstFoundOffset = 0;
+ outGrammarDetail.location = -1;
+ outGrammarDetail.length = 0;
+ outGrammarDetail.guesses.clear();
+ outGrammarDetail.userDescription = "";
+
+ // Expand the search range to encompass entire paragraphs, since text checking needs that much context.
+ // Determine the character offset from the start of the paragraph to the start of the original search range,
+ // since we will want to ignore results in this area.
+ Position paragraphStart = startOfParagraph(createVisiblePosition(m_start)).toParentAnchoredPosition();
+ Position paragraphEnd = m_end;
+ int totalRangeLength = TextIterator::rangeLength(paragraphStart, paragraphEnd);
+ paragraphEnd = endOfParagraph(createVisiblePosition(m_start)).toParentAnchoredPosition();
+
+ int rangeStartOffset = TextIterator::rangeLength(paragraphStart, m_start);
+ int totalLengthProcessed = 0;
+
+ bool firstIteration = true;
+ bool lastIteration = false;
+ while (totalLengthProcessed < totalRangeLength) {
+ // Iterate through the search range by paragraphs, checking each one for spelling and grammar.
+ int currentLength = TextIterator::rangeLength(paragraphStart, paragraphEnd);
+ int currentStartOffset = firstIteration ? rangeStartOffset : 0;
+ int currentEndOffset = currentLength;
+ if (inSameParagraph(createVisiblePosition(paragraphStart), createVisiblePosition(m_end))) {
+ // Determine the character offset from the end of the original search range to the end of the paragraph,
+ // since we will want to ignore results in this area.
+ currentEndOffset = TextIterator::rangeLength(paragraphStart, m_end);
+ lastIteration = true;
+ }
+ if (currentStartOffset < currentEndOffset) {
+ String paragraphString = plainText(EphemeralRange(paragraphStart, paragraphEnd));
+ if (paragraphString.length() > 0) {
+ bool foundGrammar = false;
+ int spellingLocation = 0;
+ int grammarPhraseLocation = 0;
+ int grammarDetailLocation = 0;
+ unsigned grammarDetailIndex = 0;
+
+ Vector<TextCheckingResult> results;
+ TextCheckingTypeMask checkingTypes = TextCheckingTypeSpelling | TextCheckingTypeGrammar;
+ checkTextOfParagraph(m_client->textChecker(), paragraphString, checkingTypes, results);
+
+ for (unsigned i = 0; i < results.size(); i++) {
+ const TextCheckingResult* result = &results[i];
+ if (result->decoration == TextDecorationTypeSpelling && result->location >= currentStartOffset && result->location + result->length <= currentEndOffset) {
+ DCHECK_GT(result->length, 0);
+ DCHECK_GE(result->location, 0);
+ spellingLocation = result->location;
+ misspelledWord = paragraphString.substring(result->location, result->length);
+ DCHECK(misspelledWord.length());
+ break;
+ }
+ if (result->decoration == TextDecorationTypeGrammar && result->location < currentEndOffset && result->location + result->length > currentStartOffset) {
+ DCHECK_GT(result->length, 0);
+ DCHECK_GE(result->location, 0);
+ // We can't stop after the first grammar result, since there might still be a spelling result after
+ // it begins but before the first detail in it, but we can stop if we find a second grammar result.
+ if (foundGrammar)
+ break;
+ for (unsigned j = 0; j < result->details.size(); j++) {
+ const GrammarDetail* detail = &result->details[j];
+ DCHECK_GT(detail->length, 0);
+ DCHECK_GE(detail->location, 0);
+ if (result->location + detail->location >= currentStartOffset && result->location + detail->location + detail->length <= currentEndOffset && (!foundGrammar || result->location + detail->location < grammarDetailLocation)) {
+ grammarDetailIndex = j;
+ grammarDetailLocation = result->location + detail->location;
+ foundGrammar = true;
+ }
+ }
+ if (foundGrammar) {
+ grammarPhraseLocation = result->location;
+ outGrammarDetail = result->details[grammarDetailIndex];
+ badGrammarPhrase = paragraphString.substring(result->location, result->length);
+ DCHECK(badGrammarPhrase.length());
+ }
+ }
+ }
+
+ if (!misspelledWord.isEmpty() && (badGrammarPhrase.isEmpty() || spellingLocation <= grammarDetailLocation)) {
+ int spellingOffset = spellingLocation - currentStartOffset;
+ if (!firstIteration)
+ spellingOffset += TextIterator::rangeLength(m_start, paragraphStart);
+ outIsSpelling = true;
+ outFirstFoundOffset = spellingOffset;
+ firstFoundItem = misspelledWord;
+ break;
+ }
+ if (!badGrammarPhrase.isEmpty()) {
+ int grammarPhraseOffset = grammarPhraseLocation - currentStartOffset;
+ if (!firstIteration)
+ grammarPhraseOffset += TextIterator::rangeLength(m_start, paragraphStart);
+ outIsSpelling = false;
+ outFirstFoundOffset = grammarPhraseOffset;
+ firstFoundItem = badGrammarPhrase;
+ break;
+ }
+ }
+ }
+ if (lastIteration || totalLengthProcessed + currentLength >= totalRangeLength)
+ break;
+ VisiblePosition newParagraphStart = startOfNextParagraph(createVisiblePosition(paragraphEnd));
+ paragraphStart = newParagraphStart.toParentAnchoredPosition();
+ paragraphEnd = endOfParagraph(newParagraphStart).toParentAnchoredPosition();
+ firstIteration = false;
+ totalLengthProcessed += currentLength;
+ }
+ return firstFoundItem;
+}
+
+int TextCheckingHelper::findFirstGrammarDetail(const Vector<GrammarDetail>& grammarDetails, int badGrammarPhraseLocation, int startOffset, int endOffset, bool markAll) const
+{
+ // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
+ // Optionally add a DocumentMarker for each detail in the range.
+ int earliestDetailLocationSoFar = -1;
+ int earliestDetailIndex = -1;
+ for (unsigned i = 0; i < grammarDetails.size(); i++) {
+ const GrammarDetail* detail = &grammarDetails[i];
+ DCHECK_GT(detail->length, 0);
+ DCHECK_GE(detail->location, 0);
+
+ int detailStartOffsetInParagraph = badGrammarPhraseLocation + detail->location;
+
+ // Skip this detail if it starts before the original search range
+ if (detailStartOffsetInParagraph < startOffset)
+ continue;
+
+ // Skip this detail if it starts after the original search range
+ if (detailStartOffsetInParagraph >= endOffset)
+ continue;
+
+ if (markAll) {
+ const EphemeralRange badGrammarRange = calculateCharacterSubrange(EphemeralRange(m_start, m_end), badGrammarPhraseLocation - startOffset + detail->location, detail->length);
+ badGrammarRange.document().markers().addMarker(badGrammarRange.startPosition(), badGrammarRange.endPosition(), DocumentMarker::Grammar, detail->userDescription);
+ }
+
+ // Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order)
+ if (earliestDetailIndex < 0 || earliestDetailLocationSoFar > detail->location) {
+ earliestDetailIndex = i;
+ earliestDetailLocationSoFar = detail->location;
+ }
+ }
+
+ return earliestDetailIndex;
+}
+
+String TextCheckingHelper::findFirstBadGrammar(GrammarDetail& outGrammarDetail, int& outGrammarPhraseOffset, bool markAll)
+{
+ // Initialize out parameters; these will be updated if we find something to return.
+ outGrammarDetail.location = -1;
+ outGrammarDetail.length = 0;
+ outGrammarDetail.guesses.clear();
+ outGrammarDetail.userDescription = "";
+ outGrammarPhraseOffset = 0;
+
+ String firstBadGrammarPhrase;
+
+ // Expand the search range to encompass entire paragraphs, since grammar checking needs that much context.
+ // Determine the character offset from the start of the paragraph to the start of the original search range,
+ // since we will want to ignore results in this area.
+ TextCheckingParagraph paragraph(EphemeralRange(m_start, m_end));
+
+ // Start checking from beginning of paragraph, but skip past results that occur before the start of the original search range.
+ int startOffset = 0;
+ while (startOffset < paragraph.checkingEnd()) {
+ Vector<GrammarDetail> grammarDetails;
+ int badGrammarPhraseLocation = -1;
+ int badGrammarPhraseLength = 0;
+ m_client->textChecker().checkGrammarOfString(paragraph.textSubstring(startOffset), grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength);
+
+ if (!badGrammarPhraseLength) {
+ DCHECK_EQ(badGrammarPhraseLocation, -1);
+ return String();
+ }
+
+ DCHECK_GE(badGrammarPhraseLocation, 0);
+ badGrammarPhraseLocation += startOffset;
+
+
+ // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
+ int badGrammarIndex = findFirstGrammarDetail(grammarDetails, badGrammarPhraseLocation, paragraph.checkingStart(), paragraph.checkingEnd(), markAll);
+ if (badGrammarIndex >= 0) {
+ DCHECK_LT(static_cast<unsigned>(badGrammarIndex), grammarDetails.size());
+ outGrammarDetail = grammarDetails[badGrammarIndex];
+ }
+
+ // If we found a detail in range, then we have found the first bad phrase (unless we found one earlier but
+ // kept going so we could mark all instances).
+ if (badGrammarIndex >= 0 && firstBadGrammarPhrase.isEmpty()) {
+ outGrammarPhraseOffset = badGrammarPhraseLocation - paragraph.checkingStart();
+ firstBadGrammarPhrase = paragraph.textSubstring(badGrammarPhraseLocation, badGrammarPhraseLength);
+
+ // Found one. We're done now, unless we're marking each instance.
+ if (!markAll)
+ break;
+ }
+
+ // These results were all between the start of the paragraph and the start of the search range; look
+ // beyond this phrase.
+ startOffset = badGrammarPhraseLocation + badGrammarPhraseLength;
+ }
+
+ return firstBadGrammarPhrase;
+}
+
bool TextCheckingHelper::markAllMisspellings()
{
// Use the "markAll" feature of findFirstMisspelling. Ignore the return value and the "out parameter";
@@ -294,6 +538,15 @@ bool TextCheckingHelper::markAllMisspellings()
return findFirstMisspelling(ignoredOffset, true).isEmpty();
}
+void TextCheckingHelper::markAllBadGrammar()
+{
+ // Use the "markAll" feature of findFirstBadGrammar. Ignore the return value and "out parameters"; all we need to
+ // do is mark every instance.
+ GrammarDetail ignoredGrammarDetail;
+ int ignoredOffset;
+ findFirstBadGrammar(ignoredGrammarDetail, ignoredOffset, true);
+}
+
bool TextCheckingHelper::unifiedTextCheckerEnabled() const
{
DCHECK(m_start.isNotNull());
@@ -311,8 +564,27 @@ void checkTextOfParagraph(TextCheckerClient& client, const String& text, TextChe
if (checkingTypes & TextCheckingTypeSpelling)
findMisspellings(client, characters.data(), 0, length, spellingResult);
- if (spellingResult.size())
- results.swap(spellingResult);
+ Vector<TextCheckingResult> grammarResult;
+ if (checkingTypes & TextCheckingTypeGrammar) {
+ // Only checks grammartical error before the first misspellings
+ int grammarCheckLength = length;
+ for (const auto& spelling : spellingResult) {
+ if (spelling.location < grammarCheckLength)
+ grammarCheckLength = spelling.location;
+ }
+
+ findBadGrammars(client, characters.data(), 0, grammarCheckLength, grammarResult);
+ }
+
+ if (grammarResult.size())
+ results.swap(grammarResult);
+
+ if (spellingResult.size()) {
+ if (results.isEmpty())
+ results.swap(spellingResult);
+ else
+ results.appendVector(spellingResult);
+ }
}
bool unifiedTextCheckerEnabled(const LocalFrame* frame)

Powered by Google App Engine
This is Rietveld 408576698