Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "core/editing/commands/InsertIncrementalTextCommand.h" | |
| 6 | |
| 7 #include "core/dom/Document.h" | |
| 8 #include "core/dom/Element.h" | |
| 9 #include "core/dom/Text.h" | |
| 10 #include "core/editing/EditingUtilities.h" | |
| 11 #include "core/editing/Editor.h" | |
| 12 #include "core/editing/PlainTextRange.h" | |
| 13 #include "core/editing/VisibleUnits.h" | |
| 14 #include "core/editing/iterators/CharacterIterator.h" | |
| 15 #include "core/frame/LocalFrame.h" | |
|
Xiaocheng
2016/12/09 05:10:30
We don't need it anymore.
yabinh
2016/12/09 06:16:47
Done.
| |
| 16 #include "core/html/HTMLSpanElement.h" | |
| 17 | |
| 18 namespace blink { | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 size_t computeCommonPrefixLength(const String& str1, const String& str2) { | |
| 23 const size_t maxCommonPrefixLength = std::min(str1.length(), str2.length()); | |
| 24 for (size_t index = 0; index < maxCommonPrefixLength; ++index) { | |
| 25 if (str1[index] != str2[index]) | |
| 26 return index; | |
| 27 } | |
| 28 return maxCommonPrefixLength; | |
| 29 } | |
| 30 | |
| 31 size_t computeCommonSuffixLength(const String& str1, const String& str2) { | |
| 32 const size_t length1 = str1.length(); | |
| 33 const size_t length2 = str2.length(); | |
| 34 const size_t maxCommonSuffixLength = std::min(length1, length2); | |
| 35 for (size_t index = 0; index < maxCommonSuffixLength; ++index) { | |
| 36 if (str1[length1 - index - 1] != str2[length2 - index - 1]) | |
| 37 return index; | |
| 38 } | |
| 39 return maxCommonSuffixLength; | |
| 40 } | |
| 41 | |
| 42 // If current position is at grapheme boundary, return 0; otherwise, return the | |
| 43 // distance to its nearest left grapheme boundary. | |
| 44 size_t computeDistanceToLeftGraphemeBoundary(const Position& position) { | |
| 45 const Position& adjustedPosition = previousPositionOf( | |
| 46 nextPositionOf(position, PositionMoveType::GraphemeCluster), | |
| 47 PositionMoveType::GraphemeCluster); | |
| 48 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode()); | |
| 49 DCHECK_GE(position.computeOffsetInContainerNode(), | |
| 50 adjustedPosition.computeOffsetInContainerNode()); | |
| 51 return static_cast<size_t>(position.computeOffsetInContainerNode() - | |
| 52 adjustedPosition.computeOffsetInContainerNode()); | |
| 53 } | |
| 54 | |
| 55 size_t computeCommonGraphemeClusterPrefixLength( | |
| 56 const String& oldText, | |
| 57 const String& newText, | |
| 58 const Element* rootEditableElement) { | |
| 59 const size_t commonPrefixLength = computeCommonPrefixLength(oldText, newText); | |
| 60 | |
| 61 // For grapheme cluster, we should adjust it for grapheme boundary. | |
| 62 const EphemeralRange& range = | |
| 63 PlainTextRange(0, commonPrefixLength).createRange(*rootEditableElement); | |
| 64 if (range.isNull()) | |
| 65 return 0; | |
| 66 const Position& position = range.endPosition(); | |
| 67 const size_t diff = computeDistanceToLeftGraphemeBoundary(position); | |
| 68 DCHECK_GE(commonPrefixLength, diff); | |
| 69 return commonPrefixLength - diff; | |
| 70 } | |
| 71 | |
| 72 // If current position is at grapheme boundary, return 0; otherwise, return the | |
| 73 // distance to its nearest right grapheme boundary. | |
| 74 size_t computeDistanceToRightGraphemeBoundary(const Position& position) { | |
| 75 const Position& adjustedPosition = nextPositionOf( | |
| 76 previousPositionOf(position, PositionMoveType::GraphemeCluster), | |
| 77 PositionMoveType::GraphemeCluster); | |
| 78 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode()); | |
| 79 DCHECK_GE(adjustedPosition.computeOffsetInContainerNode(), | |
| 80 position.computeOffsetInContainerNode()); | |
| 81 return static_cast<size_t>(adjustedPosition.computeOffsetInContainerNode() - | |
| 82 position.computeOffsetInContainerNode()); | |
| 83 } | |
| 84 | |
| 85 size_t computeCommonGraphemeClusterSuffixLength( | |
| 86 const String& oldText, | |
| 87 const String& newText, | |
| 88 const Element* rootEditableElement) { | |
| 89 const size_t commonSuffixLength = computeCommonSuffixLength(oldText, newText); | |
| 90 | |
| 91 // For grapheme cluster, we should adjust it for grapheme boundary. | |
| 92 const EphemeralRange& range = | |
| 93 PlainTextRange(0, oldText.length() - commonSuffixLength) | |
| 94 .createRange(*rootEditableElement); | |
| 95 if (range.isNull()) | |
| 96 return 0; | |
| 97 const Position& position = range.endPosition(); | |
| 98 const size_t diff = computeDistanceToRightGraphemeBoundary(position); | |
| 99 DCHECK_GE(commonSuffixLength, diff); | |
| 100 return commonSuffixLength - diff; | |
| 101 } | |
| 102 | |
| 103 const String computeTextForInsertion(const String& newText, | |
| 104 const size_t commonPrefixLength, | |
| 105 const size_t commonSuffixLength) { | |
| 106 return newText.substring( | |
| 107 commonPrefixLength, | |
| 108 newText.length() - commonPrefixLength - commonSuffixLength); | |
| 109 } | |
| 110 | |
| 111 VisibleSelection computeSelectionForInsertion( | |
| 112 const EphemeralRange& selectionRange, | |
| 113 const int offset, | |
| 114 const int length, | |
| 115 const bool isDirectional) { | |
| 116 CharacterIterator charIt(selectionRange); | |
| 117 const EphemeralRange& rangeForInsertion = | |
| 118 charIt.calculateCharacterSubrange(offset, length); | |
| 119 const VisibleSelection& selection = | |
| 120 createVisibleSelection(SelectionInDOMTree::Builder() | |
| 121 .setBaseAndExtent(rangeForInsertion) | |
| 122 .setIsDirectional(isDirectional) | |
| 123 .build()); | |
| 124 return selection; | |
| 125 } | |
| 126 | |
| 127 } // anonymous namespace | |
| 128 | |
| 129 InsertIncrementalTextCommand* InsertIncrementalTextCommand::create( | |
| 130 Document& document, | |
| 131 const String& text, | |
| 132 bool selectInsertedText, | |
| 133 RebalanceType rebalanceType) { | |
| 134 return new InsertIncrementalTextCommand(document, text, selectInsertedText, | |
| 135 rebalanceType); | |
| 136 } | |
| 137 | |
| 138 InsertIncrementalTextCommand::InsertIncrementalTextCommand( | |
| 139 Document& document, | |
| 140 const String& text, | |
| 141 bool selectInsertedText, | |
| 142 RebalanceType rebalanceType) | |
| 143 : InsertTextCommand(document, text, selectInsertedText, rebalanceType) {} | |
| 144 | |
| 145 void InsertIncrementalTextCommand::doApply(EditingState* editingState) { | |
| 146 LocalFrame* frame = document().frame(); | |
|
Xiaocheng
2016/12/09 05:10:30
We don't need |frame| anymore.
yabinh
2016/12/09 06:16:47
Done.
| |
| 147 DCHECK(frame); | |
| 148 const Element* element = endingSelection().rootEditableElement(); | |
| 149 DCHECK(element); | |
| 150 | |
| 151 const EphemeralRange selectionRange(endingSelection().start(), | |
| 152 endingSelection().end()); | |
| 153 const String oldText = plainText(selectionRange); | |
| 154 const String& newText = m_text; | |
| 155 | |
| 156 const size_t newTextLength = newText.length(); | |
| 157 const size_t oldTextLength = oldText.length(); | |
| 158 const size_t commonPrefixLength = | |
| 159 computeCommonGraphemeClusterPrefixLength(oldText, newText, element); | |
| 160 // We should ignore common prefix when finding common suffix. | |
| 161 const size_t commonSuffixLength = computeCommonGraphemeClusterSuffixLength( | |
| 162 oldText.right(oldTextLength - commonPrefixLength), | |
| 163 newText.right(newTextLength - commonPrefixLength), element); | |
| 164 DCHECK_GE(oldTextLength, commonPrefixLength + commonSuffixLength); | |
| 165 | |
| 166 m_text = | |
| 167 computeTextForInsertion(m_text, commonPrefixLength, commonSuffixLength); | |
| 168 | |
| 169 const int offset = static_cast<int>(commonPrefixLength); | |
| 170 const int length = | |
| 171 static_cast<int>(oldTextLength - commonPrefixLength - commonSuffixLength); | |
| 172 const VisibleSelection& selectionForInsertion = computeSelectionForInsertion( | |
| 173 selectionRange, offset, length, endingSelection().isDirectional()); | |
| 174 | |
| 175 setEndingSelectionWithoutValidation(selectionForInsertion.start(), | |
| 176 selectionForInsertion.end()); | |
| 177 | |
| 178 InsertTextCommand::doApply(editingState); | |
| 179 } | |
| 180 | |
| 181 } // namespace blink | |
| OLD | NEW |