📄 editor.cpp
字号:
ASSERT(ec == 0); } } currentChunkOffset += len; it.advance(); } return firstMisspelling;}#ifndef BUILDING_ON_TIGERstatic PassRefPtr<Range> paragraphAlignedRangeForRange(Range* arbitraryRange, int& offsetIntoParagraphAlignedRange, String& paragraphString){ ASSERT_ARG(arbitraryRange, arbitraryRange); ExceptionCode ec = 0; // Expand range to paragraph boundaries RefPtr<Range> paragraphRange = arbitraryRange->cloneRange(ec); setStart(paragraphRange.get(), startOfParagraph(arbitraryRange->startPosition())); setEnd(paragraphRange.get(), endOfParagraph(arbitraryRange->endPosition())); // Compute offset from start of expanded range to start of original range RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), arbitraryRange->startPosition()); offsetIntoParagraphAlignedRange = TextIterator::rangeLength(offsetAsRange.get()); // Fill in out parameter with string representing entire paragraph range. // Someday we might have a caller that doesn't use this, but for now all callers do. paragraphString = plainText(paragraphRange.get()); return paragraphRange;}static int findFirstGrammarDetailInRange(const Vector<GrammarDetail>& grammarDetails, int badGrammarPhraseLocation, int /*badGrammarPhraseLength*/, Range *searchRange, int startOffset, int endOffset, bool markAll){ // 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]; ASSERT(detail->length > 0 && 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) { RefPtr<Range> badGrammarRange = TextIterator::subrange(searchRange, badGrammarPhraseLocation - startOffset + detail->location, detail->length); ExceptionCode ec = 0; badGrammarRange->startContainer(ec)->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription); ASSERT(ec == 0); } // 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;} static String findFirstBadGrammarInRange(EditorClient* client, Range* searchRange, GrammarDetail& outGrammarDetail, int& outGrammarPhraseOffset, bool markAll){ ASSERT_ARG(client, client); ASSERT_ARG(searchRange, searchRange); // 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. int searchRangeStartOffset; String paragraphString; RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(searchRange, searchRangeStartOffset, paragraphString); // Determine the character offset from the start of the paragraph to the end of the original search range, // since we will want to ignore results in this area also. int searchRangeEndOffset = searchRangeStartOffset + TextIterator::rangeLength(searchRange); // 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 < searchRangeEndOffset) { Vector<GrammarDetail> grammarDetails; int badGrammarPhraseLocation = -1; int badGrammarPhraseLength = 0; client->checkGrammarOfString(paragraphString.characters() + startOffset, paragraphString.length() - startOffset, grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength); if (badGrammarPhraseLength == 0) { ASSERT(badGrammarPhraseLocation == -1); return String(); } ASSERT(badGrammarPhraseLocation >= 0); badGrammarPhraseLocation += startOffset; // Found some bad grammar. Find the earliest detail range that starts in our search range (if any). int badGrammarIndex = findFirstGrammarDetailInRange(grammarDetails, badGrammarPhraseLocation, badGrammarPhraseLength, searchRange, searchRangeStartOffset, searchRangeEndOffset, markAll); if (badGrammarIndex >= 0) { ASSERT(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 - searchRangeStartOffset; firstBadGrammarPhrase = paragraphString.substring(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;} #endif /* not BUILDING_ON_TIGER */#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)static String findFirstMisspellingOrBadGrammarInRange(EditorClient* client, Range* searchRange, bool checkGrammar, bool& outIsSpelling, int& outFirstFoundOffset, GrammarDetail& outGrammarDetail){ ASSERT_ARG(client, client); ASSERT_ARG(searchRange, searchRange); String firstFoundItem; String misspelledWord; String badGrammarPhrase; ExceptionCode ec = 0; // 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. RefPtr<Range> paragraphRange = searchRange->cloneRange(ec); setStart(paragraphRange.get(), startOfParagraph(searchRange->startPosition())); int totalRangeLength = TextIterator::rangeLength(paragraphRange.get()); setEnd(paragraphRange.get(), endOfParagraph(searchRange->startPosition())); RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), searchRange->startPosition()); int searchRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get()); 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(paragraphRange.get()); int currentStartOffset = firstIteration ? searchRangeStartOffset : 0; int currentEndOffset = currentLength; if (inSameParagraph(paragraphRange->startPosition(), searchRange->endPosition())) { // 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. RefPtr<Range> endOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), searchRange->endPosition()); currentEndOffset = TextIterator::rangeLength(endOffsetAsRange.get()); lastIteration = true; } if (currentStartOffset < currentEndOffset) { String paragraphString = plainText(paragraphRange.get()); if (paragraphString.length() > 0) { bool foundGrammar = false; int spellingLocation = 0; int grammarPhraseLocation = 0; int grammarDetailLocation = 0; unsigned grammarDetailIndex = 0; Vector<TextCheckingResult> results; client->checkSpellingAndGrammarOfParagraph(paragraphString.characters(), paragraphString.length(), checkGrammar, results); for (unsigned i = 0; i < results.size(); i++) { const TextCheckingResult* result = &results[i]; if (result->resultType == 1 && result->location >= currentStartOffset && result->location + result->length <= currentEndOffset) { ASSERT(result->length > 0 && result->location >= 0); spellingLocation = result->location; misspelledWord = paragraphString.substring(result->location, result->length); ASSERT(misspelledWord.length() != 0); break; } else if (checkGrammar && result->resultType == 2 && result->location < currentEndOffset && result->location + result->length > currentStartOffset) { ASSERT(result->length > 0 && 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]; ASSERT(detail->length > 0 && 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); ASSERT(badGrammarPhrase.length() != 0); } } } if (!misspelledWord.isEmpty() && (!checkGrammar || badGrammarPhrase.isEmpty() || spellingLocation <= grammarDetailLocation)) { int spellingOffset = spellingLocation - currentStartOffset; if (!firstIteration) { RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), searchRange->startPosition(), paragraphRange->startPosition()); spellingOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get()); } outIsSpelling = true; outFirstFoundOffset = spellingOffset; firstFoundItem = misspelledWord; break; } else if (checkGrammar && !badGrammarPhrase.isEmpty()) { int grammarPhraseOffset = grammarPhraseLocation - currentStartOffset; if (!firstIteration) { RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), searchRange->startPosition(), paragraphRange->startPosition()); grammarPhraseOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get()); } outIsSpelling = false; outFirstFoundOffset = grammarPhraseOffset; firstFoundItem = badGrammarPhrase; break; } } } if (lastIteration || totalLengthProcessed + currentLength >= totalRangeLength) break; setStart(paragraphRange.get(), startOfNextParagraph(paragraphRange->endPosition())); setEnd(paragraphRange.get(), endOfParagraph(paragraphRange->startPosition())); firstIteration = false; totalLengthProcessed += currentLength; } return firstFoundItem;}#endifvoid Editor::advanceToNextMisspelling(bool startBeforeSelection){ ExceptionCode ec = 0; // The basic approach is to search in two phases - from the selection end to the end of the doc, and // then we wrap and search from the doc start to (approximately) where we started. // Start at the end of the selection, search to edge of document. Starting at the selection end makes // repeated "check spelling" commands work. VisibleSelection selection(frame()->selection()->selection()); RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document())); bool startedWithSelection = false; if (selection.start().node()) { startedWithSelection = true; if (startBeforeSelection) { VisiblePosition start(selection.visibleStart()); // We match AppKit's rule: Start 1 character before the selection. VisiblePosition oneBeforeStart = start.previous(); setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start); } else setStart(spellingSearchRange.get(), selection.visibleEnd()); } Position position = spellingSearchRange->startPosition(); if (!isEditablePosition(position)) { // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the // selection is editable. // This can happen in Mail for a mix of non-editable and editable content (like Stationary), // when spell checking the whole document before sending the message. // In that case the document might not be editable, but there are editable pockets that need to be spell checked. position = firstEditablePositionAfterPositionInRoot(position, frame()->document()->documentElement()).deepEquivalent(); if (position.isNull()) return; Position rangeCompliantPosition = rangeCompliantEquivalent(position); spellingSearchRange->setStart(rangeCompliantPosition.node(), rangeCompliantPosition.offset(), ec); startedWithSelection = false; // won't need to wrap } // topNode defines the whole range we want to operate on Node* topNode = highestEditableRoot(position); spellingSearchRange->setEnd(topNode, maxDeepOffset(topNode), ec); // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking // at a word boundary. Going back by one char and then forward by a word does the trick. if (startedWithSelection) { VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous(); if (oneBeforeStart.isNotNull()) { setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart)); } // else we were already at the start of the editable node } if (spellingSearchRange->collapsed(ec)) return; // nothing to search in // Get the spell checker if it is available if (!client()) return; // We go to the end of our first range instead of the start of it, just to be sure // we don't get foiled by any word boundary problems at the start. It means we might // do a tiny bit more searching. Node *searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec); int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec); int misspellingOffset = 0;#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec); String misspelledWord; String badGrammarPhrase; int grammarPhraseOffset = 0; bool isSpelling = true; int foundOffset = 0; GrammarDetail grammarDetail; String foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellingSearchRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail); if (isSpelling) { misspelledWord = foundItem; misspellingOffset = foundOffset; } else { badGrammarPhrase = foundItem; grammarPhraseOffset = foundOffset
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -