• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }