diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/appflowy_editor.dart b/frontend/app_flowy/packages/appflowy_editor/lib/appflowy_editor.dart index e9d22990e5..61dbcc87e6 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/appflowy_editor.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/appflowy_editor.dart @@ -9,7 +9,7 @@ export 'src/document/position.dart'; export 'src/document/selection.dart'; export 'src/document/state_tree.dart'; export 'src/document/text_delta.dart'; -export 'src/document/attributes.dart'; +export 'src/core/document/attributes.dart'; export 'src/document/built_in_attribute_keys.dart'; export 'src/editor_state.dart'; export 'src/operation/operation.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_built_in_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_built_in_text.dart index 758941b198..3a558d0ba6 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_built_in_text.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_built_in_text.dart @@ -1,6 +1,6 @@ import 'package:appflowy_editor/src/commands/format_text.dart'; import 'package:appflowy_editor/src/commands/text_command_infra.dart'; -import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/core/document/attributes.dart'; import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/core/document/node.dart'; import 'package:appflowy_editor/src/document/path.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_text.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_text.dart index 4ed0f80ce1..dbe02aa8c1 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_text.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/commands/format_text.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:appflowy_editor/src/commands/text_command_infra.dart'; -import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/core/document/attributes.dart'; import 'package:appflowy_editor/src/core/document/node.dart'; import 'package:appflowy_editor/src/document/path.dart'; import 'package:appflowy_editor/src/document/selection.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/document/attributes.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/core/document/attributes.dart similarity index 53% rename from frontend/app_flowy/packages/appflowy_editor/lib/src/document/attributes.dart rename to frontend/app_flowy/packages/appflowy_editor/lib/src/core/document/attributes.dart index ff1e258dfc..163e8e9ab6 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/document/attributes.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/core/document/attributes.dart @@ -23,21 +23,27 @@ Attributes? composeAttributes( return attributes.isNotEmpty ? attributes : null; } -Attributes invertAttributes(Attributes? base, Attributes? other) { - base ??= {}; - other ??= {}; - final Attributes attributes = base.keys.fold({}, (previousValue, key) { - if (other!.containsKey(key) && other[key] != base![key]) { - previousValue[key] = base[key]; +Attributes invertAttributes(Attributes? from, Attributes? to) { + from ??= {}; + to ??= {}; + final attributes = Attributes.from({}); + + // key in from but not in to, or value is different + for (final entry in from.entries) { + if ((!to.containsKey(entry.key) && entry.value != null) || + to[entry.key] != entry.value) { + attributes[entry.key] = entry.value; } - return previousValue; - }); - return other.keys.fold(attributes, (previousValue, key) { - if (!base!.containsKey(key) && other![key] != base[key]) { - previousValue[key] = null; + } + + // key in to but not in from, or value is different + for (final entry in to.entries) { + if (!from.containsKey(entry.key) && entry.value != null) { + attributes[entry.key] = null; } - return previousValue; - }); + } + + return attributes; } int hashAttributes(Attributes base) => Object.hashAllUnordered( diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/core/document/node.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/core/document/node.dart index 50087194f4..8ed6570f77 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/core/document/node.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/core/document/node.dart @@ -2,7 +2,7 @@ import 'dart:collection'; import 'package:flutter/material.dart'; -import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/core/document/attributes.dart'; import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/document/path.dart'; import 'package:appflowy_editor/src/document/text_delta.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/document/state_tree.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/document/state_tree.dart index 73e0b5f61f..6b1b5fec7d 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/document/state_tree.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/document/state_tree.dart @@ -3,7 +3,7 @@ import 'dart:math'; import 'package:appflowy_editor/src/core/document/node.dart'; import 'package:appflowy_editor/src/document/path.dart'; import 'package:appflowy_editor/src/document/text_delta.dart'; -import './attributes.dart'; +import '../core/document/attributes.dart'; class StateTree { final Node root; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/document/text_delta.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/document/text_delta.dart index 0d9bc7f2ef..d509a1ef14 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/document/text_delta.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/document/text_delta.dart @@ -1,7 +1,7 @@ import 'dart:collection'; import 'dart:math'; -import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/core/document/attributes.dart'; import 'package:flutter/foundation.dart'; // constant number: 2^53 - 1 diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart index b8970d8225..403a007d7f 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/extensions/attributes_extension.dart @@ -1,4 +1,4 @@ -import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/core/document/attributes.dart'; import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:flutter/material.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/infra/html_converter.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/infra/html_converter.dart index e941e023f1..70f3884e4e 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/infra/html_converter.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/infra/html_converter.dart @@ -1,7 +1,7 @@ import 'dart:collection'; import 'dart:ui'; -import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/core/document/attributes.dart'; import 'package:appflowy_editor/src/core/document/node.dart'; import 'package:appflowy_editor/src/document/text_delta.dart'; import 'package:appflowy_editor/src/extensions/color_extension.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/operation/transaction_builder.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/operation/transaction_builder.dart index 7b606a8441..64127701b4 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/operation/transaction_builder.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/operation/transaction_builder.dart @@ -1,7 +1,7 @@ import 'dart:collection'; import 'dart:math'; -import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/core/document/attributes.dart'; import 'package:appflowy_editor/src/core/document/node.dart'; import 'package:appflowy_editor/src/document/path.dart'; import 'package:appflowy_editor/src/document/position.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart index 620816f165..94e34c5297 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/default_text_operations/format_rich_text_style.dart @@ -1,4 +1,4 @@ -import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/core/document/attributes.dart'; import 'package:appflowy_editor/src/core/document/node.dart'; import 'package:appflowy_editor/src/document/position.dart'; import 'package:appflowy_editor/src/document/selection.dart'; diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/number_list_helper.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/number_list_helper.dart index ed9018a71e..0c2a3ab9e2 100644 --- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/number_list_helper.dart +++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/number_list_helper.dart @@ -2,7 +2,7 @@ import 'package:appflowy_editor/src/document/selection.dart'; import 'package:appflowy_editor/src/editor_state.dart'; import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart'; import 'package:appflowy_editor/src/operation/transaction_builder.dart'; -import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/core/document/attributes.dart'; void makeFollowingNodesIncremental( EditorState editorState, List insertPath, Selection afterSelection, diff --git a/frontend/app_flowy/packages/appflowy_editor/test/core/document/attributes_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/core/document/attributes_test.dart new file mode 100644 index 0000000000..873ab2788b --- /dev/null +++ b/frontend/app_flowy/packages/appflowy_editor/test/core/document/attributes_test.dart @@ -0,0 +1,59 @@ +import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() async { + group('attributes.dart', () { + test('composeAttributes', () { + final base = { + 'a': 1, + 'b': 2, + }; + final other = { + 'b': 3, + 'c': 4, + 'd': null, + }; + expect(composeAttributes(base, other, keepNull: false), { + 'a': 1, + 'b': 3, + 'c': 4, + }); + expect(composeAttributes(base, other, keepNull: true), { + 'a': 1, + 'b': 3, + 'c': 4, + 'd': null, + }); + expect(composeAttributes(null, other, keepNull: false), { + 'b': 3, + 'c': 4, + }); + expect(composeAttributes(base, null, keepNull: false), { + 'a': 1, + 'b': 2, + }); + }); + + test('invertAttributes', () { + final base = { + 'a': 1, + 'b': 2, + }; + final other = { + 'b': 3, + 'c': 4, + 'd': null, + }; + expect(invertAttributes(base, other), { + 'a': 1, + 'b': 2, + 'c': null, + }); + expect(invertAttributes(other, base), { + 'a': null, + 'b': 3, + 'c': 4, + }); + }); + }); +} diff --git a/frontend/app_flowy/packages/appflowy_editor/test/document/node_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/core/document/node_test.dart similarity index 70% rename from frontend/app_flowy/packages/appflowy_editor/test/document/node_test.dart rename to frontend/app_flowy/packages/appflowy_editor/test/core/document/node_test.dart index cf03d98cdf..aa78bf7a7c 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/document/node_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/core/document/node_test.dart @@ -147,5 +147,68 @@ void main() async { expect(identical(node.children, base.children), false); expect(identical(node.children.first, base.children.first), false); }); + + test('test insert', () { + final base = Node( + type: 'base', + ); + + // insert at the front + final childA = Node( + type: 'child', + ); + base.insert(childA, index: -1); + expect( + identical(base.childAtIndex(0), childA), + true, + ); + + // insert at the last + final childB = Node( + type: 'child', + ); + base.insert(childB, index: 1000); + expect( + identical(base.childAtIndex(base.children.length - 1), childB), + true, + ); + + // insert at the last + final childC = Node( + type: 'child', + ); + base.insert(childC); + expect( + identical(base.childAtIndex(base.children.length - 1), childC), + true, + ); + }); + + test('test fromJson', () { + final node = Node.fromJson({ + 'type': 'example', + 'attributes': { + 'example': 'example', + }, + 'children': [ + { + 'type': 'example', + 'attributes': { + 'example': 'example', + }, + }, + ], + }); + expect(node.type, 'example'); + expect(node.attributes, {'example': 'example'}); + expect(node.children.length, 1); + expect(node.children.first.type, 'example'); + expect(node.children.first.attributes, {'example': 'example'}); + }); + + test('test toPlainText', () { + final textNode = TextNode.empty()..delta = (Delta()..insert('AppFlowy')); + expect(textNode.toPlainText(), 'AppFlowy'); + }); }); } diff --git a/frontend/app_flowy/packages/appflowy_editor/test/legacy/delta_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/legacy/delta_test.dart index 4593c757d3..3c8d4a3dfe 100644 --- a/frontend/app_flowy/packages/appflowy_editor/test/legacy/delta_test.dart +++ b/frontend/app_flowy/packages/appflowy_editor/test/legacy/delta_test.dart @@ -1,4 +1,4 @@ -import 'package:appflowy_editor/src/document/attributes.dart'; +import 'package:appflowy_editor/src/core/document/attributes.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:appflowy_editor/src/document/text_delta.dart';