Skip to content

Commit 6bda1fd

Browse files
committed
fix: delete the nested bulleted list will lost the children nodes
1 parent d648f2b commit 6bda1fd

File tree

4 files changed

+123
-114
lines changed

4 files changed

+123
-114
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import 'package:appflowy_editor/src/document/node.dart';
2+
3+
class Infra {
4+
// find the forward nearest text node
5+
static TextNode? forwardNearestTextNode(Node node) {
6+
var previous = node.previous;
7+
while (previous != null) {
8+
final lastTextNode = findLastTextNode(previous);
9+
if (lastTextNode != null) {
10+
return lastTextNode;
11+
}
12+
if (previous is TextNode) {
13+
return previous;
14+
}
15+
previous = previous.previous;
16+
}
17+
final parent = node.parent;
18+
if (parent != null) {
19+
if (parent is TextNode) {
20+
return parent;
21+
}
22+
return forwardNearestTextNode(parent);
23+
}
24+
return null;
25+
}
26+
27+
// find the last text node
28+
static TextNode? findLastTextNode(Node node) {
29+
final children = node.children.toList(growable: false).reversed;
30+
for (final child in children) {
31+
if (child.children.isNotEmpty) {
32+
final result = findLastTextNode(child);
33+
if (result != null) {
34+
return result;
35+
}
36+
}
37+
if (child is TextNode) {
38+
return child;
39+
}
40+
}
41+
if (node is TextNode) {
42+
return node;
43+
}
44+
return null;
45+
}
46+
}

frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/backspace_handler.dart

Lines changed: 17 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import 'package:appflowy_editor/src/infra/infra.dart';
12
import 'package:appflowy_editor/src/service/internal_key_event_handlers/number_list_helper.dart';
23
import 'package:flutter/material.dart';
34
import 'package:flutter/services.dart';
45
import 'package:appflowy_editor/appflowy_editor.dart';
6+
import 'package:appflowy_editor/src/extensions/path_extensions.dart';
57

