VisiblePosition SelectionModifier::nextWordPositionForPlatform( const VisiblePosition& originalPosition) { VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition); if (frame() && frame()->editor().behavior().shouldSkipSpaceWhenMovingRight()) { // In order to skip spaces when moving right, we advance one // word further and then move one word back. Given the // semantics of previousWordPosition() this will put us at the // beginning of the word following. VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord); if (positionAfterSpacingAndFollowingWord.isNotNull() && positionAfterSpacingAndFollowingWord.deepEquivalent() != positionAfterCurrentWord.deepEquivalent()) positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord); bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord.deepEquivalent() == previousWordPosition(nextWordPosition(originalPosition)) .deepEquivalent(); if (movingBackwardsMovedPositionToStartOfCurrentWord) positionAfterCurrentWord = positionAfterSpacingAndFollowingWord; } return positionAfterCurrentWord; }
VisibleSelection visibleSelectionForClosestActualWordStart(const VisibleSelection& selection) { // VisibleSelection validation has a special case when the caret is at the end of a paragraph where // it selects the paragraph marker. As well, if the position is at the end of a word, it will select // only the space between words. We want to select an actual word so we move the selection to // the start of the leftmost word if the character after the selection point is whitespace. if (selection.selectionType() != VisibleSelection::RangeSelection) { int leftDistance = 0; int rightDistance = 0; VisibleSelection leftSelection(previousWordPosition(selection.start())); bool leftSelectionIsOnWord = !isWhitespace(leftSelection.visibleStart().characterAfter()) && leftSelection.start().containerNode() == selection.start().containerNode(); if (leftSelectionIsOnWord) { VisibleSelection rangeSelection(endOfWord(leftSelection.start()), selection.visibleStart()); leftDistance = TextIterator::rangeLength(rangeSelection.toNormalizedRange().get()); } VisibleSelection rightSelection = previousWordPosition(nextWordPosition(selection.start())); bool rightSelectionIsOnWord = !isWhitespace(rightSelection.visibleStart().characterAfter()) && rightSelection.start().containerNode() == selection.start().containerNode(); if (rightSelectionIsOnWord) { VisibleSelection rangeSelection = VisibleSelection(rightSelection.visibleStart(), selection.visibleStart()); rightDistance = TextIterator::rangeLength(rangeSelection.toNormalizedRange().get()); } // Make sure we found an actual word. If not, return the original selection. if (!leftSelectionIsOnWord && !rightSelectionIsOnWord) return selection; if (!rightSelectionIsOnWord || (leftSelectionIsOnWord && leftDistance <= rightDistance)) { // Left is closer or right is invalid. return leftSelection; } // Right is closer or equal, or left was invalid. return rightSelection; } // No adjustment required. return selection; }
VisiblePosition SelectionModifier::modifyMovingBackward( TextGranularity granularity) { VisiblePosition pos; switch (granularity) { case CharacterGranularity: if (m_selection.isRange()) pos = createVisiblePosition(m_selection.start(), m_selection.affinity()); else pos = previousPositionOf( createVisiblePosition(m_selection.extent(), m_selection.affinity()), CanSkipOverEditingBoundary); break; case WordGranularity: pos = previousWordPosition( createVisiblePosition(m_selection.extent(), m_selection.affinity())); break; case SentenceGranularity: pos = previousSentencePosition( createVisiblePosition(m_selection.extent(), m_selection.affinity())); break; case LineGranularity: pos = previousLinePosition( startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); break; case ParagraphGranularity: pos = previousParagraphPosition( startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); break; case SentenceBoundary: pos = startOfSentence(startForPlatform()); break; case LineBoundary: pos = logicalStartOfLine(startForPlatform()); break; case ParagraphBoundary: pos = startOfParagraph(startForPlatform()); break; case DocumentBoundary: pos = startForPlatform(); if (isEditablePosition(pos.deepEquivalent())) pos = startOfEditableContent(pos); else pos = startOfDocument(pos); break; } return pos; }
VisiblePosition SelectionModifier::modifyExtendingBackward( TextGranularity granularity) { VisiblePosition pos = createVisiblePosition(m_selection.extent(), m_selection.affinity()); // Extending a selection backward by word or character from just after a table // selects the table. This "makes sense" from the user perspective, esp. when // deleting. It was done here instead of in VisiblePosition because we want // VPs to iterate over everything. switch (granularity) { case CharacterGranularity: pos = previousPositionOf(pos, CanSkipOverEditingBoundary); break; case WordGranularity: pos = previousWordPosition(pos); break; case SentenceGranularity: pos = previousSentencePosition(pos); break; case LineGranularity: pos = previousLinePosition( pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); break; case ParagraphGranularity: pos = previousParagraphPosition( pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); break; case SentenceBoundary: pos = startOfSentence(startForPlatform()); break; case LineBoundary: pos = logicalStartOfLine(startForPlatform()); break; case ParagraphBoundary: pos = startOfParagraph(startForPlatform()); break; case DocumentBoundary: pos = startForPlatform(); if (isEditablePosition(pos.deepEquivalent())) pos = startOfEditableContent(pos); else pos = startOfDocument(pos); break; } adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR)); return pos; }
VisiblePosition SelectionModifier::modifyExtendingRight( TextGranularity granularity) { VisiblePosition pos = createVisiblePosition(m_selection.extent(), m_selection.affinity()); // The difference between modifyExtendingRight and modifyExtendingForward is: // modifyExtendingForward always extends forward logically. // modifyExtendingRight behaves the same as modifyExtendingForward except for // extending character or word, it extends forward logically if the enclosing // block is LTR direction, but it extends backward logically if the enclosing // block is RTL direction. switch (granularity) { case CharacterGranularity: if (directionOfEnclosingBlock() == LTR) pos = nextPositionOf(pos, CanSkipOverEditingBoundary); else pos = previousPositionOf(pos, CanSkipOverEditingBoundary); break; case WordGranularity: if (directionOfEnclosingBlock() == LTR) pos = nextWordPositionForPlatform(pos); else pos = previousWordPosition(pos); break; case LineBoundary: if (directionOfEnclosingBlock() == LTR) pos = modifyExtendingForward(granularity); else pos = modifyExtendingBackward(granularity); break; case SentenceGranularity: case LineGranularity: case ParagraphGranularity: case SentenceBoundary: case ParagraphBoundary: case DocumentBoundary: // FIXME: implement all of the above? pos = modifyExtendingForward(granularity); break; } adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR); return pos; }
void SpellChecker::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary) { if (textChecker().shouldEraseMarkersAfterChangeSelection(TextCheckingTypeSpelling)) return; // We want to remove the markers from a word if an editing command will change the word. This can happen in one of // several scenarios: // 1. Insert in the middle of a word. // 2. Appending non whitespace at the beginning of word. // 3. Appending non whitespace at the end of word. // Note that, appending only whitespaces at the beginning or end of word won't change the word, so we don't need to // remove the markers on that word. // Of course, if current selection is a range, we potentially will edit two words that fall on the boundaries of // selection, and remove words between the selection boundaries. // VisiblePosition startOfSelection = m_frame.selection().selection().visibleStart(); VisiblePosition endOfSelection = m_frame.selection().selection().visibleEnd(); if (startOfSelection.isNull()) return; // First word is the word that ends after or on the start of selection. VisiblePosition startOfFirstWord = startOfWord(startOfSelection, LeftWordIfOnBoundary); VisiblePosition endOfFirstWord = endOfWord(startOfSelection, LeftWordIfOnBoundary); // Last word is the word that begins before or on the end of selection VisiblePosition startOfLastWord = startOfWord(endOfSelection, RightWordIfOnBoundary); VisiblePosition endOfLastWord = endOfWord(endOfSelection, RightWordIfOnBoundary); if (startOfFirstWord.isNull()) { startOfFirstWord = startOfWord(startOfSelection, RightWordIfOnBoundary); endOfFirstWord = endOfWord(startOfSelection, RightWordIfOnBoundary); } if (endOfLastWord.isNull()) { startOfLastWord = startOfWord(endOfSelection, LeftWordIfOnBoundary); endOfLastWord = endOfWord(endOfSelection, LeftWordIfOnBoundary); } // If doNotRemoveIfSelectionAtWordBoundary is true, and first word ends at the start of selection, // we choose next word as the first word. if (doNotRemoveIfSelectionAtWordBoundary && endOfFirstWord == startOfSelection) { startOfFirstWord = nextWordPosition(startOfFirstWord); endOfFirstWord = endOfWord(startOfFirstWord, RightWordIfOnBoundary); if (startOfFirstWord == endOfSelection) return; } // If doNotRemoveIfSelectionAtWordBoundary is true, and last word begins at the end of selection, // we choose previous word as the last word. if (doNotRemoveIfSelectionAtWordBoundary && startOfLastWord == endOfSelection) { startOfLastWord = previousWordPosition(startOfLastWord); endOfLastWord = endOfWord(startOfLastWord, RightWordIfOnBoundary); if (endOfLastWord == startOfSelection) return; } if (startOfFirstWord.isNull() || endOfFirstWord.isNull() || startOfLastWord.isNull() || endOfLastWord.isNull()) return; // Now we remove markers on everything between startOfFirstWord and endOfLastWord. // However, if an autocorrection change a single word to multiple words, we want to remove correction mark from all the // resulted words even we only edit one of them. For example, assuming autocorrection changes "avantgarde" to "avant // garde", we will have CorrectionIndicator marker on both words and on the whitespace between them. If we then edit garde, // we would like to remove the marker from word "avant" and whitespace as well. So we need to get the continous range of // of marker that contains the word in question, and remove marker on that whole range. Document* document = m_frame.document(); ASSERT(document); RefPtr<Range> wordRange = Range::create(*document, startOfFirstWord.deepEquivalent(), endOfLastWord.deepEquivalent()); document->markers().removeMarkers(wordRange.get(), DocumentMarker::MisspellingMarkers(), DocumentMarkerController::RemovePartiallyOverlappingMarker); }