refactor: move document.dart to core/document

This commit is contained in:
Lucas.Xu 2022-10-10 14:58:28 +08:00
commit d0f6d2e398
6 changed files with 4 additions and 4 deletions

View file

@ -7,7 +7,7 @@ export 'src/core/document/node.dart';
export 'src/core/document/path.dart';
export 'src/core/location/position.dart';
export 'src/core/location/selection.dart';
export 'src/core/state/document.dart';
export 'src/core/document/document.dart';
export 'src/core/document/text_delta.dart';
export 'src/core/document/attributes.dart';
export 'src/core/legacy/built_in_attribute_keys.dart';

View file

@ -0,0 +1,118 @@
import 'dart:collection';
import 'package:appflowy_editor/src/core/document/attributes.dart';
import 'package:appflowy_editor/src/core/document/node.dart';
import 'package:appflowy_editor/src/core/document/path.dart';
import 'package:appflowy_editor/src/core/document/text_delta.dart';
/// [Document] reprensents a AppFlowy Editor document structure.
///
/// It stores the root of the document.
///
/// DO NOT directly mutate the properties of a [Document] object.
class Document {
Document({
required this.root,
});
factory Document.fromJson(Map<String, dynamic> json) {
assert(json['document'] is Map);
final document = Map<String, Object>.from(json['document'] as Map);
final root = Node.fromJson(document);
return Document(root: root);
}
/// Creates a empty document with a single text node.
factory Document.empty() {
final root = Node(
type: 'editor',
children: LinkedList<Node>()..add(TextNode.empty()),
);
return Document(
root: root,
);
}
final Node root;
/// Returns the node at the given [path].
Node? nodeAtPath(Path path) {
return root.childAtPath(path);
}
/// Inserts a [Node]s at the given [Path].
bool insert(Path path, List<Node> nodes) {
if (path.isEmpty || nodes.isEmpty) {
return false;
}
final target = nodeAtPath(path);
if (target != null) {
for (final node in nodes) {
target.insertBefore(node);
}
return true;
}
final parent = nodeAtPath(path.parent);
if (parent != null) {
for (final node in nodes) {
parent.insert(node, index: path.last);
}
return true;
}
return false;
}
/// Deletes the [Node]s at the given [Path].
bool delete(Path path, [int length = 1]) {
if (path.isEmpty || length <= 0) {
return false;
}
var target = nodeAtPath(path);
if (target == null) {
return false;
}
while (target != null && length > 0) {
final next = target.next;
target.unlink();
target = next;
length--;
}
return true;
}
/// Updates the [Node] at the given [Path]
bool update(Path path, Attributes attributes) {
if (path.isEmpty) {
return false;
}
final target = nodeAtPath(path);
if (target == null) {
return false;
}
target.updateAttributes(attributes);
return true;
}
/// Updates the [TextNode] at the given [Path]
bool updateText(Path path, Delta delta) {
if (path.isEmpty) {
return false;
}
final target = nodeAtPath(path);
if (target == null || target is! TextNode) {
return false;
}
target.delta = target.delta.compose(delta);
return true;
}
Map<String, Object> toJson() {
return {
'document': root.toJson(),
};
}
}

View file

@ -1,5 +1,5 @@
import 'package:appflowy_editor/src/core/document/node.dart';
import 'package:appflowy_editor/src/core/state/document.dart';
import 'package:appflowy_editor/src/core/document/document.dart';
/// [NodeIterator] is used to traverse the nodes in visual order.
class NodeIterator implements Iterator<Node> {

View file

@ -6,7 +6,7 @@ import 'package:appflowy_editor/src/service/service.dart';
import 'package:flutter/material.dart';
import 'package:appflowy_editor/src/core/location/selection.dart';
import 'package:appflowy_editor/src/core/state/document.dart';
import 'package:appflowy_editor/src/core/document/document.dart';
import 'package:appflowy_editor/src/operation/operation.dart';
import 'package:appflowy_editor/src/operation/transaction.dart';
import 'package:appflowy_editor/src/undo_manager.dart';

View file

@ -0,0 +1,77 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter_test/flutter_test.dart';
void main() async {
group('documemnt.dart', () {
test('insert', () {
final document = Document.empty();
expect(document.insert([-1], []), false);
expect(document.insert([100], []), false);
final node0 = Node(type: '0');
final node1 = Node(type: '1');
expect(document.insert([0], [node0, node1]), true);
expect(document.nodeAtPath([0])?.type, '0');
expect(document.nodeAtPath([1])?.type, '1');
});
test('delete', () {
final document = Document(root: Node(type: 'root'));
expect(document.delete([-1], 1), false);
expect(document.delete([100], 1), false);
for (var i = 0; i < 10; i++) {
final node = Node(type: '$i');
document.insert([i], [node]);
}
document.delete([0], 10);
expect(document.root.children.isEmpty, true);
});
test('update', () {
final node = Node(type: 'example', attributes: {'a': 'a'});
final document = Document(root: Node(type: 'root'));
document.insert([0], [node]);
final attributes = {
'a': 'b',
'b': 'c',
};
expect(document.update([0], attributes), true);
expect(document.nodeAtPath([0])?.attributes, attributes);
expect(document.update([-1], attributes), false);
});
test('updateText', () {
final delta = Delta()..insert('Editor');
final textNode = TextNode(delta: delta);
final document = Document(root: Node(type: 'root'));
document.insert([0], [textNode]);
document.updateText([0], Delta()..insert('AppFlowy'));
expect((document.nodeAtPath([0]) as TextNode).toPlainText(),
'AppFlowyEditor');
});
test('serialize', () {
final json = {
'document': {
'type': 'editor',
'children': [
{
'type': 'text',
'delta': [],
}
],
'attributes': {'a': 'a'}
}
};
final document = Document.fromJson(json);
expect(document.toJson(), json);
});
});
}

View file

@ -5,7 +5,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:appflowy_editor/src/operation/operation.dart';
import 'package:appflowy_editor/src/operation/transaction_builder.dart';
import 'package:appflowy_editor/src/editor_state.dart';
import 'package:appflowy_editor/src/core/state/document.dart';
import 'package:appflowy_editor/src/core/document/document.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();