68
// Handle delete text.
79
ShortcutEventHandler deleteTextHandler = (editorState, event) {
@@ -126,33 +128,34 @@ KeyEventResult _backDeleteToPreviousTextNode(
126128
List<Node> nonTextNodes,
127129
Selection selection,
128130
) {
129-
// Not reach to the root.
130-
// if (textNode.parent?.parent != null) {
131-
// transactionBuilder
132-
// ..deleteNode(textNode)
133-
// ..insertNode(textNode.parent!.path.next, textNode)
134-
// ..afterSelection = Selection.collapsed(
135-
// Position(path: textNode.parent!.path.next, offset: 0),
136-
// )
137-
// ..commit();
138-
// return KeyEventResult.handled;
139-
// }
131+
if (textNode.next == null &&
132+
textNode.children.isEmpty &&
133+
textNode.parent?.parent != null) {
134+
transactionBuilder
135+
..deleteNode(textNode)
136+
..insertNode(textNode.parent!.path.next, textNode)
137+
..afterSelection = Selection.collapsed(
138+
Position(path: textNode.parent!.path.next, offset: 0),
139+
)
140+
..commit();
141+
return KeyEventResult.handled;
142+
}
140143

141144
bool prevIsNumberList = false;
142-
final previousTextNode = forwardNearestTextNode(textNode);
145+
final previousTextNode = Infra.forwardNearestTextNode(textNode);
143146
if (previousTextNode != null) {
144147
if (previousTextNode.subtype == BuiltInAttributeKey.numberList) {
145148
prevIsNumberList = true;
146149
}
147150

148151
transactionBuilder.mergeText(previousTextNode, textNode);
149-
transactionBuilder.deleteNode(textNode);
150152
if (textNode.children.isNotEmpty) {
151153
transactionBuilder.insertNodes(
152-
previousTextNode.path + [0],
154+
previousTextNode.path.next,
153155
textNode.children.toList(growable: false),
154156
);
155157
}
158+
transactionBuilder.deleteNode(textNode);
156159
transactionBuilder.afterSelection = Selection.collapsed(
157160
Position(
158161
path: previousTextNode.path,
@@ -273,46 +276,3 @@ void _deleteTextNodes(TransactionBuilder transactionBuilder,
273276
secondOffset: selection.end.offset,
274277
);
275278
}
276-
277-
// TODO: Just a simple solution for textNode, need to be optimized.
278-
TextNode? findLastTextNode(Node node) {
279-
final children = node.children.toList(growable: false).reversed;
280-
for (final child in children) {
281-
if (child.children.isNotEmpty) {
282-
final result = findLastTextNode(child);
283-
if (result != null) {
284-
return result;
285-
}
286-
}
287-
if (child is TextNode) {
288-
return child;
289-
}
290-
}
291-
if (node is TextNode) {
292-
return node;
293-
}
294-
return null;
295-
}
296-
297-
// find the forward nearest text node
298-
TextNode? forwardNearestTextNode(Node node) {
299-
var previous = node.previous;
300-
while (previous != null) {
301-
final lastTextNode = findLastTextNode(previous);
302-
if (lastTextNode != null) {
303-
return lastTextNode;
304-
}
305-
if (previous is TextNode) {
306-
return previous;
307-
}
308-
previous = previous.previous;
309-
}
310-
final parent = node.parent;
311-
if (parent != null) {
312-
if (parent is TextNode) {
313-
return parent;
314-
}
315-
return forwardNearestTextNode(parent);
316-
}
317-
return null;
318-
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import 'package:appflowy_editor/appflowy_editor.dart';
2+
import 'package:appflowy_editor/src/infra/infra.dart';
3+
import 'package:flutter_test/flutter_test.dart';
4+
5+
void main() async {
6+
group('infra.dart', () {
7+
test('find the last text node', () {
8+
// * Welcome to Appflowy 😁
9+
// * Welcome to Appflowy 😁
10+
// * Welcome to Appflowy 😁
11+
// * Welcome to Appflowy 😁
12+
// * Welcome to Appflowy 😁
13+
// * Welcome to Appflowy 😁
14+
// * Welcome to Appflowy 😁
15+
const text = 'Welcome to Appflowy 😁';
16+
TextNode textNode() {
17+
return TextNode(
18+
type: 'text',
19+
delta: Delta()..insert(text),
20+
);
21+
}
22+
23+
final node110 = textNode();
24+
final node111 = textNode();
25+
final node11 = textNode()
26+
..insert(node110)
27+
..insert(node111);
28+
final node10 = textNode();
29+
final node1 = textNode()
30+
..insert(node10)
31+
..insert(node11);
32+
final node0 = textNode();
33+
final node = textNode()
34+
..insert(node0)
35+
..insert(node1);
36+
37+
expect(Infra.findLastTextNode(node)?.path, [1, 1, 1]);
38+
expect(Infra.findLastTextNode(node0)?.path, [0]);
39+
expect(Infra.findLastTextNode(node1)?.path, [1, 1, 1]);
40+
expect(Infra.findLastTextNode(node10)?.path, [1, 0]);
41+
expect(Infra.findLastTextNode(node11)?.path, [1, 1, 1]);
42+
43+
expect(Infra.forwardNearestTextNode(node111)?.path, [1, 1, 0]);
44+
expect(Infra.forwardNearestTextNode(node110)?.path, [1, 1]);
45+
expect(Infra.forwardNearestTextNode(node11)?.path, [1, 0]);
46+
expect(Infra.forwardNearestTextNode(node10)?.path, [1]);
47+
expect(Infra.forwardNearestTextNode(node1)?.path, [0]);
48+
expect(Infra.forwardNearestTextNode(node0)?.path, []);
49+
});
50+
});
51+
}

frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/backspace_handler_test.dart

Lines changed: 9 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import 'dart:collection';
33
import 'package:appflowy_editor/appflowy_editor.dart';
44
import 'package:appflowy_editor/src/render/image/image_node_widget.dart';
55
import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart';
6-
import 'package:appflowy_editor/src/service/internal_key_event_handlers/backspace_handler.dart';
76
import 'package:flutter/services.dart';
87
import 'package:flutter_test/flutter_test.dart';
98
import 'package:network_image_mock/network_image_mock.dart';
@@ -383,73 +382,26 @@ void main() async {
383382
// * Welcome to Appflowy 😁
384383
// After
385384
// * Welcome to Appflowy 😁
385+
// * Welcome to Appflowy 😁Welcome to Appflowy 😁
386+
// * Welcome to Appflowy 😁
386387
// * Welcome to Appflowy 😁
387-
// Welcome to Appflowy 😁
388-
// * Welcome to Appflowy 😁
389-
// * Welcome to Appflowy 😁
390388
await editor.pressLogicKey(LogicalKeyboardKey.backspace);
391389
expect(
392-
editor.nodeAtPath([1])!.subtype != BuiltInAttributeKey.bulletedList,
390+
editor.nodeAtPath([0, 0])!.subtype == BuiltInAttributeKey.bulletedList,
393391
true,
394392
);
395393
expect(
396-
editor.nodeAtPath([1, 0])!.subtype == BuiltInAttributeKey.bulletedList,
394+
(editor.nodeAtPath([0, 0]) as TextNode).toRawString() == text * 2,
397395
true,
398396
);
399397
expect(
400-
editor.nodeAtPath([1, 1])!.subtype == BuiltInAttributeKey.bulletedList,
398+
editor.nodeAtPath([0, 1])!.subtype == BuiltInAttributeKey.bulletedList,
399+
true,
400+
);
401+
expect(
402+
editor.nodeAtPath([0, 2])!.subtype == BuiltInAttributeKey.bulletedList,
401403
true,
402404
);
403-
404-
// After
405-
// * Welcome to Appflowy 😁
406-
// * Welcome to Appflowy 😁Welcome to Appflowy 😁
407-
// * Welcome to Appflowy 😁
408-
// * Welcome to Appflowy 😁
409-
});
410-
411-
test('find the last text node', () {
412-
// * Welcome to Appflowy 😁
413-
// * Welcome to Appflowy 😁
414-
// * Welcome to Appflowy 😁
415-
// * Welcome to Appflowy 😁
416-
// * Welcome to Appflowy 😁
417-
// * Welcome to Appflowy 😁
418-
// * Welcome to Appflowy 😁
419-
const text = 'Welcome to Appflowy 😁';
420-
TextNode textNode() {
421-
return TextNode(
422-
type: 'text',
423-
delta: Delta()..insert(text),
424-
);
425-
}
426-
427-
final node110 = textNode();
428-
final node111 = textNode();
429-
final node11 = textNode()
430-
..insert(node110)
431-
..insert(node111);
432-
final node10 = textNode();
433-
final node1 = textNode()
434-
..insert(node10)
435-
..insert(node11);
436-
final node0 = textNode();
437-
final node = textNode()
438-
..insert(node0)
439-
..insert(node1);
440-
441-
expect(findLastTextNode(node)?.path, [1, 1, 1]);
442-
expect(findLastTextNode(node0)?.path, [0]);
443-
expect(findLastTextNode(node1)?.path, [1, 1, 1]);
444-
expect(findLastTextNode(node10)?.path, [1, 0]);
445-
expect(findLastTextNode(node11)?.path, [1, 1, 1]);
446-
447-
expect(forwardNearestTextNode(node111)?.path, [1, 1, 0]);
448-
expect(forwardNearestTextNode(node110)?.path, [1, 1]);
449-
expect(forwardNearestTextNode(node11)?.path, [1, 0]);
450-
expect(forwardNearestTextNode(node10)?.path, [1]);
451-
expect(forwardNearestTextNode(node1)?.path, [0]);
452-
expect(forwardNearestTextNode(node0)?.path, []);
453405
});
454406
}
455407

0 commit comments

Comments
 (0)