1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/components_ng/pattern/rich_editor/rich_editor_undo_manager.h"
17
18 namespace OHOS::Ace::NG {
19 constexpr uint32_t RECORD_MAX_LENGTH = 20;
20
Create(bool isSpanStringMode,const WeakPtr<RichEditorPattern> & pattern)21 std::unique_ptr<RichEditorUndoManager> RichEditorUndoManager::Create(
22 bool isSpanStringMode, const WeakPtr<RichEditorPattern>& pattern)
23 {
24 if (isSpanStringMode) {
25 return std::make_unique<StyledStringUndoManager>(pattern);
26 }
27 return std::make_unique<SpansUndoManager>(pattern);
28 }
29
UndoByRecords()30 void RichEditorUndoManager::UndoByRecords()
31 {
32 CHECK_NULL_VOID(!undoRecords_.empty());
33 auto pattern = pattern_.Upgrade();
34 CHECK_NULL_VOID(pattern);
35 CHECK_NULL_VOID(IsStyledUndoRedoSupported());
36 CHECK_NULL_VOID(!pattern->IsPreviewTextInputting());
37 auto record = undoRecords_.back();
38 auto sizeBefore = undoRecords_.size();
39 auto recordIndex = sizeBefore - 1;
40 undoRecords_.pop_back();
41 StartCountingRecord();
42 bool isPreventChange = !record.isOnlyStyleChange && !BeforeChangeByRecord(record, true);
43 auto recordCount = EndCountingRecord();
44 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "UndoByRecord [%{public}s] isPreventChange=%{public}d",
45 record.ToString().c_str(), isPreventChange);
46 if (isPreventChange) {
47 // if prevevent change need put the record back
48 CHECK_NULL_VOID(recordCount < RECORD_MAX_LENGTH);
49 auto sizeAfter = sizeBefore + recordCount;
50 size_t moveLength = (sizeAfter > RECORD_MAX_LENGTH) ? sizeAfter - RECORD_MAX_LENGTH : 0;
51 IF_TRUE(moveLength <= recordIndex, RecordOperation(record, recordIndex - moveLength));
52 return;
53 }
54 pattern->ProcessStyledUndo(record);
55 RecordUndoOperation(record);
56 IF_TRUE(!record.isOnlyStyleChange, AfterChangeByRecord(record, true));
57 }
58
RedoByRecords()59 void RichEditorUndoManager::RedoByRecords()
60 {
61 CHECK_NULL_VOID(!redoRecords_.empty());
62 auto pattern = pattern_.Upgrade();
63 CHECK_NULL_VOID(pattern);
64 CHECK_NULL_VOID(IsStyledUndoRedoSupported());
65 CHECK_NULL_VOID(!pattern->IsPreviewTextInputting());
66 auto record = redoRecords_.back();
67 redoRecords_.pop_back();
68 bool isPreventChange = !record.isOnlyStyleChange && !BeforeChangeByRecord(record);
69 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "RedoByRecords [%{public}s] isPreventChange=%{public}d",
70 record.ToString().c_str(), isPreventChange);
71 CHECK_NULL_VOID(!isPreventChange);
72 pattern->ProcessStyledRedo(record);
73 RecordOperation(record, true);
74 IF_TRUE(!record.isOnlyStyleChange, AfterChangeByRecord(record));
75 }
76
RecordSelectionBefore()77 void RichEditorUndoManager::RecordSelectionBefore()
78 {
79 auto pattern = pattern_.Upgrade();
80 CHECK_NULL_VOID(pattern);
81 CHECK_NULL_VOID(IsStyledUndoRedoSupported());
82 auto caretPosition = pattern->caretPosition_;
83 auto& textSelector = pattern->textSelector_;
84 auto selection = textSelector.SelectNothing() ? TextRange{ caretPosition, caretPosition }
85 : TextRange{ textSelector.GetStart(), textSelector.GetEnd() };
86 RecordSelectionBefore(selection);
87 }
88
RecordSelectionBefore(TextRange selection)89 void RichEditorUndoManager::RecordSelectionBefore(TextRange selection)
90 {
91 auto pattern = pattern_.Upgrade();
92 CHECK_NULL_VOID(pattern);
93 CHECK_NULL_VOID(IsStyledUndoRedoSupported());
94 selectionBefore_ = selection;
95 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "RecordSelectionBefore:%{public}s", selectionBefore_.ToString().c_str());
96 }
97
UpdateRecordBeforeChange(int32_t start,int32_t length,UndoRedoRecord & record,bool isOnlyStyleChange)98 void RichEditorUndoManager::UpdateRecordBeforeChange(
99 int32_t start, int32_t length, UndoRedoRecord& record, bool isOnlyStyleChange)
100 {
101 auto pattern = pattern_.Upgrade();
102 CHECK_NULL_VOID(pattern);
103 CHECK_NULL_VOID(IsStyledUndoRedoSupported());
104 record.isOnlyStyleChange = isOnlyStyleChange;
105 auto end = std::min(start + length, pattern->GetTextContentLength());
106 auto rangeBefore = TextRange{ start, end };
107 auto caretAffinityBefore = pattern->caretAffinityPolicy_;
108 if (selectionBefore_.IsValid()) {
109 SetOperationBefore(rangeBefore, selectionBefore_, caretAffinityBefore, record);
110 ClearSelectionBefore();
111 return;
112 }
113 auto caretPosition = pattern->caretPosition_;
114 auto& textSelector = pattern->textSelector_;
115 auto selection = textSelector.SelectNothing() ? TextRange{ caretPosition, caretPosition }
116 : TextRange{ textSelector.GetStart(), textSelector.GetEnd() };
117 SetOperationBefore(rangeBefore, selection, caretAffinityBefore, record);
118 }
119
RecordOperation(const UndoRedoRecord & record,size_t index)120 void RichEditorUndoManager::RecordOperation(const UndoRedoRecord& record, size_t index)
121 {
122 CHECK_NULL_VOID(record.IsValid() && !record.IsEmpty());
123 CHECK_NULL_VOID(index <= undoRecords_.size());
124 undoRecords_.insert(undoRecords_.begin() + index, record);
125 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "RecordOperation [%{public}s] in %{public}zu",
126 record.ToString().c_str(), index);
127 if (undoRecords_.size() >= RECORD_MAX_LENGTH) {
128 undoRecords_.pop_front();
129 }
130 }
131
RecordOperation(const UndoRedoRecord & record,bool isFromRedo)132 void RichEditorUndoManager::RecordOperation(const UndoRedoRecord& record, bool isFromRedo)
133 {
134 CHECK_NULL_VOID(record.IsValid() && !record.IsEmpty());
135 if (undoRecords_.size() >= RECORD_MAX_LENGTH) {
136 undoRecords_.pop_front();
137 }
138 undoRecords_.push_back(record);
139 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "RecordOperation [%{public}s]", record.ToString().c_str());
140 CountRecord();
141 IF_TRUE(!isFromRedo, redoRecords_.clear());
142 }
143
RecordUndoOperation(const UndoRedoRecord & record)144 void RichEditorUndoManager::RecordUndoOperation(const UndoRedoRecord& record)
145 {
146 CHECK_NULL_VOID(record.IsValid());
147 if (redoRecords_.size() >= RECORD_MAX_LENGTH) {
148 redoRecords_.erase(redoRecords_.begin());
149 }
150 redoRecords_.push_back(record);
151 }
152
153 // Records initial state when preview input start with selection
RecordPreviewInputtingStart(int32_t start,int32_t length)154 void RichEditorUndoManager::RecordPreviewInputtingStart(int32_t start, int32_t length)
155 {
156 ClearPreviewInputRecord();
157 CHECK_NULL_VOID(length > 0);
158 UpdateRecordBeforeChange(start, length, previewInputRecord_);
159 TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "PreviewInputtingStart [%{public}s]", previewInputRecord_.ToString().c_str());
160 }
161
RecordPreviewInputtingEnd(const UndoRedoRecord & record)162 bool RichEditorUndoManager::RecordPreviewInputtingEnd(const UndoRedoRecord& record)
163 {
164 CHECK_NULL_RETURN(IsPreviewInputStartWithSelection(), false);
165 previewInputRecord_.CopyOperationAfter(record);
166 CHECK_NULL_RETURN(previewInputRecord_.IsValid(), false);
167 RecordOperation(previewInputRecord_);
168 ClearPreviewInputRecord();
169 return true;
170 }
171
RecordInsertOperation(const UndoRedoRecord & record)172 void RichEditorUndoManager::RecordInsertOperation(const UndoRedoRecord& record)
173 {
174 // Record insert operation by initial state from preview input start
175 CHECK_NULL_VOID(!RecordPreviewInputtingEnd(record));
176 RecordOperation(record);
177 ClearPreviewInputRecord();
178 }
179
RecordOperationAfterChange(int32_t start,int32_t length,UndoRedoRecord & record)180 void RichEditorUndoManager::RecordOperationAfterChange(int32_t start, int32_t length, UndoRedoRecord& record)
181 {
182 UpdateRecordAfterChange(start, length, record);
183 RecordOperation(record);
184 }
185
IsStyledUndoRedoSupported()186 bool StyledStringUndoManager::IsStyledUndoRedoSupported()
187 {
188 auto pattern = pattern_.Upgrade();
189 CHECK_NULL_RETURN(pattern, false);
190 return pattern->IsStyledStringModeEnabled();
191 }
192
BeforeChangeByRecord(const UndoRedoRecord & record,bool isUndo)193 bool StyledStringUndoManager::BeforeChangeByRecord(const UndoRedoRecord& record, bool isUndo)
194 {
195 auto pattern = pattern_.Upgrade();
196 CHECK_NULL_RETURN(pattern, false);
197 return pattern->BeforeStyledStringChange(record, isUndo);
198 }
199
AfterChangeByRecord(const UndoRedoRecord & record,bool isUndo)200 void StyledStringUndoManager::AfterChangeByRecord(const UndoRedoRecord& record, bool isUndo)
201 {
202 auto pattern = pattern_.Upgrade();
203 CHECK_NULL_VOID(pattern);
204 pattern->AfterStyledStringChange(record, isUndo);
205 }
206
ApplyOperationToRecord(int32_t start,int32_t length,const std::u16string & string,UndoRedoRecord & record)207 void StyledStringUndoManager::ApplyOperationToRecord(
208 int32_t start, int32_t length, const std::u16string& string, UndoRedoRecord& record)
209 {
210 auto pattern = pattern_.Upgrade();
211 CHECK_NULL_VOID(pattern);
212 CHECK_NULL_VOID(pattern->IsStyledStringModeEnabled());
213 auto styledString = pattern->CreateStyledStringByStyleBefore(start, string);
214 ApplyOperationToRecord(start, length, styledString, record);
215 }
216
ApplyOperationToRecord(int32_t start,int32_t length,const RefPtr<SpanString> & styledString,UndoRedoRecord & record)217 void StyledStringUndoManager::ApplyOperationToRecord(
218 int32_t start, int32_t length, const RefPtr<SpanString>& styledString, UndoRedoRecord& record)
219 {
220 auto pattern = pattern_.Upgrade();
221 CHECK_NULL_VOID(pattern);
222 CHECK_NULL_VOID(pattern->IsStyledStringModeEnabled());
223 UpdateRecordBeforeChange(start, length, record);
224 CHECK_NULL_VOID(styledString);
225 auto stringLength = styledString->GetLength();
226 auto rangeAfter = TextRange{ start, start + stringLength };
227 record.SetOperationAfter(rangeAfter, styledString);
228 }
229
SetOperationBefore(TextRange range,TextRange selection,CaretAffinityPolicy caretAffinity,UndoRedoRecord & record)230 void StyledStringUndoManager::SetOperationBefore(
231 TextRange range, TextRange selection, CaretAffinityPolicy caretAffinity, UndoRedoRecord& record)
232 {
233 auto pattern = pattern_.Upgrade();
234 CHECK_NULL_VOID(pattern);
235 auto styledStringBefore = pattern->GetStyledString()->GetSubSpanString(range.start, range.GetLength());
236 record.SetOperationBefore(range, styledStringBefore, selection, caretAffinity);
237 }
238
UpdateRecordAfterChange(int32_t start,int32_t length,UndoRedoRecord & record)239 void StyledStringUndoManager::UpdateRecordAfterChange(int32_t start, int32_t length, UndoRedoRecord& record)
240 {
241 auto pattern = pattern_.Upgrade();
242 CHECK_NULL_VOID(pattern);
243 CHECK_NULL_VOID(pattern->IsStyledStringModeEnabled());
244 auto styledString = pattern->GetStyledString()->GetSubSpanString(start, length);
245 CHECK_NULL_VOID(styledString);
246 auto end = std::min(start + length, pattern->GetTextContentLength());
247 auto rangeAfter = TextRange{ start, end };
248 record.SetOperationAfter(rangeAfter, styledString);
249 }
250
IsStyledUndoRedoSupported()251 bool SpansUndoManager::IsStyledUndoRedoSupported()
252 {
253 auto pattern = pattern_.Upgrade();
254 CHECK_NULL_RETURN(pattern, false);
255 return pattern->IsSupportStyledUndo();
256 }
257
BeforeChangeByRecord(const UndoRedoRecord & record,bool isUndo)258 bool SpansUndoManager::BeforeChangeByRecord(const UndoRedoRecord& record, bool isUndo)
259 {
260 CHECK_NULL_RETURN(!record.isOnlyStyleChange, true);
261 auto pattern = pattern_.Upgrade();
262 CHECK_NULL_RETURN(pattern, false);
263 return pattern->BeforeSpansChange(record, isUndo);
264 }
265
RemoveBuilderSpanOptions(const RefPtr<NG::UINode> customNode)266 void SpansUndoManager::RemoveBuilderSpanOptions(const RefPtr<NG::UINode> customNode)
267 {
268 CHECK_NULL_VOID(customNode);
269 RemoveBuilderSpanOptions(undoRecords_, customNode);
270 RemoveBuilderSpanOptions(redoRecords_, customNode);
271 }
272
RemoveBuilderSpanOptions(std::deque<UndoRedoRecord> & records,const RefPtr<NG::UINode> customNode)273 void SpansUndoManager::RemoveBuilderSpanOptions(std::deque<UndoRedoRecord>& records,
274 const RefPtr<NG::UINode> customNode)
275 {
276 CHECK_NULL_VOID(customNode);
277 for (auto& record : records) {
278 CHECK_NULL_CONTINUE(record.IsRestoreBuilderSpan());
279 IF_TRUE(record.optionsListBefore.has_value(),
280 RemoveBuilderSpanOptions(record.optionsListBefore.value(), customNode));
281 IF_TRUE(record.optionsListAfter.has_value(),
282 RemoveBuilderSpanOptions(record.optionsListAfter.value(), customNode));
283 }
284 }
285
RemoveBuilderSpanOptions(OptionsList & optionsList,const RefPtr<NG::UINode> customNode)286 void SpansUndoManager::RemoveBuilderSpanOptions(OptionsList& optionsList, const RefPtr<NG::UINode> customNode)
287 {
288 CHECK_NULL_VOID(customNode);
289 for (auto& option : optionsList) {
290 if (const auto* builderOpt = std::get_if<BuilderSpanOptions>(&option)) {
291 bool needRemove = builderOpt->customNode && builderOpt->customNode == customNode;
292 IF_TRUE(needRemove, (option = TextSpanOptions{ .offset = builderOpt->offset, .value = u" " }));
293 }
294 }
295 }
296
AfterChangeByRecord(const UndoRedoRecord & record,bool isUndo)297 void SpansUndoManager::AfterChangeByRecord(const UndoRedoRecord& record, bool isUndo)
298 {
299 auto pattern = pattern_.Upgrade();
300 CHECK_NULL_VOID(pattern);
301 pattern->AfterSpansChange(record, isUndo);
302 }
303
RecordAddSpanOperation(const RefPtr<SpanItem> & item,SpanOptionsType type)304 void SpansUndoManager::RecordAddSpanOperation(const RefPtr<SpanItem>& item, SpanOptionsType type)
305 {
306 CHECK_NULL_VOID(item);
307 auto pattern = pattern_.Upgrade();
308 CHECK_NULL_VOID(pattern && pattern->IsSupportStyledUndo());
309 auto start = item->rangeStart;
310 UndoRedoRecord record;
311 UpdateRecordBeforeChange(start, 0, record);
312 int32_t length = static_cast<int32_t>(item->content.length());
313 SpanOptions options;
314 switch (type) {
315 case SpanOptionsType::TEXT: {
316 options = CreateTextSpanOptions(item);
317 break;
318 }
319 case SpanOptionsType::IMAGE: {
320 options = CreateImageSpanOptions(AceType::DynamicCast<ImageSpanItem>(item));
321 break;
322 }
323 case SpanOptionsType::SYMBOL: {
324 options = CreateSymbolSpanOptions(item);
325 break;
326 }
327 case SpanOptionsType::BUILDER: {
328 options = CreateBuilderSpanOptions(AceType::DynamicCast<PlaceholderSpanItem>(item));
329 break;
330 }
331 default:
332 break;
333 }
334 record.SetOperationAfter(TextRange{ start, start + length }, OptionsList{ options });
335 RecordOperation(record);
336 }
337
SetOperationBefore(TextRange range,TextRange selection,CaretAffinityPolicy caretAffinity,UndoRedoRecord & record)338 void SpansUndoManager::SetOperationBefore(
339 TextRange range, TextRange selection, CaretAffinityPolicy caretAffinity, UndoRedoRecord& record)
340 {
341 auto pattern = pattern_.Upgrade();
342 CHECK_NULL_VOID(pattern && pattern->IsSupportStyledUndo());
343 record.SetOperationBefore(range, CreateOptionsListByRange(range), selection, caretAffinity);
344 }
345
UpdateRecordAfterChange(int32_t start,int32_t length,UndoRedoRecord & record)346 void SpansUndoManager::UpdateRecordAfterChange(int32_t start, int32_t length, UndoRedoRecord& record)
347 {
348 auto pattern = pattern_.Upgrade();
349 CHECK_NULL_VOID(pattern && pattern->IsSupportStyledUndo());
350 auto end = std::min(start + length, pattern->GetTextContentLength());
351 auto rangeAfter = TextRange{ start, end };
352 record.SetOperationAfter(rangeAfter, CreateOptionsListByRange(rangeAfter));
353 }
354
CreateOptionsListByRange(TextRange range)355 OptionsList SpansUndoManager::CreateOptionsListByRange(TextRange range)
356 {
357 OptionsList optionsList;
358 auto pattern = pattern_.Upgrade();
359 CHECK_NULL_RETURN(pattern, optionsList);
360 CHECK_NULL_RETURN(range.GetLength() != 0, optionsList);
361 auto spansInfo = pattern->GetSpansInfoByRange(range.start, range.end);
362 auto& resultObjects = spansInfo.GetSelectionRef().resultObjects;
363 for (const ResultObject& spanObject : resultObjects) {
364 optionsList.push_back(CreateSpanOptionsBySpanObject(spanObject));
365 }
366 return optionsList;
367 }
368
CreateSpanOptionsBySpanObject(const ResultObject & object)369 SpanOptions SpansUndoManager::CreateSpanOptionsBySpanObject(const ResultObject& object)
370 {
371 auto type = object.type;
372 if (type == SelectSpanType::TYPEIMAGE && object.valueString == u" " && object.valuePixelMap == nullptr) {
373 type = SelectSpanType::TYPEBUILDERSPAN;
374 }
375 switch (type) {
376 case SelectSpanType::TYPESPAN:
377 return CreateTextSpanOptions(object);
378 case SelectSpanType::TYPEIMAGE:
379 return CreateImageSpanOptions(object);
380 case SelectSpanType::TYPESYMBOLSPAN:
381 return CreateSymbolSpanOptions(object);
382 case SelectSpanType::TYPEBUILDERSPAN:
383 return CreateBuilderSpanOptions(object);
384 default:
385 break;
386 }
387 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "CreateSpanOptions: Unhandled span type");
388 return TextSpanOptions{};
389 }
390
CreateTextSpanOptions(const ResultObject & object)391 TextSpanOptions SpansUndoManager::CreateTextSpanOptions(const ResultObject& object)
392 {
393 auto pattern = pattern_.Upgrade();
394 CHECK_NULL_RETURN(pattern, {});
395 auto spanItem = pattern->GetSpanItemByIndex(object.spanPosition.spanIndex);
396 CHECK_NULL_RETURN(pattern, {});
397 auto options = pattern->GetTextSpanOptions(spanItem);
398 auto offsetInSpanStart = object.offsetInSpan[0];
399 auto offsetInSpanEnd = object.offsetInSpan[1];
400 auto spanStart = object.spanPosition.spanRange[0];
401 options.offset = spanStart + offsetInSpanStart;
402 options.value = object.valueString.substr(offsetInSpanStart, offsetInSpanEnd - offsetInSpanStart);
403 return options;
404 }
405
CreateTextSpanOptions(const RefPtr<SpanItem> & item)406 TextSpanOptions SpansUndoManager::CreateTextSpanOptions(const RefPtr<SpanItem>& item)
407 {
408 CHECK_NULL_RETURN(item, {});
409 auto pattern = pattern_.Upgrade();
410 CHECK_NULL_RETURN(pattern, {});
411 auto options = pattern->GetTextSpanOptions(item);
412 options.offset = item->rangeStart;
413 return options;
414 }
415
CreateImageSpanOptions(const ResultObject & object)416 ImageSpanOptions SpansUndoManager::CreateImageSpanOptions(const ResultObject& object)
417 {
418 auto pattern = pattern_.Upgrade();
419 CHECK_NULL_RETURN(pattern, {});
420 auto spanItem = pattern->GetSpanItemByIndex(object.spanPosition.spanIndex);
421 return CreateImageSpanOptions(AceType::DynamicCast<ImageSpanItem>(spanItem));
422 }
423
CreateImageSpanOptions(const RefPtr<ImageSpanItem> & item)424 ImageSpanOptions SpansUndoManager::CreateImageSpanOptions(const RefPtr<ImageSpanItem>& item)
425 {
426 CHECK_NULL_RETURN(item, {});
427 auto options = item->options;
428 options.offset = item->rangeStart;
429 return options;
430 }
431
CreateSymbolSpanOptions(const ResultObject & object)432 SymbolSpanOptions SpansUndoManager::CreateSymbolSpanOptions(const ResultObject& object)
433 {
434 auto pattern = pattern_.Upgrade();
435 CHECK_NULL_RETURN(pattern, {});
436 auto spanItem = pattern->GetSpanItemByIndex(object.spanPosition.spanIndex);
437 return CreateSymbolSpanOptions(spanItem);
438 }
439
CreateSymbolSpanOptions(const RefPtr<SpanItem> & item)440 SymbolSpanOptions SpansUndoManager::CreateSymbolSpanOptions(const RefPtr<SpanItem>& item)
441 {
442 CHECK_NULL_RETURN(item, {});
443 auto pattern = pattern_.Upgrade();
444 CHECK_NULL_RETURN(pattern, {});
445 auto options = pattern->GetSymbolSpanOptions(item);
446 options.paraStyle = pattern->GetParagraphStyle(item);
447 options.offset = item->rangeStart;
448 return options;
449 }
450
CreateBuilderSpanOptions(const ResultObject & object)451 BuilderSpanOptions SpansUndoManager::CreateBuilderSpanOptions(const ResultObject& object)
452 {
453 auto pattern = pattern_.Upgrade();
454 CHECK_NULL_RETURN(pattern, {});
455 auto spanItem = pattern->GetSpanItemByIndex(object.spanPosition.spanIndex);
456 return CreateBuilderSpanOptions(AceType::DynamicCast<PlaceholderSpanItem>(spanItem));
457 }
458
CreateBuilderSpanOptions(const RefPtr<PlaceholderSpanItem> & item)459 BuilderSpanOptions SpansUndoManager::CreateBuilderSpanOptions(const RefPtr<PlaceholderSpanItem>& item)
460 {
461 CHECK_NULL_RETURN(item, {});
462 BuilderSpanOptions options;
463 options.customNode = item->GetCustomNode();
464 options.offset = item->rangeStart;
465 return options;
466 }
467 }