Merge pull request #838 from AppFlowy-IO/feat/deep-copy-nodes

Feat: deep clone nodes
This commit is contained in:
Vincent Chan 2022-08-12 19:35:53 +08:00 committed by GitHub
commit 5b1c66ee1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 20 deletions

View File

@ -163,6 +163,18 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
} }
return parent!._path([index, ...previous]); return parent!._path([index, ...previous]);
} }
Node deepClone() {
final newNode = Node(
type: type, children: LinkedList<Node>(), attributes: {...attributes});
for (final node in children) {
final newNode = node.deepClone();
newNode.parent = this;
newNode.children.add(newNode);
}
return newNode;
}
} }
class TextNode extends Node { class TextNode extends Node {
@ -213,5 +225,21 @@ class TextNode extends Node {
delta: delta ?? this.delta, delta: delta ?? this.delta,
); );
@override
TextNode deepClone() {
final newNode = TextNode(
type: type,
children: LinkedList<Node>(),
delta: delta.slice(0),
attributes: {...attributes});
for (final node in children) {
final newNode = node.deepClone();
newNode.parent = this;
newNode.children.add(newNode);
}
return newNode;
}
String toRawString() => _delta.toRawString(); String toRawString() => _delta.toRawString();
} }

View File

@ -417,7 +417,8 @@ class Delta extends Iterable<TextOperation> {
// Optimization if rest of other is just retain // Optimization if rest of other is just retain
if (!otherIter.hasNext && if (!otherIter.hasNext &&
delta._operations[delta._operations.length - 1] == newOp) { delta._operations.isNotEmpty &&
delta._operations.last == newOp) {
final rest = Delta(thisIter.rest()); final rest = Delta(thisIter.rest());
return (delta + rest)..chop(); return (delta + rest)..chop();
} }

View File

@ -36,7 +36,7 @@ class TransactionBuilder {
/// Insert a sequence of nodes at the position of path. /// Insert a sequence of nodes at the position of path.
insertNodes(Path path, List<Node> nodes) { insertNodes(Path path, List<Node> nodes) {
beforeSelection = state.cursorSelection; beforeSelection = state.cursorSelection;
add(InsertOperation(path, nodes)); add(InsertOperation(path, nodes.map((node) => node.deepClone()).toList()));
} }
/// Update the attributes of nodes. /// Update the attributes of nodes.
@ -75,7 +75,7 @@ class TransactionBuilder {
nodes.add(node); nodes.add(node);
} }
add(DeleteOperation(path, nodes)); add(DeleteOperation(path, nodes.map((node) => node.deepClone()).toList()));
} }
textEdit(TextNode node, Delta Function() f) { textEdit(TextNode node, Delta Function() f) {
@ -203,6 +203,9 @@ class TransactionBuilder {
for (var i = 0; i < operations.length; i++) { for (var i = 0; i < operations.length; i++) {
op = transformOperation(operations[i], op); op = transformOperation(operations[i], op);
} }
if (op is TextEditOperation && op.delta.isEmpty) {
return;
}
operations.add(op); operations.add(op);
} }

View File

@ -6,11 +6,10 @@ import 'package:flutter/services.dart';
import 'package:rich_clipboard/rich_clipboard.dart'; import 'package:rich_clipboard/rich_clipboard.dart';
_handleCopy(EditorState editorState) async { _handleCopy(EditorState editorState) async {
var selection = editorState.cursorSelection; final selection = editorState.cursorSelection?.normalize();
if (selection == null || selection.isCollapsed) { if (selection == null || selection.isCollapsed) {
return; return;
} }
selection = selection.normalize();
if (pathEquals(selection.start.path, selection.end.path)) { if (pathEquals(selection.start.path, selection.end.path)) {
final nodeAtPath = editorState.document.nodeAtPath(selection.end.path)!; final nodeAtPath = editorState.document.nodeAtPath(selection.end.path)!;
if (nodeAtPath.type == "text") { if (nodeAtPath.type == "text") {
@ -43,11 +42,13 @@ _handleCopy(EditorState editorState) async {
} }
_pasteHTML(EditorState editorState, String html) { _pasteHTML(EditorState editorState, String html) {
final selection = editorState.cursorSelection; final selection = editorState.cursorSelection?.normalize();
if (selection == null) { if (selection == null) {
return; return;
} }
assert(selection.isCollapsed);
final path = [...selection.end.path]; final path = [...selection.end.path];
if (path.isEmpty) { if (path.isEmpty) {
return; return;
@ -124,6 +125,20 @@ _pasteMultipleLinesInText(
_handlePaste(EditorState editorState) async { _handlePaste(EditorState editorState) async {
final data = await RichClipboard.getData(); final data = await RichClipboard.getData();
if (editorState.cursorSelection?.isCollapsed ?? false) {
_pastRichClipboard(editorState, data);
return;
}
_deleteSelectedContent(editorState);
WidgetsBinding.instance.addPostFrameCallback((_) {
_pastRichClipboard(editorState, data);
});
}
_pastRichClipboard(EditorState editorState, RichClipboardData data) {
if (data.html != null) { if (data.html != null) {
_pasteHTML(editorState, data.html!); _pasteHTML(editorState, data.html!);
return; return;
@ -135,7 +150,7 @@ _handlePaste(EditorState editorState) async {
} }
_handlePastePlainText(EditorState editorState, String plainText) { _handlePastePlainText(EditorState editorState, String plainText) {
final selection = editorState.cursorSelection; final selection = editorState.cursorSelection?.normalize();
if (selection == null) { if (selection == null) {
return; return;
} }
@ -208,22 +223,13 @@ _handlePastePlainText(EditorState editorState, String plainText) {
/// 2. delete selected content /// 2. delete selected content
_handleCut(EditorState editorState) { _handleCut(EditorState editorState) {
debugPrint('cut'); debugPrint('cut');
final selection = editorState.cursorSelection;
if (selection == null) {
return;
}
if (selection.isCollapsed) {
return;
}
_handleCopy(editorState); _handleCopy(editorState);
_deleteSelectedContent(editorState); _deleteSelectedContent(editorState);
} }
_deleteSelectedContent(EditorState editorState) { _deleteSelectedContent(EditorState editorState) {
final selection = editorState.cursorSelection; final selection = editorState.cursorSelection?.normalize();
if (selection == null) { if (selection == null || selection.isCollapsed) {
return; return;
} }
final beginNode = editorState.document.nodeAtPath(selection.start.path)!; final beginNode = editorState.document.nodeAtPath(selection.start.path)!;
@ -262,11 +268,11 @@ _deleteSelectedContent(EditorState editorState) {
return delta; return delta;
}); });
tb.setAfterSelection(Selection.collapsed(selection.start));
} else { } else {
tb.deleteNode(item); tb.deleteNode(item);
} }
} }
tb.setAfterSelection(Selection.collapsed(selection.start));
tb.commit(); tb.commit();
} }

View File

@ -86,7 +86,7 @@ FlowyKeyEventHandler enterWithoutShiftInTextNodesHandler =
); );
TransactionBuilder(editorState) TransactionBuilder(editorState)
..insertNode( ..insertNode(
textNode.path, textNode.path.next,
TextNode.empty(), TextNode.empty(),
) )
..afterSelection = afterSelection ..afterSelection = afterSelection