refactor: move document.dart to core/document
This commit is contained in:
parent
33ff8a09da
commit
d0f6d2e398
6 changed files with 4 additions and 4 deletions
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue