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/frame/LocalFrame.h" | |
| 15 #include "core/html/HTMLSpanElement.h" | |
| 16 | |
| 17 namespace blink { | |
| 18 | |
| 19 InsertIncrementalTextCommand::InsertIncrementalTextCommand( | |
| 20 Document& document, | |
| 21 const String& text, | |
| 22 bool selectInsertedText, | |
| 23 RebalanceType rebalanceType) | |
| 24 : InsertTextCommand(document, text, selectInsertedText, rebalanceType) {} | |
| 25 | |
| 26 static size_t computeCommonPrefixLength(const String& str1, | |
|
yosin_UTC9
2016/12/07 05:36:22
Let's use unnamed namespace: https://chromium.goog
yabinh
2016/12/08 07:54:57
Done.
| |
| 27 const String& str2) { | |
| 28 const size_t maxCommonPrefixLength = std::min(str1.length(), str2.length()); | |
| 29 for (size_t index = 0; index < maxCommonPrefixLength; ++index) { | |
| 30 if (str1[index] != str2[index]) | |
| 31 return index; | |
| 32 } | |
| 33 return maxCommonPrefixLength; | |
| 34 } | |
| 35 | |
| 36 static size_t computeCommonSuffixLength(const String& str1, | |
| 37 const String& str2) { | |
| 38 const size_t length1 = str1.length(); | |
| 39 const size_t length2 = str2.length(); | |
| 40 const size_t maxCommonSuffixLength = std::min(length1, length2); | |
| 41 for (size_t index = 0; index < maxCommonSuffixLength; ++index) { | |
| 42 if (str1[length1 - index - 1] != str2[length2 - index - 1]) | |
| 43 return index; | |
| 44 } | |
| 45 return maxCommonSuffixLength; | |
| 46 } | |
| 47 | |
| 48 // If current position is at grapheme boundary, return 0; otherwise, return the | |
| 49 // distance to its nearest left grapheme boundary. | |
| 50 static size_t computeDistanceToLeftGraphemeBoundary(const Position& position) { | |
| 51 const Position& adjustedPosition = previousPositionOf( | |
| 52 nextPositionOf(position, PositionMoveType::GraphemeCluster), | |
| 53 PositionMoveType::GraphemeCluster); | |
| 54 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode()); | |
| 55 DCHECK_GE(position.computeOffsetInContainerNode(), | |
| 56 adjustedPosition.computeOffsetInContainerNode()); | |
| 57 return static_cast<size_t>(position.computeOffsetInContainerNode() - | |
| 58 adjustedPosition.computeOffsetInContainerNode()); | |
| 59 } | |
| 60 | |
| 61 static size_t computeCommonGraphemeClusterPrefixLength( | |
| 62 const String& oldText, | |
| 63 const String& newText, | |
| 64 const Element* rootEditableElement) { | |
| 65 const size_t commonPrefixLength = computeCommonPrefixLength(oldText, newText); | |
| 66 | |
| 67 // For grapheme cluster, we should adjust it for grapheme boundary. | |
| 68 const EphemeralRange& range = | |
| 69 PlainTextRange(0, commonPrefixLength).createRange(*rootEditableElement); | |
|
Xiaocheng
2016/12/07 10:23:26
Is this correct when |oldText| is not at the begin
yabinh
2016/12/08 07:54:57
Yes. See InputMethodControllerTest#SetCompositionK
| |
| 70 if (range.isNull()) | |
| 71 return 0; | |
| 72 const Position& position = range.endPosition(); | |
| 73 const size_t diff = computeDistanceToLeftGraphemeBoundary(position); | |
| 74 DCHECK_GE(commonPrefixLength, diff); | |
| 75 return commonPrefixLength - diff; | |
| 76 } | |
| 77 | |
| 78 // If current position is at grapheme boundary, return 0; otherwise, return the | |
| 79 // distance to its nearest right grapheme boundary. | |
| 80 static size_t computeDistanceToRightGraphemeBoundary(const Position& position) { | |
| 81 const Position& adjustedPosition = nextPositionOf( | |
| 82 previousPositionOf(position, PositionMoveType::GraphemeCluster), | |
| 83 PositionMoveType::GraphemeCluster); | |
| 84 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode()); | |
| 85 DCHECK_GE(adjustedPosition.computeOffsetInContainerNode(), | |
| 86 position.computeOffsetInContainerNode()); | |
| 87 return static_cast<size_t>(adjustedPosition.computeOffsetInContainerNode() - | |
| 88 position.computeOffsetInContainerNode()); | |
| 89 } | |
| 90 | |
| 91 static size_t computeCommonGraphemeClusterSuffixLength( | |
| 92 const String& oldText, | |
| 93 const String& newText, | |
| 94 const Element* rootEditableElement) { | |
| 95 const size_t commonSuffixLength = computeCommonSuffixLength(oldText, newText); | |
| 96 | |
| 97 // For grapheme cluster, we should adjust it for grapheme boundary. | |
| 98 const EphemeralRange& range = | |
| 99 PlainTextRange(0, oldText.length() - commonSuffixLength) | |
|
Xiaocheng
2016/12/07 10:23:26
Is this correct when |oldText| is not at the end o
yabinh
2016/12/08 07:54:58
ditto
| |
| 100 .createRange(*rootEditableElement); | |
| 101 if (range.isNull()) | |
| 102 return 0; | |
| 103 const Position& position = range.endPosition(); | |
| 104 const size_t diff = computeDistanceToRightGraphemeBoundary(position); | |
| 105 DCHECK_GE(commonSuffixLength, diff); | |
| 106 return commonSuffixLength - diff; | |
| 107 } | |
| 108 | |
| 109 static const String computeTextForInsertion(const String& newText, | |
| 110 const size_t commonPrefixLength, | |
| 111 const size_t commonSuffixLength) { | |
| 112 return newText.substring( | |
| 113 commonPrefixLength, | |
| 114 newText.length() - commonPrefixLength - commonSuffixLength); | |
| 115 } | |
| 116 | |
| 117 static PlainTextRange getSelectionOffsets(LocalFrame* frame) { | |
|
Xiaocheng
2016/12/07 10:23:26
Let's get rid of this function. See comments on do
yabinh
2016/12/08 07:54:57
Done.
| |
| 118 EphemeralRange range = firstEphemeralRangeOf(frame->selection().selection()); | |
| 119 if (range.isNull()) | |
| 120 return PlainTextRange(); | |
| 121 ContainerNode* editable = | |
| 122 frame->selection().rootEditableElementOrTreeScopeRootNode(); | |
| 123 DCHECK(editable); | |
| 124 | |
| 125 return PlainTextRange::create(*editable, range); | |
| 126 } | |
| 127 | |
| 128 static const VisibleSelection createSelection(const size_t start, | |
|
Xiaocheng
2016/12/07 10:23:25
Let's get rid of this function. See comments on do
yabinh
2016/12/08 07:54:57
Done.
| |
| 129 const size_t end, | |
| 130 const bool isDirectional, | |
| 131 LocalFrame* frame) { | |
| 132 Element* element = frame->selection().selection().rootEditableElement(); | |
|
yosin_UTC9
2016/12/07 05:23:34
Make root editable as parameter rather than passin
yabinh
2016/12/08 07:54:58
Done.
| |
| 133 DCHECK(element); | |
| 134 | |
| 135 const EphemeralRange& startRange = | |
| 136 PlainTextRange(0, static_cast<int>(start)).createRange(*element); | |
| 137 DCHECK(startRange.isNotNull()); | |
| 138 const Position& startPosition = startRange.endPosition(); | |
| 139 | |
| 140 const EphemeralRange& endRange = | |
| 141 PlainTextRange(0, static_cast<int>(end)).createRange(*element); | |
| 142 DCHECK(endRange.isNotNull()); | |
| 143 const Position& endPosition = endRange.endPosition(); | |
| 144 | |
| 145 VisibleSelection selection = | |
|
yosin_UTC9
2016/12/07 05:23:34
nit: s/VisibleSelection/const VisibleSelection&/
yabinh
2016/12/08 07:54:58
Done.
| |
| 146 createVisibleSelection(SelectionInDOMTree::Builder() | |
| 147 .setBaseAndExtent(startPosition, endPosition) | |
| 148 .build()); | |
| 149 selection.setIsDirectional(isDirectional); | |
|
yosin_UTC9
2016/12/07 05:23:34
Use SelectionInDOMTree::Builder::setIsDirectional(
yabinh
2016/12/08 07:54:58
Done.
| |
| 150 | |
| 151 return selection; | |
| 152 } | |
| 153 | |
| 154 const VisibleSelection | |
|
yosin_UTC9
2016/12/07 05:23:34
nit: s/const//
yabinh
2016/12/08 07:54:58
This function has been removed.
| |
| 155 InsertIncrementalTextCommand::computeSelectionForInsertion( | |
|
Xiaocheng
2016/12/07 10:23:26
Let's get rid of this function. See comments on do
yabinh
2016/12/08 07:54:57
Done.
| |
| 156 const size_t selectionStart, | |
| 157 const size_t selectionEnd, | |
| 158 const size_t commonPrefixLength, | |
| 159 const size_t commonSuffixLength) { | |
| 160 const size_t insertionStart = selectionStart + commonPrefixLength; | |
| 161 const size_t insertionEnd = selectionEnd - commonSuffixLength; | |
| 162 DCHECK_LE(insertionStart, insertionEnd); | |
| 163 | |
| 164 const VisibleSelection selectionForInsertion = | |
|
yosin_UTC9
2016/12/07 05:23:34
nit: s/VisibleSelection/const VisibleSelection&/
yabinh
2016/12/08 07:54:57
This function has been removed.
| |
| 165 createSelection(insertionStart, insertionEnd, | |
| 166 endingSelection().isDirectional(), document().frame()); | |
| 167 | |
| 168 return selectionForInsertion; | |
| 169 } | |
| 170 | |
| 171 void InsertIncrementalTextCommand::setSelection(const size_t start, | |
|
yosin_UTC9
2016/12/07 05:23:34
EditingCommand should not set selection by it self
yabinh
2016/12/08 07:54:58
Done.
| |
| 172 const size_t end, | |
| 173 LocalFrame* frame) { | |
| 174 const VisibleSelection selection = | |
| 175 createSelection(start, end, endingSelection().isDirectional(), frame); | |
| 176 setStartingSelection(selection); | |
|
Xiaocheng
2016/12/07 10:23:25
I don't think we should change the starting select
yabinh
2016/12/08 07:54:58
Done.
| |
| 177 setEndingSelectionWithoutValidation(selection.start(), selection.end()); | |
| 178 | |
| 179 document().frame()->selection().setSelection(selection); | |
| 180 } | |
| 181 | |
| 182 void InsertIncrementalTextCommand::doApply(EditingState* editingState) { | |
| 183 LocalFrame* frame = document().frame(); | |
| 184 DCHECK(frame); | |
| 185 const Element* element = endingSelection().rootEditableElement(); | |
| 186 DCHECK(element); | |
| 187 | |
| 188 const String oldText = frame->selectedText(); | |
|
Xiaocheng
2016/12/07 10:23:26
Accessing FrameSelection from EditCommand is disco
yabinh
2016/12/08 07:54:58
Done.
| |
| 189 const String& newText = m_text; | |
| 190 const size_t newTextLength = newText.length(); | |
| 191 const size_t commonPrefixLength = | |
| 192 computeCommonGraphemeClusterPrefixLength(oldText, newText, element); | |
| 193 // We should ignore common prefix when finding common suffix. | |
| 194 const size_t commonSuffixLength = computeCommonGraphemeClusterSuffixLength( | |
| 195 oldText.right(oldText.length() - commonPrefixLength), | |
| 196 newText.right(newTextLength - commonPrefixLength), element); | |
| 197 | |
| 198 m_text = | |
| 199 computeTextForInsertion(m_text, commonPrefixLength, commonSuffixLength); | |
| 200 | |
| 201 const PlainTextRange selectionOffsets = getSelectionOffsets(frame); | |
|
Xiaocheng
2016/12/07 10:23:26
Use CharacterIterator::calculateCharacterSubrange
yabinh
2016/12/08 07:54:58
Done.
| |
| 202 const size_t selectionStart = selectionOffsets.start(); | |
| 203 const size_t selectionEnd = selectionOffsets.end(); | |
| 204 const VisibleSelection selectionForInsertion = computeSelectionForInsertion( | |
| 205 selectionStart, selectionEnd, commonPrefixLength, commonSuffixLength); | |
| 206 | |
| 207 const bool changeSelection = selectionForInsertion != endingSelection(); | |
| 208 | |
| 209 setStartingSelection(selectionForInsertion); | |
|
Xiaocheng
2016/12/07 10:23:26
I don't think we should change starting selection.
yabinh
2016/12/08 07:54:58
Done.
| |
| 210 setEndingSelectionWithoutValidation(selectionForInsertion.start(), | |
| 211 selectionForInsertion.end()); | |
| 212 | |
| 213 InsertTextCommand::doApply(editingState); | |
| 214 | |
| 215 if (editingState->isAborted()) | |
| 216 return; | |
| 217 if (changeSelection) | |
| 218 setSelection(selectionStart, selectionStart + newTextLength, frame); | |
|
Xiaocheng
2016/12/07 10:23:26
Use |setEndingSelection| instead.
yabinh
2016/12/08 07:54:57
This function has been removed.
| |
| 219 } | |
| 220 | |
| 221 } // namespace blink | |
| OLD | NEW |