commitSaveState method
Called AFTER a mutation (from FluentDocument.updateContent). Compares the current top-level nodes with the pending snapshot, builds a minimal DocumentDelta, and pushes it onto the undo stack. Returns a SaveStateResult indicating whether a new delta was created, merged into an existing delta, or if no changes occurred.
Implementation
SaveStateResult commitSaveState(FluentDocument document) {
if (_isRestoringState || _pending == null) return SaveStateResult.noChange;
final pending = _pending!;
final newNodes = document.content.nodes;
final oldNodes = pending.oldTopLevelNodes;
final changes = <NodeChange>[];
if (newNodes.length == oldNodes.length) {
for (int i = 0; i < oldNodes.length; i++) {
final oldJson = oldNodes[i];
final newNode = newNodes[i];
final newJson = newNode.toJson();
if (!_mapsEqual(oldJson, newJson)) {
final frozenNewJson = _deepCopyJsonMap(newJson);
changes.add(NodeChange(
index: i,
oldJson: oldJson,
newJson: frozenNewJson,
));
}
}
} else {
final maxLen = oldNodes.length > newNodes.length
? oldNodes.length
: newNodes.length;
for (int i = 0; i < maxLen; i++) {
final oldJson = i < oldNodes.length ? oldNodes[i] : null;
final newNode = i < newNodes.length ? newNodes[i] : null;
final newJson = newNode?.toJson();
if (oldJson == null || newJson == null || !_mapsEqual(oldJson, newJson)) {
changes.add(NodeChange(
index: i,
oldJson: oldJson ?? <String, dynamic>{},
newJson: newJson != null ? _deepCopyJsonMap(newJson) : <String, dynamic>{},
));
}
}
}
if (changes.isEmpty) {
_pending = null;
return SaveStateResult.noChange;
}
final delta = NodeReplaceDelta(
description: pending.description,
timestamp: pending.timestamp,
changes: changes,
oldCursor: pending.oldCursor,
newCursor: CursorSnapshot.fromDocument(document),
);
if (_currentGroupDescription != null &&
_currentGroupDescription == pending.description &&
_undoStack.isNotEmpty &&
!_forceNewAction) {
final lastDelta = _undoStack.last;
if (lastDelta is NodeReplaceDelta &&
lastDelta.description == pending.description) {
final mergedChanges = <int, NodeChange>{};
for (final c in lastDelta.changes) {
mergedChanges[c.index] = c;
}
for (final c in delta.changes) {
final existing = mergedChanges[c.index];
if (existing != null) {
mergedChanges[c.index] = NodeChange(
index: c.index,
oldJson: existing.oldJson,
newJson: c.newJson,
);
} else {
mergedChanges[c.index] = c;
}
}
final mergedDelta = NodeReplaceDelta(
description: pending.description,
timestamp: lastDelta.timestamp,
changes: mergedChanges.values.toList(),
oldCursor: lastDelta.oldCursor,
newCursor: delta.newCursor,
);
_undoStack.last = mergedDelta;
_pending = null;
_redoStack.clear();
_updateJsonCacheFromChanges(document, mergedChanges.values.toList());
_enforceMemoryLimit();
return SaveStateResult.merged;
}
}
_undoStack.add(delta);
_redoStack.clear();
_pending = null;
_updateJsonCacheFromChanges(document, changes);
_enforceMemoryLimit();
return SaveStateResult.created;
}