Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(383)

Side by Side Diff: third_party/WebKit/Source/core/editing/commands/InsertIncrementalTextCommand.cpp

Issue 2530843003: Introduce InsertIncrementalTextCommand to respect existing style for composition (Closed)
Patch Set: Address xiaochengh@'s review Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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/html/HTMLSpanElement.h"
16
17 namespace blink {
18
19 namespace {
20
21 size_t computeCommonPrefixLength(const String& str1, const String& str2) {
22 const size_t maxCommonPrefixLength = std::min(str1.length(), str2.length());
23 for (size_t index = 0; index < maxCommonPrefixLength; ++index) {
24 if (str1[index] != str2[index])
25 return index;
26 }
27 return maxCommonPrefixLength;
28 }
29
30 size_t computeCommonSuffixLength(const String& str1, const String& str2) {
31 const size_t length1 = str1.length();
32 const size_t length2 = str2.length();
33 const size_t maxCommonSuffixLength = std::min(length1, length2);
34 for (size_t index = 0; index < maxCommonSuffixLength; ++index) {
35 if (str1[length1 - index - 1] != str2[length2 - index - 1])
36 return index;
37 }
38 return maxCommonSuffixLength;
39 }
40
41 // If current position is at grapheme boundary, return 0; otherwise, return the
42 // distance to its nearest left grapheme boundary.
43 size_t computeDistanceToLeftGraphemeBoundary(const Position& position) {
44 const Position& adjustedPosition = previousPositionOf(
45 nextPositionOf(position, PositionMoveType::GraphemeCluster),
46 PositionMoveType::GraphemeCluster);
47 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode());
48 DCHECK_GE(position.computeOffsetInContainerNode(),
49 adjustedPosition.computeOffsetInContainerNode());
50 return static_cast<size_t>(position.computeOffsetInContainerNode() -
51 adjustedPosition.computeOffsetInContainerNode());
52 }
53
54 size_t computeCommonGraphemeClusterPrefixLength(
55 const String& oldText,
56 const String& newText,
57 const Element* rootEditableElement) {
58 const size_t commonPrefixLength = computeCommonPrefixLength(oldText, newText);
59
60 // For grapheme cluster, we should adjust it for grapheme boundary.
61 const EphemeralRange& range =
62 PlainTextRange(0, commonPrefixLength).createRange(*rootEditableElement);
63 if (range.isNull())
64 return 0;
65 const Position& position = range.endPosition();
66 const size_t diff = computeDistanceToLeftGraphemeBoundary(position);
67 DCHECK_GE(commonPrefixLength, diff);
68 return commonPrefixLength - diff;
69 }
70
71 // If current position is at grapheme boundary, return 0; otherwise, return the
72 // distance to its nearest right grapheme boundary.
73 size_t computeDistanceToRightGraphemeBoundary(const Position& position) {
74 const Position& adjustedPosition = nextPositionOf(
75 previousPositionOf(position, PositionMoveType::GraphemeCluster),
76 PositionMoveType::GraphemeCluster);
77 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode());
78 DCHECK_GE(adjustedPosition.computeOffsetInContainerNode(),
79 position.computeOffsetInContainerNode());
80 return static_cast<size_t>(adjustedPosition.computeOffsetInContainerNode() -
81 position.computeOffsetInContainerNode());
82 }
83
84 size_t computeCommonGraphemeClusterSuffixLength(
85 const String& oldText,
86 const String& newText,
87 const Element* rootEditableElement) {
88 const size_t commonSuffixLength = computeCommonSuffixLength(oldText, newText);
89
90 // For grapheme cluster, we should adjust it for grapheme boundary.
91 const EphemeralRange& range =
92 PlainTextRange(0, oldText.length() - commonSuffixLength)
93 .createRange(*rootEditableElement);
94 if (range.isNull())
95 return 0;
96 const Position& position = range.endPosition();
97 const size_t diff = computeDistanceToRightGraphemeBoundary(position);
98 DCHECK_GE(commonSuffixLength, diff);
99 return commonSuffixLength - diff;
100 }
101
102 const String computeTextForInsertion(const String& newText,
103 const size_t commonPrefixLength,
104 const size_t commonSuffixLength) {
105 return newText.substring(
106 commonPrefixLength,
107 newText.length() - commonPrefixLength - commonSuffixLength);
108 }
109
110 VisibleSelection computeSelectionForInsertion(
111 const EphemeralRange& selectionRange,
112 const int offset,
113 const int length,
114 const bool isDirectional) {
115 CharacterIterator charIt(selectionRange);
116 const EphemeralRange& rangeForInsertion =
117 charIt.calculateCharacterSubrange(offset, length);
118 const VisibleSelection& selection =
119 createVisibleSelection(SelectionInDOMTree::Builder()
120 .setBaseAndExtent(rangeForInsertion)
121 .setIsDirectional(isDirectional)
122 .build());
123 return selection;
124 }
125
126 } // anonymous namespace
127
128 InsertIncrementalTextCommand* InsertIncrementalTextCommand::create(
129 Document& document,
130 const String& text,
131 bool selectInsertedText,
132 RebalanceType rebalanceType) {
133 return new InsertIncrementalTextCommand(document, text, selectInsertedText,
134 rebalanceType);
135 }
136
137 InsertIncrementalTextCommand::InsertIncrementalTextCommand(
138 Document& document,
139 const String& text,
140 bool selectInsertedText,
141 RebalanceType rebalanceType)
142 : InsertTextCommand(document, text, selectInsertedText, rebalanceType) {}
143
144 void InsertIncrementalTextCommand::doApply(EditingState* editingState) {
145 const Element* element = endingSelection().rootEditableElement();
146 DCHECK(element);
147
148 const EphemeralRange selectionRange(endingSelection().start(),
149 endingSelection().end());
150 const String oldText = plainText(selectionRange);
151 const String& newText = m_text;
152
153 const size_t newTextLength = newText.length();
154 const size_t oldTextLength = oldText.length();
155 const size_t commonPrefixLength =
156 computeCommonGraphemeClusterPrefixLength(oldText, newText, element);
157 // We should ignore common prefix when finding common suffix.
158 const size_t commonSuffixLength = computeCommonGraphemeClusterSuffixLength(
159 oldText.right(oldTextLength - commonPrefixLength),
160 newText.right(newTextLength - commonPrefixLength), element);
161 DCHECK_GE(oldTextLength, commonPrefixLength + commonSuffixLength);
162
163 m_text =
164 computeTextForInsertion(m_text, commonPrefixLength, commonSuffixLength);
165
166 const int offset = static_cast<int>(commonPrefixLength);
167 const int length =
168 static_cast<int>(oldTextLength - commonPrefixLength - commonSuffixLength);
169 const VisibleSelection& selectionForInsertion = computeSelectionForInsertion(
170 selectionRange, offset, length, endingSelection().isDirectional());
171
172 setEndingSelectionWithoutValidation(selectionForInsertion.start(),
173 selectionForInsertion.end());
174
175 InsertTextCommand::doApply(editingState);
176 }
177
178 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698