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 |