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 |