mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat(doc): transaction and deltas
This commit is contained in:
parent
a71d9401d5
commit
f2c624778e
@ -256,7 +256,12 @@ TextOperation? _textOperationFromJson(Map<String, dynamic> json) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// basically copy from: https://github.com/quilljs/delta
|
||||
/// Deltas are a simple, yet expressive format that can be used to describe contents and changes.
|
||||
/// The format is JSON based, and is human readable, yet easily parsible by machines.
|
||||
/// Deltas can describe any rich text document, includes all text and formatting information, without the ambiguity and complexity of HTML.
|
||||
///
|
||||
|
||||
/// Basically borrowed from: https://github.com/quilljs/delta
|
||||
class Delta extends Iterable<TextOperation> {
|
||||
final List<TextOperation> _operations;
|
||||
String? _rawString;
|
||||
@ -316,6 +321,9 @@ class Delta extends Iterable<TextOperation> {
|
||||
_operations.add(textOp);
|
||||
}
|
||||
|
||||
/// The slice() method does not change the original string.
|
||||
/// The start and end parameters specifies the part of the string to extract.
|
||||
/// The end position is optional.
|
||||
Delta slice(int start, [int? end]) {
|
||||
final result = Delta();
|
||||
final iterator = _OpIterator(_operations);
|
||||
@ -336,19 +344,29 @@ class Delta extends Iterable<TextOperation> {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Insert operations have an `insert` key defined.
|
||||
/// A String value represents inserting text.
|
||||
void insert(String content, [Attributes? attributes]) =>
|
||||
add(TextInsert(content, attributes));
|
||||
|
||||
/// Retain operations have a Number `retain` key defined representing the number of characters to keep (other libraries might use the name keep or skip).
|
||||
/// An optional `attributes` key can be defined with an Object to describe formatting changes to the character range.
|
||||
/// A value of `null` in the `attributes` Object represents removal of that key.
|
||||
///
|
||||
/// *Note: It is not necessary to retain the last characters of a document as this is implied.*
|
||||
void retain(int length, [Attributes? attributes]) =>
|
||||
add(TextRetain(length, attributes));
|
||||
|
||||
/// Delete operations have a Number `delete` key defined representing the number of characters to delete.
|
||||
void delete(int length) => add(TextDelete(length));
|
||||
|
||||
/// The length of the string fo the [Delta].
|
||||
int get length {
|
||||
return _operations.fold(
|
||||
0, (previousValue, element) => previousValue + element.length);
|
||||
}
|
||||
|
||||
/// Returns a Delta that is equivalent to applying the operations of own Delta, followed by another Delta.
|
||||
Delta compose(Delta other) {
|
||||
final thisIter = _OpIterator(_operations);
|
||||
final otherIter = _OpIterator(other._operations);
|
||||
@ -412,6 +430,7 @@ class Delta extends Iterable<TextOperation> {
|
||||
return delta..chop();
|
||||
}
|
||||
|
||||
/// This method joins two Delta together.
|
||||
Delta operator +(Delta other) {
|
||||
var ops = [..._operations];
|
||||
if (other._operations.isNotEmpty) {
|
||||
@ -445,6 +464,7 @@ class Delta extends Iterable<TextOperation> {
|
||||
return hashList(_operations);
|
||||
}
|
||||
|
||||
/// Returned an inverted delta that has the opposite effect of against a base document delta.
|
||||
Delta invert(Delta base) {
|
||||
final inverted = Delta();
|
||||
_operations.fold(0, (int previousValue, op) {
|
||||
@ -475,6 +495,13 @@ class Delta extends Iterable<TextOperation> {
|
||||
return _operations.map((e) => e.toJson()).toList();
|
||||
}
|
||||
|
||||
/// This method will return the position of the previous rune.
|
||||
///
|
||||
/// Since the encoding of the [String] in Dart is UTF-16.
|
||||
/// If you want to find the previous character of a position,
|
||||
/// you can' just use the `position - 1` simply.
|
||||
///
|
||||
/// This method can help you to compute the position of the previous character.
|
||||
int prevRunePosition(int pos) {
|
||||
if (pos == 0) {
|
||||
return pos - 1;
|
||||
@ -485,6 +512,13 @@ class Delta extends Iterable<TextOperation> {
|
||||
return _runeIndexes![pos - 1];
|
||||
}
|
||||
|
||||
/// This method will return the position of the next rune.
|
||||
///
|
||||
/// Since the encoding of the [String] in Dart is UTF-16.
|
||||
/// If you want to find the previous character of a position,
|
||||
/// you can' just use the `position + 1` simply.
|
||||
///
|
||||
/// This method can help you to compute the position of the next character.
|
||||
int nextRunePosition(int pos) {
|
||||
final stringContent = toRawString();
|
||||
if (pos >= stringContent.length - 1) {
|
||||
|
@ -14,7 +14,6 @@ import 'package:flowy_editor/src/operation/transaction.dart';
|
||||
/// A [TransactionBuilder] is used to build the transaction from the state.
|
||||
/// It will save make a snapshot of the cursor selection state automatically.
|
||||
/// The cursor can be resorted if the transaction is undo.
|
||||
|
||||
class TransactionBuilder {
|
||||
final List<Operation> operations = [];
|
||||
EditorState state;
|
||||
@ -29,15 +28,18 @@ class TransactionBuilder {
|
||||
state.apply(transaction);
|
||||
}
|
||||
|
||||
/// Insert the nodes at the position of path.
|
||||
insertNode(Path path, Node node) {
|
||||
insertNodes(path, [node]);
|
||||
}
|
||||
|
||||
/// Insert a sequence of nodes at the position of path.
|
||||
insertNodes(Path path, List<Node> nodes) {
|
||||
beforeSelection = state.cursorSelection;
|
||||
add(InsertOperation(path, nodes));
|
||||
}
|
||||
|
||||
/// Update the attributes of nodes.
|
||||
updateNode(Node node, Attributes attributes) {
|
||||
beforeSelection = state.cursorSelection;
|
||||
|
||||
@ -49,6 +51,7 @@ class TransactionBuilder {
|
||||
));
|
||||
}
|
||||
|
||||
/// Delete a node in the document.
|
||||
deleteNode(Node node) {
|
||||
deleteNodesAtPath(node.path);
|
||||
}
|
||||
@ -57,6 +60,9 @@ class TransactionBuilder {
|
||||
nodes.forEach(deleteNode);
|
||||
}
|
||||
|
||||
/// Delete a sequence of nodes at the path of the document.
|
||||
/// The length specific the length of the following nodes to delete(
|
||||
/// including the start one).
|
||||
deleteNodesAtPath(Path path, [int length = 1]) {
|
||||
if (path.isEmpty) {
|
||||
return;
|
||||
@ -106,6 +112,9 @@ class TransactionBuilder {
|
||||
);
|
||||
}
|
||||
|
||||
/// Insert content at a specified index.
|
||||
/// Optionally, you may specify formatting attributes that are applied to the inserted string.
|
||||
/// By default, the formatting attributes before the insert position will be used.
|
||||
insertText(TextNode node, int index, String content,
|
||||
[Attributes? attributes]) {
|
||||
var newAttributes = attributes;
|
||||
@ -126,6 +135,7 @@ class TransactionBuilder {
|
||||
Position(path: node.path, offset: index + content.length));
|
||||
}
|
||||
|
||||
/// Assign formatting attributes to a range of text.
|
||||
formatText(TextNode node, int index, int length, Attributes attributes) {
|
||||
textEdit(
|
||||
node,
|
||||
@ -135,6 +145,7 @@ class TransactionBuilder {
|
||||
afterSelection = beforeSelection;
|
||||
}
|
||||
|
||||
/// Delete length characters starting from index.
|
||||
deleteText(TextNode node, int index, int length) {
|
||||
textEdit(
|
||||
node,
|
||||
@ -169,6 +180,11 @@ class TransactionBuilder {
|
||||
);
|
||||
}
|
||||
|
||||
/// Add an operation to the transaction.
|
||||
/// This method will merge operations if they are both TextEdits.
|
||||
///
|
||||
/// Also, this method will transform the path of the operations
|
||||
/// to avoid conflicts.
|
||||
add(Operation op) {
|
||||
final Operation? last = operations.isEmpty ? null : operations.last;
|
||||
if (last != null) {
|
||||
@ -190,6 +206,7 @@ class TransactionBuilder {
|
||||
operations.add(op);
|
||||
}
|
||||
|
||||
/// Generate a immutable [Transaction] to apply or transmit.
|
||||
Transaction finish() {
|
||||
return Transaction(
|
||||
operations: UnmodifiableListView(operations),
|
||||
|
Loading…
Reference in New Issue
Block a user