mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
refactor: move node to core/document
This commit is contained in:
parent
8d6e1cdaa1
commit
11eca2b3d9
@ -60,7 +60,7 @@ SelectionMenuItem codeBlockMenuItem = SelectionMenuItem(
|
|||||||
if (selection == null || textNodes.isEmpty) {
|
if (selection == null || textNodes.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (textNodes.first.toRawString().isEmpty) {
|
if (textNodes.first.toPlainText().isEmpty) {
|
||||||
TransactionBuilder(editorState)
|
TransactionBuilder(editorState)
|
||||||
..updateNode(textNodes.first, {
|
..updateNode(textNodes.first, {
|
||||||
'subtype': 'code_block',
|
'subtype': 'code_block',
|
||||||
@ -74,7 +74,6 @@ SelectionMenuItem codeBlockMenuItem = SelectionMenuItem(
|
|||||||
..insertNode(
|
..insertNode(
|
||||||
selection.end.path.next,
|
selection.end.path.next,
|
||||||
TextNode(
|
TextNode(
|
||||||
type: 'text',
|
|
||||||
children: LinkedList(),
|
children: LinkedList(),
|
||||||
attributes: {
|
attributes: {
|
||||||
'subtype': 'code_block',
|
'subtype': 'code_block',
|
||||||
@ -149,7 +148,7 @@ class __CodeBlockNodeWidgeState extends State<_CodeBlockNodeWidge>
|
|||||||
|
|
||||||
Widget _buildCodeBlock(BuildContext context) {
|
Widget _buildCodeBlock(BuildContext context) {
|
||||||
final result = highlight.highlight.parse(
|
final result = highlight.highlight.parse(
|
||||||
widget.textNode.toRawString(),
|
widget.textNode.toPlainText(),
|
||||||
language: _language,
|
language: _language,
|
||||||
autoDetection: _language == null,
|
autoDetection: _language == null,
|
||||||
);
|
);
|
||||||
|
@ -17,7 +17,7 @@ ShortcutEventHandler _insertHorzaontalRule = (editorState, event) {
|
|||||||
return KeyEventResult.ignored;
|
return KeyEventResult.ignored;
|
||||||
}
|
}
|
||||||
final textNode = textNodes.first;
|
final textNode = textNodes.first;
|
||||||
if (textNode.toRawString() == '--') {
|
if (textNode.toPlainText() == '--') {
|
||||||
TransactionBuilder(editorState)
|
TransactionBuilder(editorState)
|
||||||
..deleteText(textNode, 0, 2)
|
..deleteText(textNode, 0, 2)
|
||||||
..insertNode(
|
..insertNode(
|
||||||
@ -53,7 +53,7 @@ SelectionMenuItem horizontalRuleMenuItem = SelectionMenuItem(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final textNode = textNodes.first;
|
final textNode = textNodes.first;
|
||||||
if (textNode.toRawString().isEmpty) {
|
if (textNode.toPlainText().isEmpty) {
|
||||||
TransactionBuilder(editorState)
|
TransactionBuilder(editorState)
|
||||||
..insertNode(
|
..insertNode(
|
||||||
textNode.path,
|
textNode.path,
|
||||||
@ -71,7 +71,6 @@ SelectionMenuItem horizontalRuleMenuItem = SelectionMenuItem(
|
|||||||
..insertNode(
|
..insertNode(
|
||||||
selection.end.path.next,
|
selection.end.path.next,
|
||||||
TextNode(
|
TextNode(
|
||||||
type: 'text',
|
|
||||||
children: LinkedList(),
|
children: LinkedList(),
|
||||||
attributes: {
|
attributes: {
|
||||||
'subtype': 'horizontal_rule',
|
'subtype': 'horizontal_rule',
|
||||||
|
@ -21,7 +21,7 @@ SelectionMenuItem teXBlockMenuItem = SelectionMenuItem(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Path texNodePath;
|
final Path texNodePath;
|
||||||
if (textNodes.first.toRawString().isEmpty) {
|
if (textNodes.first.toPlainText().isEmpty) {
|
||||||
texNodePath = selection.end.path;
|
texNodePath = selection.end.path;
|
||||||
TransactionBuilder(editorState)
|
TransactionBuilder(editorState)
|
||||||
..insertNode(
|
..insertNode(
|
||||||
|
@ -18,7 +18,7 @@ ShortcutEventHandler _underscoreToItalicHandler = (editorState, event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final textNode = textNodes.first;
|
final textNode = textNodes.first;
|
||||||
final text = textNode.toRawString();
|
final text = textNode.toPlainText();
|
||||||
// Determine if an 'underscore' already exists in the text node and only once.
|
// Determine if an 'underscore' already exists in the text node and only once.
|
||||||
final firstUnderscore = text.indexOf('_');
|
final firstUnderscore = text.indexOf('_');
|
||||||
final lastUnderscore = text.lastIndexOf('_');
|
final lastUnderscore = text.lastIndexOf('_');
|
||||||
|
@ -3,7 +3,7 @@ library appflowy_editor;
|
|||||||
|
|
||||||
export 'src/infra/log.dart';
|
export 'src/infra/log.dart';
|
||||||
export 'src/render/style/editor_style.dart';
|
export 'src/render/style/editor_style.dart';
|
||||||
export 'src/document/node.dart';
|
export 'src/core/document/node.dart';
|
||||||
export 'src/document/path.dart';
|
export 'src/document/path.dart';
|
||||||
export 'src/document/position.dart';
|
export 'src/document/position.dart';
|
||||||
export 'src/document/selection.dart';
|
export 'src/document/selection.dart';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/commands/text_command_infra.dart';
|
import 'package:appflowy_editor/src/commands/text_command_infra.dart';
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/path.dart';
|
import 'package:appflowy_editor/src/document/path.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
import 'package:appflowy_editor/src/operation/transaction_builder.dart';
|
import 'package:appflowy_editor/src/operation/transaction_builder.dart';
|
||||||
|
@ -2,7 +2,7 @@ import 'package:appflowy_editor/src/commands/format_text.dart';
|
|||||||
import 'package:appflowy_editor/src/commands/text_command_infra.dart';
|
import 'package:appflowy_editor/src/commands/text_command_infra.dart';
|
||||||
import 'package:appflowy_editor/src/document/attributes.dart';
|
import 'package:appflowy_editor/src/document/attributes.dart';
|
||||||
import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart';
|
import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart';
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/path.dart';
|
import 'package:appflowy_editor/src/document/path.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
|
@ -2,7 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:appflowy_editor/src/commands/text_command_infra.dart';
|
import 'package:appflowy_editor/src/commands/text_command_infra.dart';
|
||||||
import 'package:appflowy_editor/src/document/attributes.dart';
|
import 'package:appflowy_editor/src/document/attributes.dart';
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/path.dart';
|
import 'package:appflowy_editor/src/document/path.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/path.dart';
|
import 'package:appflowy_editor/src/document/path.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
|
@ -1,47 +1,21 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:appflowy_editor/src/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/path.dart';
|
||||||
import 'package:appflowy_editor/src/document/text_delta.dart';
|
import 'package:appflowy_editor/src/document/text_delta.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import './attributes.dart';
|
|
||||||
|
|
||||||
class Node extends ChangeNotifier with LinkedListEntry<Node> {
|
class Node extends ChangeNotifier with LinkedListEntry<Node> {
|
||||||
Node? parent;
|
|
||||||
final String type;
|
|
||||||
final LinkedList<Node> children;
|
|
||||||
Attributes _attributes;
|
|
||||||
|
|
||||||
GlobalKey? key;
|
|
||||||
// TODO: abstract a selectable node??
|
|
||||||
final layerLink = LayerLink();
|
|
||||||
|
|
||||||
String? get subtype {
|
|
||||||
// TODO: make 'subtype' as a const value.
|
|
||||||
if (_attributes.containsKey('subtype')) {
|
|
||||||
assert(_attributes['subtype'] is String?,
|
|
||||||
'subtype must be a [String] or [null]');
|
|
||||||
return _attributes['subtype'] as String?;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String get id {
|
|
||||||
if (subtype != null) {
|
|
||||||
return '$type/$subtype';
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
Path get path => _path();
|
|
||||||
|
|
||||||
Attributes get attributes => _attributes;
|
|
||||||
|
|
||||||
Node({
|
Node({
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.children,
|
Attributes? attributes,
|
||||||
required Attributes attributes,
|
|
||||||
this.parent,
|
this.parent,
|
||||||
}) : _attributes = attributes {
|
LinkedList<Node>? children,
|
||||||
for (final child in children) {
|
}) : children = children ?? LinkedList<Node>(),
|
||||||
|
_attributes = attributes ?? {} {
|
||||||
|
for (final child in this.children) {
|
||||||
child.parent = this;
|
child.parent = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,14 +23,13 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
|
|||||||
factory Node.fromJson(Map<String, Object> json) {
|
factory Node.fromJson(Map<String, Object> json) {
|
||||||
assert(json['type'] is String);
|
assert(json['type'] is String);
|
||||||
|
|
||||||
// TODO: check the type that not exist on plugins.
|
|
||||||
final jType = json['type'] as String;
|
final jType = json['type'] as String;
|
||||||
final jChildren = json['children'] as List?;
|
final jChildren = json['children'] as List?;
|
||||||
final jAttributes = json['attributes'] != null
|
final jAttributes = json['attributes'] != null
|
||||||
? Attributes.from(json['attributes'] as Map)
|
? Attributes.from(json['attributes'] as Map)
|
||||||
: Attributes.from({});
|
: Attributes.from({});
|
||||||
|
|
||||||
final LinkedList<Node> children = LinkedList();
|
final children = LinkedList<Node>();
|
||||||
if (jChildren != null) {
|
if (jChildren != null) {
|
||||||
children.addAll(
|
children.addAll(
|
||||||
jChildren.map(
|
jChildren.map(
|
||||||
@ -69,14 +42,14 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
|
|||||||
|
|
||||||
Node node;
|
Node node;
|
||||||
|
|
||||||
if (jType == "text") {
|
if (jType == 'text') {
|
||||||
final jDelta = json['delta'] as List<dynamic>?;
|
final jDelta = json['delta'] as List<dynamic>?;
|
||||||
final delta = jDelta == null ? Delta() : Delta.fromJson(jDelta);
|
final delta = jDelta == null ? Delta() : Delta.fromJson(jDelta);
|
||||||
node = TextNode(
|
node = TextNode(
|
||||||
type: jType,
|
children: children,
|
||||||
children: children,
|
attributes: jAttributes,
|
||||||
attributes: jAttributes,
|
delta: delta,
|
||||||
delta: delta);
|
);
|
||||||
} else {
|
} else {
|
||||||
node = Node(
|
node = Node(
|
||||||
type: jType,
|
type: jType,
|
||||||
@ -92,20 +65,48 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final String type;
|
||||||
|
final LinkedList<Node> children;
|
||||||
|
Node? parent;
|
||||||
|
Attributes _attributes;
|
||||||
|
|
||||||
|
// Renderable
|
||||||
|
GlobalKey? key;
|
||||||
|
final layerLink = LayerLink();
|
||||||
|
|
||||||
|
Attributes get attributes => {..._attributes};
|
||||||
|
|
||||||
|
String get id {
|
||||||
|
if (subtype != null) {
|
||||||
|
return '$type/$subtype';
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? get subtype {
|
||||||
|
if (attributes[BuiltInAttributeKey.subtype] is String) {
|
||||||
|
return attributes[BuiltInAttributeKey.subtype] as String;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Path get path => _computePath();
|
||||||
|
|
||||||
void updateAttributes(Attributes attributes) {
|
void updateAttributes(Attributes attributes) {
|
||||||
final oldAttributes = {..._attributes};
|
final oldAttributes = this.attributes;
|
||||||
_attributes = composeAttributes(_attributes, attributes) ?? {};
|
|
||||||
|
_attributes = composeAttributes(this.attributes, attributes) ?? {};
|
||||||
|
|
||||||
// Notifies the new attributes
|
// Notifies the new attributes
|
||||||
// if attributes contains 'subtype', should notify parent to rebuild node
|
// if attributes contains 'subtype', should notify parent to rebuild node
|
||||||
// else, just notify current node.
|
// else, just notify current node.
|
||||||
bool shouldNotifyParent =
|
bool shouldNotifyParent =
|
||||||
_attributes['subtype'] != oldAttributes['subtype'];
|
this.attributes['subtype'] != oldAttributes['subtype'];
|
||||||
shouldNotifyParent ? parent?.notifyListeners() : notifyListeners();
|
shouldNotifyParent ? parent?.notifyListeners() : notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Node? childAtIndex(int index) {
|
Node? childAtIndex(int index) {
|
||||||
if (children.length <= index) {
|
if (children.length <= index || index < 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +122,8 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void insert(Node entry, {int? index}) {
|
void insert(Node entry, {int? index}) {
|
||||||
index ??= children.length;
|
final length = children.length;
|
||||||
|
index ??= length;
|
||||||
|
|
||||||
if (children.isEmpty) {
|
if (children.isEmpty) {
|
||||||
entry.parent = this;
|
entry.parent = this;
|
||||||
@ -130,8 +132,9 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final length = children.length;
|
// If index is out of range, insert at the end.
|
||||||
|
// If index is negative, insert at the beginning.
|
||||||
|
// If index is positive, insert at the index.
|
||||||
if (index >= length) {
|
if (index >= length) {
|
||||||
children.last.insertAfter(entry);
|
children.last.insertAfter(entry);
|
||||||
} else if (index <= 0) {
|
} else if (index <= 0) {
|
||||||
@ -173,28 +176,14 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
|
|||||||
};
|
};
|
||||||
if (children.isNotEmpty) {
|
if (children.isNotEmpty) {
|
||||||
map['children'] =
|
map['children'] =
|
||||||
(children.map((node) => node.toJson())).toList(growable: false);
|
children.map((node) => node.toJson()).toList(growable: false);
|
||||||
}
|
}
|
||||||
if (_attributes.isNotEmpty) {
|
if (attributes.isNotEmpty) {
|
||||||
map['attributes'] = _attributes;
|
map['attributes'] = attributes;
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
Path _path([Path previous = const []]) {
|
|
||||||
if (parent == null) {
|
|
||||||
return previous;
|
|
||||||
}
|
|
||||||
var index = 0;
|
|
||||||
for (var child in parent!.children) {
|
|
||||||
if (child == this) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
return parent!._path([index, ...previous]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Node copyWith({
|
Node copyWith({
|
||||||
String? type,
|
String? type,
|
||||||
LinkedList<Node>? children,
|
LinkedList<Node>? children,
|
||||||
@ -202,8 +191,8 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
|
|||||||
}) {
|
}) {
|
||||||
final node = Node(
|
final node = Node(
|
||||||
type: type ?? this.type,
|
type: type ?? this.type,
|
||||||
attributes: attributes ?? {..._attributes},
|
attributes: attributes ?? {...this.attributes},
|
||||||
children: children ?? LinkedList(),
|
children: children,
|
||||||
);
|
);
|
||||||
if (children == null && this.children.isNotEmpty) {
|
if (children == null && this.children.isNotEmpty) {
|
||||||
for (final child in this.children) {
|
for (final child in this.children) {
|
||||||
@ -214,19 +203,31 @@ class Node extends ChangeNotifier with LinkedListEntry<Node> {
|
|||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Path _computePath([Path previous = const []]) {
|
||||||
|
if (parent == null) {
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
var index = 0;
|
||||||
|
for (final child in parent!.children) {
|
||||||
|
if (child == this) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
return parent!._computePath([index, ...previous]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextNode extends Node {
|
class TextNode extends Node {
|
||||||
Delta _delta;
|
|
||||||
|
|
||||||
TextNode({
|
TextNode({
|
||||||
required super.type,
|
|
||||||
required Delta delta,
|
required Delta delta,
|
||||||
LinkedList<Node>? children,
|
LinkedList<Node>? children,
|
||||||
Attributes? attributes,
|
Attributes? attributes,
|
||||||
}) : _delta = delta,
|
}) : _delta = delta,
|
||||||
super(
|
super(
|
||||||
children: children ?? LinkedList(),
|
type: 'text',
|
||||||
|
children: children,
|
||||||
attributes: attributes ?? {},
|
attributes: attributes ?? {},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -234,14 +235,11 @@ class TextNode extends Node {
|
|||||||
: _delta = Delta([TextInsert('')]),
|
: _delta = Delta([TextInsert('')]),
|
||||||
super(
|
super(
|
||||||
type: 'text',
|
type: 'text',
|
||||||
children: LinkedList(),
|
|
||||||
attributes: attributes ?? {},
|
attributes: attributes ?? {},
|
||||||
);
|
);
|
||||||
|
|
||||||
Delta get delta {
|
Delta _delta;
|
||||||
return _delta;
|
Delta get delta => _delta;
|
||||||
}
|
|
||||||
|
|
||||||
set delta(Delta v) {
|
set delta(Delta v) {
|
||||||
_delta = v;
|
_delta = v;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -250,21 +248,20 @@ class TextNode extends Node {
|
|||||||
@override
|
@override
|
||||||
Map<String, Object> toJson() {
|
Map<String, Object> toJson() {
|
||||||
final map = super.toJson();
|
final map = super.toJson();
|
||||||
map['delta'] = _delta.toJson();
|
map['delta'] = delta.toJson();
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TextNode copyWith({
|
TextNode copyWith({
|
||||||
String? type,
|
String? type = 'text',
|
||||||
LinkedList<Node>? children,
|
LinkedList<Node>? children,
|
||||||
Attributes? attributes,
|
Attributes? attributes,
|
||||||
Delta? delta,
|
Delta? delta,
|
||||||
}) {
|
}) {
|
||||||
final textNode = TextNode(
|
final textNode = TextNode(
|
||||||
type: type ?? this.type,
|
|
||||||
children: children,
|
children: children,
|
||||||
attributes: attributes ?? _attributes,
|
attributes: attributes ?? this.attributes,
|
||||||
delta: delta ?? this.delta,
|
delta: delta ?? this.delta,
|
||||||
);
|
);
|
||||||
if (children == null && this.children.isNotEmpty) {
|
if (children == null && this.children.isNotEmpty) {
|
||||||
@ -277,5 +274,5 @@ class TextNode extends Node {
|
|||||||
return textNode;
|
return textNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
String toRawString() => _delta.toRawString();
|
String toPlainText() => _delta.toPlainText();
|
||||||
}
|
}
|
@ -1,42 +1,45 @@
|
|||||||
|
/// Attributes is used to describe the Node's information.
|
||||||
|
///
|
||||||
|
/// Please note: The keywords in [BuiltInAttributeKey] are reserved.
|
||||||
typedef Attributes = Map<String, dynamic>;
|
typedef Attributes = Map<String, dynamic>;
|
||||||
|
|
||||||
int hashAttributes(Attributes attributes) {
|
Attributes? composeAttributes(
|
||||||
return Object.hashAllUnordered(
|
Attributes? base,
|
||||||
attributes.entries.map((e) => Object.hash(e.key, e.value)));
|
Attributes? other, {
|
||||||
}
|
keepNull = false,
|
||||||
|
}) {
|
||||||
Attributes invertAttributes(Attributes? attr, Attributes? base) {
|
|
||||||
attr ??= {};
|
|
||||||
base ??= {};
|
base ??= {};
|
||||||
final Attributes baseInverted = base.keys.fold({}, (memo, key) {
|
other ??= {};
|
||||||
if (base![key] != attr![key] && attr.containsKey(key)) {
|
Attributes attributes = {
|
||||||
memo[key] = base[key];
|
...base,
|
||||||
}
|
...other,
|
||||||
return memo;
|
};
|
||||||
});
|
|
||||||
return attr.keys.fold(baseInverted, (memo, key) {
|
|
||||||
if (attr![key] != base![key] && !base.containsKey(key)) {
|
|
||||||
memo[key] = null;
|
|
||||||
}
|
|
||||||
return memo;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Attributes? composeAttributes(Attributes? a, Attributes? b,
|
|
||||||
[bool keepNull = false]) {
|
|
||||||
a ??= {};
|
|
||||||
b ??= {};
|
|
||||||
Attributes attributes = {...b};
|
|
||||||
|
|
||||||
if (!keepNull) {
|
if (!keepNull) {
|
||||||
attributes = Map.from(attributes)..removeWhere((_, value) => value == null);
|
attributes = Attributes.from(attributes)
|
||||||
}
|
..removeWhere((_, value) => value == null);
|
||||||
|
|
||||||
for (final entry in a.entries) {
|
|
||||||
if (!b.containsKey(entry.key)) {
|
|
||||||
attributes[entry.key] = entry.value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return attributes.isNotEmpty ? attributes : null;
|
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];
|
||||||
|
}
|
||||||
|
return previousValue;
|
||||||
|
});
|
||||||
|
return other.keys.fold(attributes, (previousValue, key) {
|
||||||
|
if (!base!.containsKey(key) && other![key] != base[key]) {
|
||||||
|
previousValue[key] = null;
|
||||||
|
}
|
||||||
|
return previousValue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int hashAttributes(Attributes base) => Object.hashAllUnordered(
|
||||||
|
base.entries.map((e) => Object.hash(e.key, e.value)),
|
||||||
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
|
|
||||||
import './state_tree.dart';
|
import './state_tree.dart';
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/path.dart';
|
import 'package:appflowy_editor/src/document/path.dart';
|
||||||
import 'package:appflowy_editor/src/document/text_delta.dart';
|
import 'package:appflowy_editor/src/document/text_delta.dart';
|
||||||
import './attributes.dart';
|
import './attributes.dart';
|
||||||
|
@ -47,7 +47,9 @@ class TextInsert extends TextOperation {
|
|||||||
final contentHash = content.hashCode;
|
final contentHash = content.hashCode;
|
||||||
final attrs = _attributes;
|
final attrs = _attributes;
|
||||||
return Object.hash(
|
return Object.hash(
|
||||||
contentHash, attrs == null ? null : hashAttributes(attrs));
|
contentHash,
|
||||||
|
attrs != null ? hashAttributes(attrs) : null,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -101,7 +103,10 @@ class TextRetain extends TextOperation {
|
|||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
final attrs = _attributes;
|
final attrs = _attributes;
|
||||||
return Object.hash(_length, attrs == null ? null : hashAttributes(attrs));
|
return Object.hash(
|
||||||
|
_length,
|
||||||
|
attrs != null ? hashAttributes(attrs) : null,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -401,7 +406,11 @@ class Delta extends Iterable<TextOperation> {
|
|||||||
final thisOp = thisIter._next(length);
|
final thisOp = thisIter._next(length);
|
||||||
final otherOp = otherIter._next(length);
|
final otherOp = otherIter._next(length);
|
||||||
final attributes = composeAttributes(
|
final attributes = composeAttributes(
|
||||||
thisOp.attributes, otherOp.attributes, thisOp is TextRetain);
|
thisOp.attributes,
|
||||||
|
otherOp.attributes,
|
||||||
|
keepNull: thisOp is TextRetain,
|
||||||
|
);
|
||||||
|
|
||||||
if (otherOp is TextRetain && otherOp.length > 0) {
|
if (otherOp is TextRetain && otherOp.length > 0) {
|
||||||
TextOperation? newOp;
|
TextOperation? newOp;
|
||||||
if (thisOp is TextRetain) {
|
if (thisOp is TextRetain) {
|
||||||
@ -480,8 +489,10 @@ class Delta extends Iterable<TextOperation> {
|
|||||||
if (op is TextDelete) {
|
if (op is TextDelete) {
|
||||||
inverted.add(baseOp);
|
inverted.add(baseOp);
|
||||||
} else if (op is TextRetain && op.attributes != null) {
|
} else if (op is TextRetain && op.attributes != null) {
|
||||||
inverted.retain(baseOp.length,
|
inverted.retain(
|
||||||
invertAttributes(op.attributes, baseOp.attributes));
|
baseOp.length,
|
||||||
|
invertAttributes(baseOp.attributes, op.attributes),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return previousValue + length;
|
return previousValue + length;
|
||||||
@ -520,7 +531,7 @@ class Delta extends Iterable<TextOperation> {
|
|||||||
///
|
///
|
||||||
/// This method can help you to compute the position of the next character.
|
/// This method can help you to compute the position of the next character.
|
||||||
int nextRunePosition(int pos) {
|
int nextRunePosition(int pos) {
|
||||||
final stringContent = toRawString();
|
final stringContent = toPlainText();
|
||||||
if (pos >= stringContent.length - 1) {
|
if (pos >= stringContent.length - 1) {
|
||||||
return stringContent.length;
|
return stringContent.length;
|
||||||
}
|
}
|
||||||
@ -535,7 +546,7 @@ class Delta extends Iterable<TextOperation> {
|
|||||||
return stringContent.length;
|
return stringContent.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
String toRawString() {
|
String toPlainText() {
|
||||||
_rawString ??=
|
_rawString ??=
|
||||||
_operations.whereType<TextInsert>().map((op) => op.content).join();
|
_operations.whereType<TextInsert>().map((op) => op.content).join();
|
||||||
return _rawString!;
|
return _rawString!;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
import 'package:appflowy_editor/src/extensions/object_extensions.dart';
|
import 'package:appflowy_editor/src/extensions/object_extensions.dart';
|
||||||
import 'package:appflowy_editor/src/extensions/path_extensions.dart';
|
import 'package:appflowy_editor/src/extensions/path_extensions.dart';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/path.dart';
|
import 'package:appflowy_editor/src/document/path.dart';
|
||||||
import 'package:appflowy_editor/src/document/position.dart';
|
import 'package:appflowy_editor/src/document/position.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
@ -171,7 +171,7 @@ extension TextNodesExtension on List<TextNode> {
|
|||||||
if (i == 0 && pathEquals(node.path, selection.start.path)) {
|
if (i == 0 && pathEquals(node.path, selection.start.path)) {
|
||||||
if (selection.isBackward) {
|
if (selection.isBackward) {
|
||||||
newSelection = selection.copyWith(
|
newSelection = selection.copyWith(
|
||||||
end: Position(path: node.path, offset: node.toRawString().length),
|
end: Position(path: node.path, offset: node.toPlainText().length),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
newSelection = selection.copyWith(
|
newSelection = selection.copyWith(
|
||||||
@ -187,13 +187,13 @@ extension TextNodesExtension on List<TextNode> {
|
|||||||
} else {
|
} else {
|
||||||
newSelection = selection.copyWith(
|
newSelection = selection.copyWith(
|
||||||
start:
|
start:
|
||||||
Position(path: node.path, offset: node.toRawString().length),
|
Position(path: node.path, offset: node.toPlainText().length),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newSelection = Selection(
|
newSelection = Selection(
|
||||||
start: Position(path: node.path, offset: 0),
|
start: Position(path: node.path, offset: 0),
|
||||||
end: Position(path: node.path, offset: node.toRawString().length),
|
end: Position(path: node.path, offset: node.toPlainText().length),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!node.allSatisfyInSelection(newSelection, styleKey, test)) {
|
if (!node.allSatisfyInSelection(newSelection, styleKey, test)) {
|
||||||
|
@ -2,7 +2,7 @@ import 'dart:collection';
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/document/attributes.dart';
|
import 'package:appflowy_editor/src/document/attributes.dart';
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/text_delta.dart';
|
import 'package:appflowy_editor/src/document/text_delta.dart';
|
||||||
import 'package:appflowy_editor/src/extensions/color_extension.dart';
|
import 'package:appflowy_editor/src/extensions/color_extension.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -89,7 +89,7 @@ class HTMLToNodesConverter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (delta.isNotEmpty) {
|
if (delta.isNotEmpty) {
|
||||||
result.add(TextNode(type: "text", delta: delta));
|
result.add(TextNode(delta: delta));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -134,7 +134,7 @@ class HTMLToNodesConverter {
|
|||||||
final delta = Delta();
|
final delta = Delta();
|
||||||
delta.insert(element.text);
|
delta.insert(element.text);
|
||||||
if (delta.isNotEmpty) {
|
if (delta.isNotEmpty) {
|
||||||
return [TextNode(type: "text", delta: delta)];
|
return [TextNode(delta: delta)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
@ -271,8 +271,7 @@ class HTMLToNodesConverter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final textNode =
|
final textNode = TextNode(delta: delta, attributes: attributes);
|
||||||
TextNode(type: "text", delta: delta, attributes: attributes);
|
|
||||||
if (isCheckbox) {
|
if (isCheckbox) {
|
||||||
textNode.attributes["subtype"] = BuiltInAttributeKey.checkbox;
|
textNode.attributes["subtype"] = BuiltInAttributeKey.checkbox;
|
||||||
textNode.attributes["checkbox"] = checked;
|
textNode.attributes["checkbox"] = checked;
|
||||||
@ -315,7 +314,6 @@ class HTMLToNodesConverter {
|
|||||||
final delta = Delta();
|
final delta = Delta();
|
||||||
delta.insert(element.text);
|
delta.insert(element.text);
|
||||||
return TextNode(
|
return TextNode(
|
||||||
type: "text",
|
|
||||||
attributes: {"subtype": "heading", "heading": headingStyle},
|
attributes: {"subtype": "heading", "heading": headingStyle},
|
||||||
delta: delta);
|
delta: delta);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
|
|
||||||
class Infra {
|
class Infra {
|
||||||
// find the forward nearest text node
|
// find the forward nearest text node
|
||||||
|
@ -2,7 +2,7 @@ import 'dart:collection';
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/document/attributes.dart';
|
import 'package:appflowy_editor/src/document/attributes.dart';
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/path.dart';
|
import 'package:appflowy_editor/src/document/path.dart';
|
||||||
import 'package:appflowy_editor/src/document/position.dart';
|
import 'package:appflowy_editor/src/document/position.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
@ -42,8 +42,7 @@ class TransactionBuilder {
|
|||||||
/// Updates the attributes of nodes.
|
/// Updates the attributes of nodes.
|
||||||
updateNode(Node node, Attributes attributes) {
|
updateNode(Node node, Attributes attributes) {
|
||||||
beforeSelection = state.cursorSelection;
|
beforeSelection = state.cursorSelection;
|
||||||
|
final inverted = invertAttributes(node.attributes, attributes);
|
||||||
final inverted = invertAttributes(attributes, node.attributes);
|
|
||||||
add(UpdateOperation(
|
add(UpdateOperation(
|
||||||
node.path,
|
node.path,
|
||||||
{...attributes},
|
{...attributes},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
import 'package:appflowy_editor/src/service/render_plugin_service.dart';
|
import 'package:appflowy_editor/src/service/render_plugin_service.dart';
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/operation/transaction_builder.dart';
|
import 'package:appflowy_editor/src/operation/transaction_builder.dart';
|
||||||
import 'package:appflowy_editor/src/service/render_plugin_service.dart';
|
import 'package:appflowy_editor/src/service/render_plugin_service.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy_editor/src/extensions/object_extensions.dart';
|
import 'package:appflowy_editor/src/extensions/object_extensions.dart';
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/position.dart';
|
import 'package:appflowy_editor/src/document/position.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
import 'package:appflowy_editor/src/infra/flowy_svg.dart';
|
import 'package:appflowy_editor/src/infra/flowy_svg.dart';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
import 'package:appflowy_editor/src/infra/flowy_svg.dart';
|
import 'package:appflowy_editor/src/infra/flowy_svg.dart';
|
||||||
import 'package:appflowy_editor/src/operation/transaction_builder.dart';
|
import 'package:appflowy_editor/src/operation/transaction_builder.dart';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
import 'package:appflowy_editor/src/infra/flowy_svg.dart';
|
import 'package:appflowy_editor/src/infra/flowy_svg.dart';
|
||||||
import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
|
import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
|
||||||
|
@ -5,7 +5,7 @@ import 'package:flutter/gestures.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/path.dart';
|
import 'package:appflowy_editor/src/document/path.dart';
|
||||||
import 'package:appflowy_editor/src/document/position.dart';
|
import 'package:appflowy_editor/src/document/position.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
@ -163,7 +163,7 @@ class _FlowyRichTextState extends State<FlowyRichText> with SelectableMixin {
|
|||||||
Widget _buildRichText(BuildContext context) {
|
Widget _buildRichText(BuildContext context) {
|
||||||
return MouseRegion(
|
return MouseRegion(
|
||||||
cursor: SystemMouseCursors.text,
|
cursor: SystemMouseCursors.text,
|
||||||
child: widget.textNode.toRawString().isEmpty
|
child: widget.textNode.toPlainText().isEmpty
|
||||||
? Stack(
|
? Stack(
|
||||||
children: [
|
children: [
|
||||||
_buildPlaceholderText(context),
|
_buildPlaceholderText(context),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
|
import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
|
||||||
import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart';
|
import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
|
import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
|
||||||
import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart';
|
import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
import 'package:appflowy_editor/src/infra/flowy_svg.dart';
|
import 'package:appflowy_editor/src/infra/flowy_svg.dart';
|
||||||
import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
|
import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
|
import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
|
||||||
import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart';
|
import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart';
|
||||||
|
@ -44,7 +44,7 @@ class SelectionMenuItem {
|
|||||||
if (selection != null && nodes.length == 1) {
|
if (selection != null && nodes.length == 1) {
|
||||||
final node = nodes.first as TextNode;
|
final node = nodes.first as TextNode;
|
||||||
final end = selection.start.offset;
|
final end = selection.start.offset;
|
||||||
final start = node.toRawString().substring(0, end).lastIndexOf('/');
|
final start = node.toPlainText().substring(0, end).lastIndexOf('/');
|
||||||
TransactionBuilder(editorState)
|
TransactionBuilder(editorState)
|
||||||
..deleteText(
|
..deleteText(
|
||||||
node,
|
node,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
import 'package:appflowy_editor/src/extensions/attributes_extension.dart';
|
import 'package:appflowy_editor/src/extensions/attributes_extension.dart';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:appflowy_editor/src/document/attributes.dart';
|
import 'package:appflowy_editor/src/document/attributes.dart';
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/position.dart';
|
import 'package:appflowy_editor/src/document/position.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
@ -125,7 +125,7 @@ bool formatTextNodes(EditorState editorState, Attributes attributes) {
|
|||||||
..afterSelection = Selection.collapsed(
|
..afterSelection = Selection.collapsed(
|
||||||
Position(
|
Position(
|
||||||
path: textNode.path,
|
path: textNode.path,
|
||||||
offset: textNode.toRawString().length,
|
offset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -232,10 +232,10 @@ bool formatRichTextStyle(EditorState editorState, Attributes attributes) {
|
|||||||
for (var i = 0; i < textNodes.length; i++) {
|
for (var i = 0; i < textNodes.length; i++) {
|
||||||
final textNode = textNodes[i];
|
final textNode = textNodes[i];
|
||||||
var index = 0;
|
var index = 0;
|
||||||
var length = textNode.toRawString().length;
|
var length = textNode.toPlainText().length;
|
||||||
if (i == 0 && textNode == nodes.first) {
|
if (i == 0 && textNode == nodes.first) {
|
||||||
index = selection.start.offset;
|
index = selection.start.offset;
|
||||||
length = textNode.toRawString().length - selection.start.offset;
|
length = textNode.toPlainText().length - selection.start.offset;
|
||||||
} else if (i == textNodes.length - 1 && textNode == nodes.last) {
|
} else if (i == textNodes.length - 1 && textNode == nodes.last) {
|
||||||
length = selection.end.offset;
|
length = selection.end.offset;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
import 'package:appflowy_editor/src/extensions/node_extensions.dart';
|
import 'package:appflowy_editor/src/extensions/node_extensions.dart';
|
||||||
@ -282,7 +282,7 @@ class _AppFlowyInputState extends State<AppFlowyInput>
|
|||||||
// FIXME: upward and selection update.
|
// FIXME: upward and selection update.
|
||||||
if (textNodes.isNotEmpty && selection != null) {
|
if (textNodes.isNotEmpty && selection != null) {
|
||||||
final text = textNodes.fold<String>(
|
final text = textNodes.fold<String>(
|
||||||
'', (sum, textNode) => '$sum${textNode.toRawString()}\n');
|
'', (sum, textNode) => '$sum${textNode.toPlainText()}\n');
|
||||||
attach(
|
attach(
|
||||||
TextEditingValue(
|
TextEditingValue(
|
||||||
text: text,
|
text: text,
|
||||||
|
@ -163,7 +163,7 @@ KeyEventResult _backDeleteToPreviousTextNode(
|
|||||||
transactionBuilder.afterSelection = Selection.collapsed(
|
transactionBuilder.afterSelection = Selection.collapsed(
|
||||||
Position(
|
Position(
|
||||||
path: previousTextNode.path,
|
path: previousTextNode.path,
|
||||||
offset: previousTextNode.toRawString().length,
|
offset: previousTextNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -267,7 +267,7 @@ void _deleteTextNodes(TransactionBuilder transactionBuilder,
|
|||||||
List<TextNode> textNodes, Selection selection) {
|
List<TextNode> textNodes, Selection selection) {
|
||||||
final first = textNodes.first;
|
final first = textNodes.first;
|
||||||
final last = textNodes.last;
|
final last = textNodes.last;
|
||||||
var content = textNodes.last.toRawString();
|
var content = textNodes.last.toPlainText();
|
||||||
content = content.substring(selection.end.offset, content.length);
|
content = content.substring(selection.end.offset, content.length);
|
||||||
// Merge the fist and the last text node content,
|
// Merge the fist and the last text node content,
|
||||||
// and delete the all nodes expect for the first.
|
// and delete the all nodes expect for the first.
|
||||||
|
@ -136,10 +136,10 @@ void _pasteMultipleLinesInText(
|
|||||||
final tailTextNode = tailNodes.last as TextNode;
|
final tailTextNode = tailNodes.last as TextNode;
|
||||||
tailTextNode.delta = tailTextNode.delta + remain;
|
tailTextNode.delta = tailTextNode.delta + remain;
|
||||||
} else if (remain.isNotEmpty) {
|
} else if (remain.isNotEmpty) {
|
||||||
tailNodes.add(TextNode(type: "text", delta: remain));
|
tailNodes.add(TextNode(delta: remain));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tailNodes.add(TextNode(type: "text", delta: remain));
|
tailNodes.add(TextNode(delta: remain));
|
||||||
}
|
}
|
||||||
|
|
||||||
tb.setAfterSelection(afterSelection);
|
tb.setAfterSelection(afterSelection);
|
||||||
@ -261,9 +261,8 @@ void _handlePastePlainText(EditorState editorState, String plainText) {
|
|||||||
|
|
||||||
path[path.length - 1]++;
|
path[path.length - 1]++;
|
||||||
final tb = TransactionBuilder(editorState);
|
final tb = TransactionBuilder(editorState);
|
||||||
final List<TextNode> nodes = remains
|
final List<TextNode> nodes =
|
||||||
.map((e) => TextNode(type: "text", delta: _lineContentToDelta(e)))
|
remains.map((e) => TextNode(delta: _lineContentToDelta(e))).toList();
|
||||||
.toList();
|
|
||||||
|
|
||||||
final afterSelection =
|
final afterSelection =
|
||||||
_computeSelectionAfterPasteMultipleNodes(editorState, nodes);
|
_computeSelectionAfterPasteMultipleNodes(editorState, nodes);
|
||||||
@ -272,7 +271,7 @@ void _handlePastePlainText(EditorState editorState, String plainText) {
|
|||||||
if (nodes.isNotEmpty) {
|
if (nodes.isNotEmpty) {
|
||||||
final last = nodes.last;
|
final last = nodes.last;
|
||||||
nodes[nodes.length - 1] =
|
nodes[nodes.length - 1] =
|
||||||
TextNode(type: "text", delta: last.delta..addAll(insertedLineSuffix));
|
TextNode(delta: last.delta..addAll(insertedLineSuffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert first line
|
// insert first line
|
||||||
|
@ -43,7 +43,7 @@ ShortcutEventHandler enterWithoutShiftInTextNodesHandler =
|
|||||||
..deleteText(
|
..deleteText(
|
||||||
textNodes.first,
|
textNodes.first,
|
||||||
selection.start.offset,
|
selection.start.offset,
|
||||||
textNodes.first.toRawString().length,
|
textNodes.first.toPlainText().length,
|
||||||
)
|
)
|
||||||
..deleteNodes(subTextNodes)
|
..deleteNodes(subTextNodes)
|
||||||
..deleteText(
|
..deleteText(
|
||||||
@ -73,7 +73,7 @@ ShortcutEventHandler enterWithoutShiftInTextNodesHandler =
|
|||||||
// If selection is collapsed and position.start.offset == 0,
|
// If selection is collapsed and position.start.offset == 0,
|
||||||
// insert a empty text node before.
|
// insert a empty text node before.
|
||||||
if (selection.isCollapsed && selection.start.offset == 0) {
|
if (selection.isCollapsed && selection.start.offset == 0) {
|
||||||
if (textNode.toRawString().isEmpty && textNode.subtype != null) {
|
if (textNode.toPlainText().isEmpty && textNode.subtype != null) {
|
||||||
final afterSelection = Selection.collapsed(
|
final afterSelection = Selection.collapsed(
|
||||||
Position(path: textNode.path, offset: 0),
|
Position(path: textNode.path, offset: 0),
|
||||||
);
|
);
|
||||||
@ -156,7 +156,7 @@ ShortcutEventHandler enterWithoutShiftInTextNodesHandler =
|
|||||||
transactionBuilder.deleteText(
|
transactionBuilder.deleteText(
|
||||||
textNode,
|
textNode,
|
||||||
selection.start.offset,
|
selection.start.offset,
|
||||||
textNode.toRawString().length - selection.start.offset,
|
textNode.toPlainText().length - selection.start.offset,
|
||||||
);
|
);
|
||||||
if (textNode.children.isNotEmpty) {
|
if (textNode.children.isNotEmpty) {
|
||||||
final children = textNode.children.toList(growable: false);
|
final children = textNode.children.toList(growable: false);
|
||||||
|
@ -2,7 +2,7 @@ import 'package:appflowy_editor/src/service/default_text_operations/format_rich_
|
|||||||
import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart';
|
import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
|
|
||||||
ShortcutEventHandler formatBoldEventHandler = (editorState, event) {
|
ShortcutEventHandler formatBoldEventHandler = (editorState, event) {
|
||||||
final selection = editorState.service.selectionService.currentSelection.value;
|
final selection = editorState.service.selectionService.currentSelection.value;
|
||||||
|
@ -44,7 +44,7 @@ ShortcutEventHandler backquoteToCodeHandler = (editorState, event) {
|
|||||||
|
|
||||||
final textNode = textNodes.first;
|
final textNode = textNodes.first;
|
||||||
final selectionText = textNode
|
final selectionText = textNode
|
||||||
.toRawString()
|
.toPlainText()
|
||||||
.substring(selection.start.offset, selection.end.offset);
|
.substring(selection.start.offset, selection.end.offset);
|
||||||
|
|
||||||
// toggle code style when selected some text
|
// toggle code style when selected some text
|
||||||
@ -53,7 +53,7 @@ ShortcutEventHandler backquoteToCodeHandler = (editorState, event) {
|
|||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
final text = textNode.toRawString().substring(0, selection.end.offset);
|
final text = textNode.toPlainText().substring(0, selection.end.offset);
|
||||||
final backquoteIndexes = _findBackquoteIndexes(text, textNode);
|
final backquoteIndexes = _findBackquoteIndexes(text, textNode);
|
||||||
if (backquoteIndexes.isEmpty) {
|
if (backquoteIndexes.isEmpty) {
|
||||||
return KeyEventResult.ignored;
|
return KeyEventResult.ignored;
|
||||||
@ -134,7 +134,7 @@ ShortcutEventHandler doubleTildeToStrikethrough = (editorState, event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final textNode = textNodes.first;
|
final textNode = textNodes.first;
|
||||||
final text = textNode.toRawString().substring(0, selection.end.offset);
|
final text = textNode.toPlainText().substring(0, selection.end.offset);
|
||||||
|
|
||||||
// make sure the last two characters are ~~.
|
// make sure the last two characters are ~~.
|
||||||
if (text.length < 2 || text[selection.end.offset - 1] != '~') {
|
if (text.length < 2 || text[selection.end.offset - 1] != '~') {
|
||||||
@ -199,7 +199,7 @@ ShortcutEventHandler markdownLinkToLinkHandler = (editorState, event) {
|
|||||||
|
|
||||||
// find all of the indexs for important characters
|
// find all of the indexs for important characters
|
||||||
final textNode = textNodes.first;
|
final textNode = textNodes.first;
|
||||||
final text = textNode.toRawString();
|
final text = textNode.toPlainText();
|
||||||
final firstOpeningBracket = text.indexOf('[');
|
final firstOpeningBracket = text.indexOf('[');
|
||||||
final firstClosingBracket = text.indexOf(']');
|
final firstClosingBracket = text.indexOf(']');
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ ShortcutEventHandler doubleAsterisksToBold = (editorState, event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final textNode = textNodes.first;
|
final textNode = textNodes.first;
|
||||||
final text = textNode.toRawString().substring(0, selection.end.offset);
|
final text = textNode.toPlainText().substring(0, selection.end.offset);
|
||||||
|
|
||||||
// make sure the last two characters are **.
|
// make sure the last two characters are **.
|
||||||
if (text.length < 2 || text[selection.end.offset - 1] != '*') {
|
if (text.length < 2 || text[selection.end.offset - 1] != '*') {
|
||||||
@ -75,7 +75,7 @@ ShortcutEventHandler doubleUnderscoresToBold = (editorState, event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final textNode = textNodes.first;
|
final textNode = textNodes.first;
|
||||||
final text = textNode.toRawString().substring(0, selection.end.offset);
|
final text = textNode.toPlainText().substring(0, selection.end.offset);
|
||||||
|
|
||||||
// make sure the last two characters are __.
|
// make sure the last two characters are __.
|
||||||
if (text.length < 2 || text[selection.end.offset - 1] != '_') {
|
if (text.length < 2 || text[selection.end.offset - 1] != '_') {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/position.dart';
|
import 'package:appflowy_editor/src/document/position.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart';
|
import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handler.dart';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/operation/transaction_builder.dart';
|
import 'package:appflowy_editor/src/operation/transaction_builder.dart';
|
||||||
import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart';
|
import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart';
|
||||||
import 'package:appflowy_editor/src/extensions/node_extensions.dart';
|
import 'package:appflowy_editor/src/extensions/node_extensions.dart';
|
||||||
|
@ -2,7 +2,7 @@ import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event_handle
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart';
|
import 'package:appflowy_editor/src/document/built_in_attribute_keys.dart';
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/position.dart';
|
import 'package:appflowy_editor/src/document/position.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
@ -44,7 +44,7 @@ ShortcutEventHandler whiteSpaceHandler = (editorState, event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final textNode = textNodes.first;
|
final textNode = textNodes.first;
|
||||||
final text = textNode.toRawString().substring(0, selection.end.offset);
|
final text = textNode.toPlainText().substring(0, selection.end.offset);
|
||||||
|
|
||||||
final numberMatch = _numberRegex.firstMatch(text);
|
final numberMatch = _numberRegex.firstMatch(text);
|
||||||
|
|
||||||
@ -140,13 +140,13 @@ KeyEventResult _toCheckboxList(EditorState editorState, TextNode textNode) {
|
|||||||
final String symbol;
|
final String symbol;
|
||||||
bool check = false;
|
bool check = false;
|
||||||
final symbols = List<String>.from(_checkboxListSymbols)
|
final symbols = List<String>.from(_checkboxListSymbols)
|
||||||
..retainWhere(textNode.toRawString().startsWith);
|
..retainWhere(textNode.toPlainText().startsWith);
|
||||||
if (symbols.isNotEmpty) {
|
if (symbols.isNotEmpty) {
|
||||||
symbol = symbols.first;
|
symbol = symbols.first;
|
||||||
check = true;
|
check = true;
|
||||||
} else {
|
} else {
|
||||||
symbol = (List<String>.from(_unCheckboxListSymbols)
|
symbol = (List<String>.from(_unCheckboxListSymbols)
|
||||||
..retainWhere(textNode.toRawString().startsWith))
|
..retainWhere(textNode.toPlainText().startsWith))
|
||||||
.first;
|
.first;
|
||||||
check = false;
|
check = false;
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ KeyEventResult _toCheckboxList(EditorState editorState, TextNode textNode) {
|
|||||||
KeyEventResult _toHeadingStyle(
|
KeyEventResult _toHeadingStyle(
|
||||||
EditorState editorState, TextNode textNode, Selection selection) {
|
EditorState editorState, TextNode textNode, Selection selection) {
|
||||||
final x = _countOfSign(
|
final x = _countOfSign(
|
||||||
textNode.toRawString(),
|
textNode.toPlainText(),
|
||||||
selection,
|
selection,
|
||||||
);
|
);
|
||||||
final hX = 'h$x';
|
final hX = 'h$x';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/editor_state.dart';
|
import 'package:appflowy_editor/src/editor_state.dart';
|
||||||
import 'package:appflowy_editor/src/infra/log.dart';
|
import 'package:appflowy_editor/src/infra/log.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:appflowy_editor/src/infra/log.dart';
|
import 'package:appflowy_editor/src/infra/log.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:appflowy_editor/src/document/node_iterator.dart';
|
import 'package:appflowy_editor/src/document/node_iterator.dart';
|
||||||
import 'package:appflowy_editor/src/document/position.dart';
|
import 'package:appflowy_editor/src/document/position.dart';
|
||||||
import 'package:appflowy_editor/src/document/selection.dart';
|
import 'package:appflowy_editor/src/document/selection.dart';
|
||||||
|
@ -57,7 +57,6 @@ void main() async {
|
|||||||
|
|
||||||
test('test textNode copyWith', () {
|
test('test textNode copyWith', () {
|
||||||
final textNode = TextNode(
|
final textNode = TextNode(
|
||||||
type: 'example',
|
|
||||||
children: LinkedList(),
|
children: LinkedList(),
|
||||||
attributes: {
|
attributes: {
|
||||||
'example': 'example',
|
'example': 'example',
|
||||||
@ -65,7 +64,7 @@ void main() async {
|
|||||||
delta: Delta()..insert('AppFlowy'),
|
delta: Delta()..insert('AppFlowy'),
|
||||||
);
|
);
|
||||||
expect(textNode.toJson(), {
|
expect(textNode.toJson(), {
|
||||||
'type': 'example',
|
'type': 'text',
|
||||||
'attributes': {
|
'attributes': {
|
||||||
'example': 'example',
|
'example': 'example',
|
||||||
},
|
},
|
||||||
@ -79,7 +78,6 @@ void main() async {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final textNodeWithChildren = TextNode(
|
final textNodeWithChildren = TextNode(
|
||||||
type: 'example',
|
|
||||||
children: LinkedList()..add(textNode),
|
children: LinkedList()..add(textNode),
|
||||||
attributes: {
|
attributes: {
|
||||||
'example': 'example',
|
'example': 'example',
|
||||||
@ -87,7 +85,7 @@ void main() async {
|
|||||||
delta: Delta()..insert('AppFlowy'),
|
delta: Delta()..insert('AppFlowy'),
|
||||||
);
|
);
|
||||||
expect(textNodeWithChildren.toJson(), {
|
expect(textNodeWithChildren.toJson(), {
|
||||||
'type': 'example',
|
'type': 'text',
|
||||||
'attributes': {
|
'attributes': {
|
||||||
'example': 'example',
|
'example': 'example',
|
||||||
},
|
},
|
||||||
@ -96,7 +94,7 @@ void main() async {
|
|||||||
],
|
],
|
||||||
'children': [
|
'children': [
|
||||||
{
|
{
|
||||||
'type': 'example',
|
'type': 'text',
|
||||||
'attributes': {
|
'attributes': {
|
||||||
'example': 'example',
|
'example': 'example',
|
||||||
},
|
},
|
||||||
|
@ -15,7 +15,6 @@ void main() async {
|
|||||||
const text = 'Welcome to Appflowy 😁';
|
const text = 'Welcome to Appflowy 😁';
|
||||||
TextNode textNode() {
|
TextNode textNode() {
|
||||||
return TextNode(
|
return TextNode(
|
||||||
type: 'text',
|
|
||||||
delta: Delta()..insert(text),
|
delta: Delta()..insert(text),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,6 @@ class EditorWidgetTester {
|
|||||||
void insertTextNode(String? text, {Attributes? attributes, Delta? delta}) {
|
void insertTextNode(String? text, {Attributes? attributes, Delta? delta}) {
|
||||||
insert(
|
insert(
|
||||||
TextNode(
|
TextNode(
|
||||||
type: 'text',
|
|
||||||
delta: delta ?? Delta([TextInsert(text ?? 'Test')]),
|
delta: delta ?? Delta([TextInsert(text ?? 'Test')]),
|
||||||
attributes: attributes,
|
attributes: attributes,
|
||||||
),
|
),
|
||||||
@ -103,7 +102,7 @@ class EditorWidgetTester {
|
|||||||
{Selection? selection}) async {
|
{Selection? selection}) async {
|
||||||
await apply([
|
await apply([
|
||||||
TextEditingDeltaInsertion(
|
TextEditingDeltaInsertion(
|
||||||
oldText: textNode.toRawString(),
|
oldText: textNode.toPlainText(),
|
||||||
textInserted: text,
|
textInserted: text,
|
||||||
insertionOffset: offset,
|
insertionOffset: offset,
|
||||||
selection: selection != null
|
selection: selection != null
|
||||||
|
@ -318,12 +318,12 @@ void main() {
|
|||||||
});
|
});
|
||||||
group("attributes", () {
|
group("attributes", () {
|
||||||
test("compose", () {
|
test("compose", () {
|
||||||
final attrs = composeAttributes({"a": null}, {"b": null}, true);
|
final attrs = composeAttributes({'a': null}, {'b': null}, keepNull: true);
|
||||||
expect(attrs != null, true);
|
expect(attrs != null, true);
|
||||||
expect(attrs!.containsKey("a"), true);
|
expect(attrs?.containsKey("a"), true);
|
||||||
expect(attrs.containsKey("b"), true);
|
expect(attrs?.containsKey("b"), true);
|
||||||
expect(attrs["a"], null);
|
expect(attrs?["a"], null);
|
||||||
expect(attrs["b"], null);
|
expect(attrs?["b"], null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:appflowy_editor/src/document/node.dart';
|
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:appflowy_editor/src/operation/operation.dart';
|
import 'package:appflowy_editor/src/operation/operation.dart';
|
||||||
import 'package:appflowy_editor/src/operation/transaction_builder.dart';
|
import 'package:appflowy_editor/src/operation/transaction_builder.dart';
|
||||||
|
@ -21,12 +21,12 @@ void main() async {
|
|||||||
final editorState = EditorState(document: document);
|
final editorState = EditorState(document: document);
|
||||||
|
|
||||||
final historyItem = HistoryItem();
|
final historyItem = HistoryItem();
|
||||||
historyItem.add(DeleteOperation(
|
historyItem
|
||||||
[0], [TextNode(type: 'text', delta: Delta()..insert('0'))]));
|
.add(DeleteOperation([0], [TextNode(delta: Delta()..insert('0'))]));
|
||||||
historyItem.add(DeleteOperation(
|
historyItem
|
||||||
[0], [TextNode(type: 'text', delta: Delta()..insert('1'))]));
|
.add(DeleteOperation([0], [TextNode(delta: Delta()..insert('1'))]));
|
||||||
historyItem.add(DeleteOperation(
|
historyItem
|
||||||
[0], [TextNode(type: 'text', delta: Delta()..insert('2'))]));
|
.add(DeleteOperation([0], [TextNode(delta: Delta()..insert('2'))]));
|
||||||
|
|
||||||
final transaction = historyItem.toTransaction(editorState);
|
final transaction = historyItem.toTransaction(editorState);
|
||||||
assert(isInsertAndPathEqual(transaction.operations[0], [0], '2'));
|
assert(isInsertAndPathEqual(transaction.operations[0], [0], '2'));
|
||||||
@ -39,8 +39,8 @@ void main() async {
|
|||||||
final editorState = EditorState(document: document);
|
final editorState = EditorState(document: document);
|
||||||
|
|
||||||
final historyItem = HistoryItem();
|
final historyItem = HistoryItem();
|
||||||
historyItem.add(DeleteOperation(
|
historyItem
|
||||||
[0], [TextNode(type: 'text', delta: Delta()..insert('0'))]));
|
.add(DeleteOperation([0], [TextNode(delta: Delta()..insert('0'))]));
|
||||||
historyItem
|
historyItem
|
||||||
.add(UpdateOperation([0], {"subType": "number"}, {"subType": null}));
|
.add(UpdateOperation([0], {"subType": "number"}, {"subType": null}));
|
||||||
historyItem.add(DeleteOperation([0], [TextNode.empty(), TextNode.empty()]));
|
historyItem.add(DeleteOperation([0], [TextNode.empty(), TextNode.empty()]));
|
||||||
@ -72,5 +72,5 @@ bool isInsertAndPathEqual(Operation operation, Path path, [String? content]) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return firstNode.delta.toRawString() == content;
|
return firstNode.delta.toPlainText() == content;
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ Future<void> _testDefaultSelectionMenuItems(
|
|||||||
int index, EditorWidgetTester editor) async {
|
int index, EditorWidgetTester editor) async {
|
||||||
expect(editor.documentLength, 4);
|
expect(editor.documentLength, 4);
|
||||||
expect(editor.documentSelection, Selection.single(path: [2], startOffset: 0));
|
expect(editor.documentSelection, Selection.single(path: [2], startOffset: 0));
|
||||||
expect((editor.nodeAtPath([1]) as TextNode).toRawString(),
|
expect((editor.nodeAtPath([1]) as TextNode).toPlainText(),
|
||||||
'Welcome to Appflowy 😁');
|
'Welcome to Appflowy 😁');
|
||||||
final node = editor.nodeAtPath([2]);
|
final node = editor.nodeAtPath([2]);
|
||||||
final item = defaultSelectionMenuItems[index];
|
final item = defaultSelectionMenuItems[index];
|
||||||
|
@ -117,7 +117,7 @@ void main() async {
|
|||||||
expect(editor.documentLength, 1);
|
expect(editor.documentLength, 1);
|
||||||
expect(editor.documentSelection,
|
expect(editor.documentSelection,
|
||||||
Selection.single(path: [0], startOffset: text.length));
|
Selection.single(path: [0], startOffset: text.length));
|
||||||
expect((editor.nodeAtPath([0]) as TextNode).toRawString(), text * 2);
|
expect((editor.nodeAtPath([0]) as TextNode).toPlainText(), text * 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Before
|
// Before
|
||||||
@ -275,7 +275,6 @@ void main() async {
|
|||||||
// * Welcome to Appflowy 😁
|
// * Welcome to Appflowy 😁
|
||||||
const text = 'Welcome to Appflowy 😁';
|
const text = 'Welcome to Appflowy 😁';
|
||||||
final node = TextNode(
|
final node = TextNode(
|
||||||
type: 'text',
|
|
||||||
delta: Delta()..insert(text),
|
delta: Delta()..insert(text),
|
||||||
attributes: {
|
attributes: {
|
||||||
BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList,
|
BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList,
|
||||||
@ -320,7 +319,7 @@ void main() async {
|
|||||||
editor.documentSelection,
|
editor.documentSelection,
|
||||||
Selection.single(path: [0, 0], startOffset: text.length),
|
Selection.single(path: [0, 0], startOffset: text.length),
|
||||||
);
|
);
|
||||||
expect((editor.nodeAtPath([0, 0]) as TextNode).toRawString(), text * 2);
|
expect((editor.nodeAtPath([0, 0]) as TextNode).toPlainText(), text * 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('Delete the complicated nested bulleted list', (tester) async {
|
testWidgets('Delete the complicated nested bulleted list', (tester) async {
|
||||||
@ -331,7 +330,6 @@ void main() async {
|
|||||||
// * Welcome to Appflowy 😁
|
// * Welcome to Appflowy 😁
|
||||||
const text = 'Welcome to Appflowy 😁';
|
const text = 'Welcome to Appflowy 😁';
|
||||||
final node = TextNode(
|
final node = TextNode(
|
||||||
type: 'text',
|
|
||||||
delta: Delta()..insert(text),
|
delta: Delta()..insert(text),
|
||||||
attributes: {
|
attributes: {
|
||||||
BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList,
|
BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList,
|
||||||
@ -390,7 +388,7 @@ void main() async {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
(editor.nodeAtPath([0, 0]) as TextNode).toRawString() == text * 2,
|
(editor.nodeAtPath([0, 0]) as TextNode).toPlainText() == text * 2,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
@ -496,7 +494,7 @@ Future<void> _deleteStyledTextByBackspace(
|
|||||||
expect(editor.documentSelection,
|
expect(editor.documentSelection,
|
||||||
Selection.single(path: [1], startOffset: text.length));
|
Selection.single(path: [1], startOffset: text.length));
|
||||||
expect(editor.nodeAtPath([1])?.subtype, style);
|
expect(editor.nodeAtPath([1])?.subtype, style);
|
||||||
expect((editor.nodeAtPath([1]) as TextNode).toRawString(), text * 2);
|
expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), text * 2);
|
||||||
|
|
||||||
await editor.updateSelection(
|
await editor.updateSelection(
|
||||||
Selection.single(path: [1], startOffset: 0),
|
Selection.single(path: [1], startOffset: 0),
|
||||||
@ -538,7 +536,7 @@ Future<void> _deleteStyledTextByDelete(
|
|||||||
expect(
|
expect(
|
||||||
editor.documentSelection, Selection.single(path: [1], startOffset: 0));
|
editor.documentSelection, Selection.single(path: [1], startOffset: 0));
|
||||||
expect(editor.nodeAtPath([1])?.subtype, style);
|
expect(editor.nodeAtPath([1])?.subtype, style);
|
||||||
expect((editor.nodeAtPath([1]) as TextNode).toRawString(),
|
expect((editor.nodeAtPath([1]) as TextNode).toPlainText(),
|
||||||
text.safeSubString(i));
|
text.safeSubString(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -548,7 +546,7 @@ Future<void> _deleteStyledTextByDelete(
|
|||||||
expect(editor.documentLength, 2);
|
expect(editor.documentLength, 2);
|
||||||
expect(editor.documentSelection, Selection.single(path: [1], startOffset: 0));
|
expect(editor.documentSelection, Selection.single(path: [1], startOffset: 0));
|
||||||
expect(editor.nodeAtPath([1])?.subtype, style);
|
expect(editor.nodeAtPath([1])?.subtype, style);
|
||||||
expect((editor.nodeAtPath([1]) as TextNode).toRawString(), text);
|
expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), text);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _deleteTextByBackspace(
|
Future<void> _deleteTextByBackspace(
|
||||||
@ -568,7 +566,7 @@ Future<void> _deleteTextByBackspace(
|
|||||||
|
|
||||||
expect(editor.documentLength, 3);
|
expect(editor.documentLength, 3);
|
||||||
expect(editor.documentSelection, Selection.single(path: [1], startOffset: 9));
|
expect(editor.documentSelection, Selection.single(path: [1], startOffset: 9));
|
||||||
expect((editor.nodeAtPath([1]) as TextNode).toRawString(),
|
expect((editor.nodeAtPath([1]) as TextNode).toPlainText(),
|
||||||
'Welcome t Appflowy 😁');
|
'Welcome t Appflowy 😁');
|
||||||
|
|
||||||
// delete 'to '
|
// delete 'to '
|
||||||
@ -578,7 +576,7 @@ Future<void> _deleteTextByBackspace(
|
|||||||
await editor.pressLogicKey(LogicalKeyboardKey.backspace);
|
await editor.pressLogicKey(LogicalKeyboardKey.backspace);
|
||||||
expect(editor.documentLength, 3);
|
expect(editor.documentLength, 3);
|
||||||
expect(editor.documentSelection, Selection.single(path: [2], startOffset: 8));
|
expect(editor.documentSelection, Selection.single(path: [2], startOffset: 8));
|
||||||
expect((editor.nodeAtPath([2]) as TextNode).toRawString(),
|
expect((editor.nodeAtPath([2]) as TextNode).toPlainText(),
|
||||||
'Welcome Appflowy 😁');
|
'Welcome Appflowy 😁');
|
||||||
|
|
||||||
// delete 'Appflowy 😁
|
// delete 'Appflowy 😁
|
||||||
@ -593,7 +591,7 @@ Future<void> _deleteTextByBackspace(
|
|||||||
expect(editor.documentLength, 1);
|
expect(editor.documentLength, 1);
|
||||||
expect(
|
expect(
|
||||||
editor.documentSelection, Selection.single(path: [0], startOffset: 11));
|
editor.documentSelection, Selection.single(path: [0], startOffset: 11));
|
||||||
expect((editor.nodeAtPath([0]) as TextNode).toRawString(),
|
expect((editor.nodeAtPath([0]) as TextNode).toPlainText(),
|
||||||
'Welcome to Appflowy 😁');
|
'Welcome to Appflowy 😁');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,7 +612,7 @@ Future<void> _deleteTextByDelete(
|
|||||||
|
|
||||||
expect(editor.documentLength, 3);
|
expect(editor.documentLength, 3);
|
||||||
expect(editor.documentSelection, Selection.single(path: [1], startOffset: 9));
|
expect(editor.documentSelection, Selection.single(path: [1], startOffset: 9));
|
||||||
expect((editor.nodeAtPath([1]) as TextNode).toRawString(),
|
expect((editor.nodeAtPath([1]) as TextNode).toPlainText(),
|
||||||
'Welcome t Appflowy 😁');
|
'Welcome t Appflowy 😁');
|
||||||
|
|
||||||
// delete 'to '
|
// delete 'to '
|
||||||
@ -624,7 +622,7 @@ Future<void> _deleteTextByDelete(
|
|||||||
await editor.pressLogicKey(LogicalKeyboardKey.delete);
|
await editor.pressLogicKey(LogicalKeyboardKey.delete);
|
||||||
expect(editor.documentLength, 3);
|
expect(editor.documentLength, 3);
|
||||||
expect(editor.documentSelection, Selection.single(path: [2], startOffset: 8));
|
expect(editor.documentSelection, Selection.single(path: [2], startOffset: 8));
|
||||||
expect((editor.nodeAtPath([2]) as TextNode).toRawString(),
|
expect((editor.nodeAtPath([2]) as TextNode).toPlainText(),
|
||||||
'Welcome Appflowy 😁');
|
'Welcome Appflowy 😁');
|
||||||
|
|
||||||
// delete 'Appflowy 😁
|
// delete 'Appflowy 😁
|
||||||
@ -639,6 +637,6 @@ Future<void> _deleteTextByDelete(
|
|||||||
expect(editor.documentLength, 1);
|
expect(editor.documentLength, 1);
|
||||||
expect(
|
expect(
|
||||||
editor.documentSelection, Selection.single(path: [0], startOffset: 11));
|
editor.documentSelection, Selection.single(path: [0], startOffset: 11));
|
||||||
expect((editor.nodeAtPath([0]) as TextNode).toRawString(),
|
expect((editor.nodeAtPath([0]) as TextNode).toPlainText(),
|
||||||
'Welcome to Appflowy 😁');
|
'Welcome to Appflowy 😁');
|
||||||
}
|
}
|
||||||
|
@ -74,10 +74,10 @@ void main() async {
|
|||||||
expect(lastNode != null, true);
|
expect(lastNode != null, true);
|
||||||
expect(lastNode is TextNode, true);
|
expect(lastNode is TextNode, true);
|
||||||
lastNode = lastNode as TextNode;
|
lastNode = lastNode as TextNode;
|
||||||
expect(lastNode.delta.toRawString(), text);
|
expect(lastNode.delta.toPlainText(), text);
|
||||||
expect((lastNode.previous as TextNode).delta.toRawString(), '');
|
expect((lastNode.previous as TextNode).delta.toPlainText(), '');
|
||||||
expect(
|
expect(
|
||||||
(lastNode.previous!.previous as TextNode).delta.toRawString(), text);
|
(lastNode.previous!.previous as TextNode).delta.toPlainText(), text);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Before
|
// Before
|
||||||
@ -134,7 +134,7 @@ void main() async {
|
|||||||
);
|
);
|
||||||
await editor.pressLogicKey(LogicalKeyboardKey.enter);
|
await editor.pressLogicKey(LogicalKeyboardKey.enter);
|
||||||
expect(editor.documentLength, 2);
|
expect(editor.documentLength, 2);
|
||||||
expect((editor.nodeAtPath([1]) as TextNode).toRawString(), text);
|
expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), text);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -227,6 +227,6 @@ Future<void> _testMultipleSelection(
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(editor.documentLength, 2);
|
expect(editor.documentLength, 2);
|
||||||
expect((editor.nodeAtPath([0]) as TextNode).toRawString(), 'Welcome');
|
expect((editor.nodeAtPath([0]) as TextNode).toPlainText(), 'Welcome');
|
||||||
expect((editor.nodeAtPath([1]) as TextNode).toRawString(), 'to Appflowy 😁');
|
expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), 'to Appflowy 😁');
|
||||||
}
|
}
|
||||||
|
@ -39,11 +39,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allBold, true);
|
expect(allBold, true);
|
||||||
expect(textNode.toRawString(), 'AppFlowy');
|
expect(textNode.toPlainText(), 'AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('App**Flowy** to bold AppFlowy', (tester) async {
|
testWidgets('App**Flowy** to bold AppFlowy', (tester) async {
|
||||||
@ -62,11 +62,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 3,
|
startOffset: 3,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allBold, true);
|
expect(allBold, true);
|
||||||
expect(textNode.toRawString(), 'AppFlowy');
|
expect(textNode.toPlainText(), 'AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('***AppFlowy** to bold *AppFlowy', (tester) async {
|
testWidgets('***AppFlowy** to bold *AppFlowy', (tester) async {
|
||||||
@ -85,11 +85,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 1,
|
startOffset: 1,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allBold, true);
|
expect(allBold, true);
|
||||||
expect(textNode.toRawString(), '*AppFlowy');
|
expect(textNode.toPlainText(), '*AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('**AppFlowy** application to bold AppFlowy only',
|
testWidgets('**AppFlowy** application to bold AppFlowy only',
|
||||||
@ -115,7 +115,7 @@ void main() async {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(appFlowyBold, true);
|
expect(appFlowyBold, true);
|
||||||
expect(textNode.toRawString(), 'AppFlowy');
|
expect(textNode.toPlainText(), 'AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('**** nothing changes', (tester) async {
|
testWidgets('**** nothing changes', (tester) async {
|
||||||
@ -134,11 +134,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allBold, false);
|
expect(allBold, false);
|
||||||
expect(textNode.toRawString(), text);
|
expect(textNode.toPlainText(), text);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -171,11 +171,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allBold, true);
|
expect(allBold, true);
|
||||||
expect(textNode.toRawString(), 'AppFlowy');
|
expect(textNode.toPlainText(), 'AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('App__Flowy__ to bold AppFlowy', (tester) async {
|
testWidgets('App__Flowy__ to bold AppFlowy', (tester) async {
|
||||||
@ -194,11 +194,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 3,
|
startOffset: 3,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allBold, true);
|
expect(allBold, true);
|
||||||
expect(textNode.toRawString(), 'AppFlowy');
|
expect(textNode.toPlainText(), 'AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('___AppFlowy__ to bold _AppFlowy', (tester) async {
|
testWidgets('___AppFlowy__ to bold _AppFlowy', (tester) async {
|
||||||
@ -217,11 +217,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 1,
|
startOffset: 1,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allBold, true);
|
expect(allBold, true);
|
||||||
expect(textNode.toRawString(), '_AppFlowy');
|
expect(textNode.toPlainText(), '_AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('__AppFlowy__ application to bold AppFlowy only',
|
testWidgets('__AppFlowy__ application to bold AppFlowy only',
|
||||||
@ -247,7 +247,7 @@ void main() async {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(appFlowyBold, true);
|
expect(appFlowyBold, true);
|
||||||
expect(textNode.toRawString(), 'AppFlowy');
|
expect(textNode.toPlainText(), 'AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('____ nothing changes', (tester) async {
|
testWidgets('____ nothing changes', (tester) async {
|
||||||
@ -266,11 +266,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allBold, false);
|
expect(allBold, false);
|
||||||
expect(textNode.toRawString(), text);
|
expect(textNode.toPlainText(), text);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -38,11 +38,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allCode, true);
|
expect(allCode, true);
|
||||||
expect(textNode.toRawString(), 'AppFlowy');
|
expect(textNode.toPlainText(), 'AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('App`Flowy` to code AppFlowy', (tester) async {
|
testWidgets('App`Flowy` to code AppFlowy', (tester) async {
|
||||||
@ -61,11 +61,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 3,
|
startOffset: 3,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allCode, true);
|
expect(allCode, true);
|
||||||
expect(textNode.toRawString(), 'AppFlowy');
|
expect(textNode.toPlainText(), 'AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('`` nothing changes', (tester) async {
|
testWidgets('`` nothing changes', (tester) async {
|
||||||
@ -84,11 +84,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allCode, false);
|
expect(allCode, false);
|
||||||
expect(textNode.toRawString(), text);
|
expect(textNode.toPlainText(), text);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -120,11 +120,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 1,
|
startOffset: 1,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allCode, true);
|
expect(allCode, true);
|
||||||
expect(textNode.toRawString(), '`AppFlowy');
|
expect(textNode.toPlainText(), '`AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('```` nothing changes', (tester) async {
|
testWidgets('```` nothing changes', (tester) async {
|
||||||
@ -143,11 +143,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allCode, false);
|
expect(allCode, false);
|
||||||
expect(textNode.toRawString(), text);
|
expect(textNode.toPlainText(), text);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -180,11 +180,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allStrikethrough, true);
|
expect(allStrikethrough, true);
|
||||||
expect(textNode.toRawString(), 'AppFlowy');
|
expect(textNode.toPlainText(), 'AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('App~~Flowy~~ to strikethrough AppFlowy', (tester) async {
|
testWidgets('App~~Flowy~~ to strikethrough AppFlowy', (tester) async {
|
||||||
@ -203,11 +203,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 3,
|
startOffset: 3,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allStrikethrough, true);
|
expect(allStrikethrough, true);
|
||||||
expect(textNode.toRawString(), 'AppFlowy');
|
expect(textNode.toPlainText(), 'AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('~~~AppFlowy~~ to bold ~AppFlowy', (tester) async {
|
testWidgets('~~~AppFlowy~~ to bold ~AppFlowy', (tester) async {
|
||||||
@ -226,11 +226,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 1,
|
startOffset: 1,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allStrikethrough, true);
|
expect(allStrikethrough, true);
|
||||||
expect(textNode.toRawString(), '~AppFlowy');
|
expect(textNode.toPlainText(), '~AppFlowy');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('~~~~ nothing changes', (tester) async {
|
testWidgets('~~~~ nothing changes', (tester) async {
|
||||||
@ -249,11 +249,11 @@ void main() async {
|
|||||||
Selection.single(
|
Selection.single(
|
||||||
path: [0],
|
path: [0],
|
||||||
startOffset: 0,
|
startOffset: 0,
|
||||||
endOffset: textNode.toRawString().length,
|
endOffset: textNode.toPlainText().length,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(allStrikethrough, false);
|
expect(allStrikethrough, false);
|
||||||
expect(textNode.toRawString(), text);
|
expect(textNode.toPlainText(), text);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -56,7 +56,7 @@ Future<void> _testBackspaceUndoRedo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
expect(editor.documentLength, 3);
|
expect(editor.documentLength, 3);
|
||||||
expect((editor.nodeAtPath([1]) as TextNode).toRawString(), text);
|
expect((editor.nodeAtPath([1]) as TextNode).toPlainText(), text);
|
||||||
expect(editor.documentSelection, selection);
|
expect(editor.documentSelection, selection);
|
||||||
|
|
||||||
if (Platform.isWindows || Platform.isLinux) {
|
if (Platform.isWindows || Platform.isLinux) {
|
||||||
|
@ -26,7 +26,7 @@ void main() async {
|
|||||||
);
|
);
|
||||||
await editor.pressLogicKey(LogicalKeyboardKey.space);
|
await editor.pressLogicKey(LogicalKeyboardKey.space);
|
||||||
expect(
|
expect(
|
||||||
(editor.nodeAtPath([i]) as TextNode).toRawString(),
|
(editor.nodeAtPath([i]) as TextNode).toPlainText(),
|
||||||
'W elcome to Appflowy 😁',
|
'W elcome to Appflowy 😁',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ void main() async {
|
|||||||
);
|
);
|
||||||
await editor.pressLogicKey(LogicalKeyboardKey.space);
|
await editor.pressLogicKey(LogicalKeyboardKey.space);
|
||||||
expect(
|
expect(
|
||||||
(editor.nodeAtPath([i]) as TextNode).toRawString(),
|
(editor.nodeAtPath([i]) as TextNode).toPlainText(),
|
||||||
'W elcome to Appflowy 😁 ',
|
'W elcome to Appflowy 😁 ',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ void main() async {
|
|||||||
expect(textNode.subtype, BuiltInAttributeKey.heading);
|
expect(textNode.subtype, BuiltInAttributeKey.heading);
|
||||||
// BuiltInAttributeKey.h1 ~ BuiltInAttributeKey.h6
|
// BuiltInAttributeKey.h1 ~ BuiltInAttributeKey.h6
|
||||||
expect(textNode.attributes.heading, 'h$i');
|
expect(textNode.attributes.heading, 'h$i');
|
||||||
expect(textNode.toRawString().startsWith('##'), true);
|
expect(textNode.toPlainText().startsWith('##'), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ void main() async {
|
|||||||
await editor.pressLogicKey(LogicalKeyboardKey.space);
|
await editor.pressLogicKey(LogicalKeyboardKey.space);
|
||||||
expect(textNode.subtype, BuiltInAttributeKey.checkbox);
|
expect(textNode.subtype, BuiltInAttributeKey.checkbox);
|
||||||
expect(textNode.attributes.check, true);
|
expect(textNode.attributes.check, true);
|
||||||
expect(textNode.toRawString(), insertedText);
|
expect(textNode.toPlainText(), insertedText);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user