Forráskód Böngészése

Merge branch 'main' of github.com:AppFlowy-IO/AppFlowy

Sean Riley Hawkins 3 éve
szülő
commit
5f039c2d1a
86 módosított fájl, 1133 hozzáadás és 626 törlés
  1. 28 3
      .githooks/commit-msg
  2. 18 1
      .githooks/pre-commit
  3. 24 6
      .githooks/pre-push
  4. 3 0
      .gitignore
  5. 2 1
      README.md
  6. 56 0
      ROADMAP.md
  7. 1 0
      commitlint.config.js
  8. 0 1
      frontend/Makefile.toml
  9. 1 0
      frontend/app_flowy/lib/startup/tasks/app_widget.dart
  10. 2 2
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_persistence.dart
  11. 1 1
      frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart
  12. 4 4
      frontend/app_flowy/lib/workspace/application/grid/cell/select_option_service.dart
  13. 5 5
      frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart
  14. 2 5
      frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart
  15. 4 4
      frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart
  16. 5 1
      frontend/app_flowy/lib/workspace/presentation/settings/widgets/settings_appearance_view.dart
  17. 82 19
      frontend/app_flowy/lib/workspace/presentation/settings/widgets/settings_language_view.dart
  18. 52 0
      frontend/app_flowy/lib/workspace/presentation/widgets/toggle/toggle.dart
  19. 38 0
      frontend/app_flowy/lib/workspace/presentation/widgets/toggle/toggle_style.dart
  20. 2 0
      frontend/app_flowy/packages/flowy_infra/lib/language.dart
  21. 3 3
      frontend/app_flowy/pubspec.lock
  22. 1 1
      frontend/rust-lib/flowy-folder/src/manager.rs
  23. 4 4
      frontend/rust-lib/flowy-folder/src/services/folder_editor.rs
  24. 1 1
      frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs
  25. 4 4
      frontend/rust-lib/flowy-folder/src/services/web_socket.rs
  26. 16 17
      frontend/rust-lib/flowy-grid/src/entities/cell_entities.rs
  27. 97 9
      frontend/rust-lib/flowy-grid/src/entities/field_entities.rs
  28. 7 12
      frontend/rust-lib/flowy-grid/src/entities/row_entities.rs
  29. 17 17
      frontend/rust-lib/flowy-grid/src/event_handler.rs
  30. 9 9
      frontend/rust-lib/flowy-grid/src/event_map.rs
  31. 2 2
      frontend/rust-lib/flowy-grid/src/manager.rs
  32. 4 4
      frontend/rust-lib/flowy-grid/src/services/block_revision_editor.rs
  33. 1 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs
  34. 1 1
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_tests.rs
  35. 4 4
      frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs
  36. 6 6
      frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs
  37. 7 7
      frontend/rust-lib/flowy-grid/src/services/grid_editor.rs
  38. 1 1
      frontend/rust-lib/flowy-grid/src/services/persistence/migration.rs
  39. 3 3
      frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs
  40. 4 4
      frontend/rust-lib/flowy-revision/src/conflict_resolve.rs
  41. 1 1
      frontend/rust-lib/flowy-text-block/src/editor.rs
  42. 2 2
      frontend/rust-lib/flowy-text-block/src/queue.rs
  43. 1 1
      frontend/rust-lib/flowy-text-block/tests/document/script.rs
  44. 6 6
      frontend/rust-lib/flowy-text-block/tests/editor/attribute_test.rs
  45. 13 13
      frontend/rust-lib/flowy-text-block/tests/editor/mod.rs
  46. 137 98
      frontend/rust-lib/flowy-text-block/tests/editor/op_test.rs
  47. 7 7
      frontend/rust-lib/flowy-text-block/tests/editor/serde_test.rs
  48. 1 0
      frontend/rust-lib/flowy-text-block/tests/editor/undo_redo_test.rs
  49. 7 4
      frontend/scripts/install_dev_env/install_linux.sh
  50. 7 4
      frontend/scripts/install_dev_env/install_macos.sh
  51. 0 39
      frontend/scripts/makefile/githooks.toml
  52. 0 6
      package.json
  53. 3 3
      shared-lib/flowy-sync/src/client_document/default/mod.rs
  54. 5 5
      shared-lib/flowy-sync/src/client_document/document_pad.rs
  55. 2 2
      shared-lib/flowy-sync/src/client_document/extensions/delete/preserve_line_format_merge.rs
  56. 2 2
      shared-lib/flowy-sync/src/client_document/extensions/format/resolve_block_format.rs
  57. 2 2
      shared-lib/flowy-sync/src/client_document/extensions/format/resolve_inline_format.rs
  58. 3 5
      shared-lib/flowy-sync/src/client_document/extensions/insert/auto_exit_block.rs
  59. 2 2
      shared-lib/flowy-sync/src/client_document/extensions/insert/auto_format.rs
  60. 2 2
      shared-lib/flowy-sync/src/client_document/extensions/insert/default_insert.rs
  61. 2 2
      shared-lib/flowy-sync/src/client_document/extensions/insert/preserve_block_format.rs
  62. 3 3
      shared-lib/flowy-sync/src/client_document/extensions/insert/preserve_inline_format.rs
  63. 2 2
      shared-lib/flowy-sync/src/client_document/extensions/insert/reset_format_on_new_line.rs
  64. 5 5
      shared-lib/flowy-sync/src/client_folder/builder.rs
  65. 3 3
      shared-lib/flowy-sync/src/client_folder/folder_pad.rs
  66. 15 15
      shared-lib/flowy-sync/src/client_grid/grid_block_revsion_pad.rs
  67. 9 9
      shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs
  68. 1 1
      shared-lib/flowy-sync/src/entities/revision.rs
  69. 1 1
      shared-lib/flowy-sync/src/entities/text_block.rs
  70. 1 1
      shared-lib/flowy-sync/src/server_document/document_pad.rs
  71. 2 2
      shared-lib/flowy-sync/src/server_folder/folder_manager.rs
  72. 3 3
      shared-lib/flowy-sync/src/server_folder/folder_pad.rs
  73. 2 2
      shared-lib/flowy-sync/src/util.rs
  74. 13 2
      shared-lib/lib-ot/src/core/delta/builder.rs
  75. 77 44
      shared-lib/lib-ot/src/core/delta/cursor.rs
  76. 122 55
      shared-lib/lib-ot/src/core/delta/delta.rs
  77. 2 1
      shared-lib/lib-ot/src/core/delta/delta_serde.rs
  78. 17 17
      shared-lib/lib-ot/src/core/delta/iterator.rs
  79. 3 2
      shared-lib/lib-ot/src/core/flowy_str.rs
  80. 1 1
      shared-lib/lib-ot/src/core/interval.rs
  81. 0 21
      shared-lib/lib-ot/src/core/mod.rs
  82. 15 17
      shared-lib/lib-ot/src/core/operation/builder.rs
  83. 103 39
      shared-lib/lib-ot/src/core/operation/operation.rs
  84. 2 1
      shared-lib/lib-ot/src/core/operation/operation_serde.rs
  85. 2 6
      shared-lib/lib-ot/src/rich_text/attributes.rs
  86. 2 4
      shared-lib/lib-ot/src/rich_text/delta.rs

+ 28 - 3
.githooks/commit-msg

@@ -6,7 +6,24 @@
 # status after issuing an appropriate message if it wants to stop the
 # commit.  The hook is allowed to edit the commit message file.
 
-echo "Running the AppFlowy commit-msg hook."
+YELLOW="\e[93m"
+GREEN="\e[32m"
+RED="\e[31m"
+ENDCOLOR="\e[0m"
+
+printMessage() {
+   printf "${YELLOW}AppFlowy : $1${ENDCOLOR}\n"
+}
+
+printSuccess() {
+   printf "${GREEN}AppFlowy : $1${ENDCOLOR}\n"
+}
+
+printError() {
+   printf "${RED}AppFlowy : $1${ENDCOLOR}\n"
+}
+
+printMessage "Running the AppFlowy commit-msg hook."
 
 # This example catches duplicate Signed-off-by lines.
 
@@ -16,11 +33,19 @@ test "" = "$(grep '^Signed-off-by: ' "$1" |
 	exit 1
 }
 
-npx --no -- commitlint --edit $1
+.githooks/gitlint \
+	 --msg-file=$1 \
+	 --subject-regex="^(build|chore|ci|docs|feat|feature|fix|perf|refactor|revert|style|test)(.*)?:\s?.*" \
+    --subject-maxlen=100 \
+    --subject-minlen=10 \
+    --body-regex=".*" \
+    --body-maxlen=200 \
+    --max-parents=1
 
 if [ $? -ne 0 ]
 then
-    echo "Please fix your commit message to match AppFlowy coding standards"
+    printError "Please fix your commit message to match AppFlowy coding standards"
+    printError "https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/software-contributions/submitting-code/style-guides"
     exit 1
 fi
 

+ 18 - 1
.githooks/pre-commit

@@ -1,6 +1,23 @@
 #!/usr/bin/env bash
 
-echo "Running local AppFlowy pre-commit hook."
+YELLOW="\e[93m"
+GREEN="\e[32m"
+RED="\e[31m"
+ENDCOLOR="\e[0m"
+
+printMessage() {
+   printf "${YELLOW}AppFlowy : $1${ENDCOLOR}\n"
+}
+
+printSuccess() {
+   printf "${GREEN}AppFlowy : $1${ENDCOLOR}\n"
+}
+
+printError() {
+   printf "${RED}AppFlowy : $1${ENDCOLOR}\n"
+}
+
+printMessage "Running local AppFlowy pre-commit hook."
 
 #flutter format .
 ##https://gist.github.com/benmccallum/28e4f216d9d72f5965133e6c43aaff6e

+ 24 - 6
.githooks/pre-push

@@ -1,23 +1,41 @@
 #!/usr/bin/env bash
 
-echo "Running local AppFlowy pre-push hook."
+YELLOW="\e[93m"
+GREEN="\e[32m"
+RED="\e[31m"
+ENDCOLOR="\e[0m"
+
+printMessage() {
+   printf "${YELLOW}AppFlowy : $1${ENDCOLOR}\n"
+}
+
+printSuccess() {
+   printf "${GREEN}AppFlowy : $1${ENDCOLOR}\n"
+}
+
+printError() {
+   printf "${RED}AppFlowy : $1${ENDCOLOR}\n"
+}
+
+printMessage "Running local AppFlowy pre-push hook."
 
 if [[ `git status --porcelain` ]]; then
-  printf "\e[31;1m%s\e[0m\n" 'This script needs to run against committed code only. Please commit or stash you changes.'
+  printError "This script needs to run against committed code only. Please commit or stash you changes."
   exit 1
 fi
 
-printf "\e[33;1m%s\e[0m\n" 'Running the Flutter analyzer'
+printMessage "Running the Flutter analyzer"
 flutter analyze
 
 if [ $? -ne 0 ]; then
-  printf "\e[31;1m%s\e[0m\n" 'Flutter analyzer error'
+  printError "Flutter analyzer error"
   exit 1
 fi
 
-printf "\e[33;1m%s\e[0m\n" 'Finished running the Flutter analyzer'
-printf "\e[33;1m%s\e[0m\n" 'Running unit tests'
+printMessage "Finished running the Flutter analyzer"
+
 
+#printMessage "Running unit tests"
 #flutter test
 #if [ $? -ne 0 ]; then
 #  printf "\e[31;1m%s\e[0m\n" 'Unit tests error'

+ 3 - 0
.gitignore

@@ -32,3 +32,6 @@ frontend/.vscode/*
 # Commit the highest level pubspec.lock, but ignore the others
 pubspec.lock
 !frontend/app_flowy/pubspec.lock
+
+# ignore tool used for commit linting
+.githooks/gitlint

+ 2 - 1
README.md

@@ -45,7 +45,8 @@ Please view the [documentation](https://appflowy.gitbook.io/docs/essential-docum
 
 ## Roadmap
 
-[AppFlowy Roadmap](https://trello.com/b/NCyXCXXh/appflowy-roadmap)
+- [AppFlowy Roadmap ReadMe](https://github.com/AppFlowy-IO/AppFlowy/blob/main/ROADMAP.md)
+- [AppFlowy Public Roadmap](https://github.com/orgs/AppFlowy-IO/projects/5/views/12)
 
 If you'd like to propose a feature, submit an issue [here](https://github.com/AppFlowy-IO/appflowy/issues).
 

+ 56 - 0
ROADMAP.md

@@ -0,0 +1,56 @@
+# View the official [AppFlowy public roadmap](https://github.com/orgs/AppFlowy-IO/projects/5/views/12)
+Our [roadmap](https://github.com/orgs/AppFlowy-IO/projects/5/views/12) is where you can learn about the features we’re working on, their status, when we expect to release them, and how you can help us. Have any questions or comments about items on the roadmap? Or feedback about the roadmap itself, such as how issues are presented? Share your feedback via the [discussions](https://github.com/AppFlowy-IO/AppFlowy/discussions).
+
+
+## Guide to the roadmap
+
+### Labels
+Every item on the roadmap is an issue, with labels that indicate each of the following:
+- bug: something needs fixing
+- new feature: this is something new for the end user
+- improvements: an improvement on an existing feature
+- documentation: this is a documentation task
+- needs design: this requires a design spec
+- grid: features related to Flowy Grid
+- editor: features related to Flowy Editor
+- help-wanted: for the community members to claim
+- first time issue for devs: for the community members to claim
+- first time issue for experienced devs: for the experienced community developers to claim
+
+### Item Status
+“Status” indicates the stages that the feature goes through, from “Need triage” to “Done”. Most of the options are self explanatory.:
+- Need triage
+- Need test for Windows / Linux / macOS
+- Wait for reporter
+- Ready for assess
+- Planned: included in our plan
+- Not Planned: decided against it
+- ToDo: in the queue of the upcoming development (next two releases)
+- In progress: currently in development
+- Blocked: have started but can’t proceed as it is blocked by something
+- Done: the development is finished and merged into the main branch
+
+
+### Milestones
+The roadmap is arranged on a project board to give a sense of how far out each item is on the horizon. If a feature is planned, it is already or will be added to a particular milestone, aka release. For example, “Implement FlowyEditor’s RichText component” is added to v0.0.7. You will also find issues that are not planned for which no milestone is yet available. In addition, you can see a list of milestones that are already planned and track their progress [here](https://github.com/AppFlowy-IO/AppFlowy/milestones).
+
+### Views
+To easily track the project based on your interest, we organize issues into different views as follows:
+- [Roadmap](https://github.com/orgs/AppFlowy-IO/projects/5/views/12)
+- [v0.0.x](https://github.com/orgs/AppFlowy-IO/projects/5/views/1)
+- [Upcoming](https://github.com/orgs/AppFlowy-IO/projects/5/views/3)
+- [Help Wanted](https://github.com/orgs/AppFlowy-IO/projects/5/views/4)
+- [Editor](https://github.com/orgs/AppFlowy-IO/projects/5/views/5)
+- [Grid](https://github.com/orgs/AppFlowy-IO/projects/5/views/6)
+- [Bug Tracker](https://github.com/orgs/AppFlowy-IO/projects/5/views/9)
+
+If you are interested in contributing to AppFlowy, please have a look at the “[Help Wanted](https://github.com/orgs/AppFlowy-IO/projects/5/views/4)” tab where we maintain a list of issues open to the community.
+Flowy Editor and Flowy Grid are two key components AppFlowy offers. Flowy Editor is a rich-text editor. Flowy Grid is a database component that can be inserted into Flowy Editor. Both components are developed by AppFlowy’s team from the ground up. You can use the “Editor” and the “Grid” tab to track the development progress of these two components.
+
+## Disclaimer
+The roadmap is subject to change, especially further out on the timeline. Any statement in this repository that is not purely historical is considered a forward-looking statement. The forward-looking roadmap does not represent a commitment, guarantee, obligation or promise to deliver any product or feature, or the deliver any product and feature by any particular date, and is intended to outline the general development plans.
+
+
+## Acknowledgements
+This article is adapted from GitHub public roadmap’s [README.md](https://github.com/github/roadmap)
+

+ 1 - 0
commitlint.config.js

@@ -22,3 +22,4 @@ module.exports = {
         'footer-max-line-length': [2, 'always', 100]
     },
 };
+

+ 0 - 1
frontend/Makefile.toml

@@ -8,7 +8,6 @@ extend = [
     { path = "scripts/makefile/env.toml" },
     { path = "scripts/makefile/flutter.toml" },
     { path = "scripts/makefile/tool.toml" },
-    { path = "scripts/makefile/githooks.toml" },
 ]
 
 [config]

+ 1 - 0
frontend/app_flowy/lib/startup/tasks/app_widget.dart

@@ -36,6 +36,7 @@ class InitAppWidgetTask extends LaunchTask {
               Locale('fr', 'FR'),
               Locale('fr', 'CA'),
               Locale('hu', 'HU'),
+              Locale('id', 'ID'),
               Locale('it', 'IT'),
               Locale('ja', 'JP'),
               Locale('pt', 'BR'),

+ 2 - 2
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_data_persistence.dart

@@ -58,8 +58,8 @@ class DateCellDataPersistence implements IGridCellDataPersistence<CalendarData>
   }
 }
 
-GridCellIdentifierPayloadPB _makeCellIdPayload(GridCellIdentifier cellId) {
-  return GridCellIdentifierPayloadPB.create()
+GridCellIdPB _makeCellIdPayload(GridCellIdentifier cellId) {
+  return GridCellIdPB.create()
     ..gridId = cellId.gridId
     ..fieldId = cellId.fieldId
     ..rowId = cellId.rowId;

+ 1 - 1
frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/cell_service.dart

@@ -46,7 +46,7 @@ class CellService {
   Future<Either<GridCellPB, FlowyError>> getCell({
     required GridCellIdentifier cellId,
   }) {
-    final payload = GridCellIdentifierPayloadPB.create()
+    final payload = GridCellIdPB.create()
       ..gridId = cellId.gridId
       ..fieldId = cellId.fieldId
       ..rowId = cellId.rowId;

+ 4 - 4
frontend/app_flowy/lib/workspace/application/grid/cell/select_option_service.dart

@@ -19,7 +19,7 @@ class SelectOptionService {
       (result) {
         return result.fold(
           (option) {
-            final cellIdentifier = GridCellIdentifierPayloadPB.create()
+            final cellIdentifier = GridCellIdPB.create()
               ..gridId = gridId
               ..fieldId = fieldId
               ..rowId = rowId;
@@ -54,7 +54,7 @@ class SelectOptionService {
   }
 
   Future<Either<SelectOptionCellDataPB, FlowyError>> getOpitonContext() {
-    final payload = GridCellIdentifierPayloadPB.create()
+    final payload = GridCellIdPB.create()
       ..gridId = gridId
       ..fieldId = fieldId
       ..rowId = rowId;
@@ -76,8 +76,8 @@ class SelectOptionService {
     return GridEventUpdateSelectOptionCell(payload).send();
   }
 
-  GridCellIdentifierPayloadPB _cellIdentifier() {
-    return GridCellIdentifierPayloadPB.create()
+  GridCellIdPB _cellIdentifier() {
+    return GridCellIdPB.create()
       ..gridId = gridId
       ..fieldId = fieldId
       ..rowId = rowId;

+ 5 - 5
frontend/app_flowy/lib/workspace/application/grid/field/field_service.dart

@@ -103,7 +103,7 @@ class FieldService {
   }
 
   Future<Either<Unit, FlowyError>> deleteField() {
-    final payload = GridFieldIdentifierPayloadPB.create()
+    final payload = DeleteFieldPayloadPB.create()
       ..gridId = gridId
       ..fieldId = fieldId;
 
@@ -111,7 +111,7 @@ class FieldService {
   }
 
   Future<Either<Unit, FlowyError>> duplicateField() {
-    final payload = GridFieldIdentifierPayloadPB.create()
+    final payload = DuplicateFieldPayloadPB.create()
       ..gridId = gridId
       ..fieldId = fieldId;
 
@@ -121,7 +121,7 @@ class FieldService {
   Future<Either<FieldTypeOptionDataPB, FlowyError>> getFieldTypeOptionData({
     required FieldType fieldType,
   }) {
-    final payload = EditFieldPayloadPB.create()
+    final payload = GridFieldTypeOptionIdPB.create()
       ..gridId = gridId
       ..fieldId = fieldId
       ..fieldType = fieldType;
@@ -165,7 +165,7 @@ class NewFieldTypeOptionLoader extends IFieldTypeOptionLoader {
 
   @override
   Future<Either<FieldTypeOptionDataPB, FlowyError>> load() {
-    final payload = EditFieldPayloadPB.create()
+    final payload = CreateFieldPayloadPB.create()
       ..gridId = gridId
       ..fieldType = FieldType.RichText;
 
@@ -185,7 +185,7 @@ class FieldTypeOptionLoader extends IFieldTypeOptionLoader {
 
   @override
   Future<Either<FieldTypeOptionDataPB, FlowyError>> load() {
-    final payload = EditFieldPayloadPB.create()
+    final payload = GridFieldTypeOptionIdPB.create()
       ..gridId = gridId
       ..fieldId = field.id
       ..fieldType = field.fieldType;

+ 2 - 5
frontend/app_flowy/lib/workspace/application/grid/field/type_option/type_option_service.dart

@@ -21,13 +21,10 @@ class TypeOptionService {
   Future<Either<SelectOptionPB, FlowyError>> newOption({
     required String name,
   }) {
-    final fieldIdentifier = GridFieldIdentifierPayloadPB.create()
-      ..gridId = gridId
-      ..fieldId = fieldId;
-
     final payload = CreateSelectOptionPayloadPB.create()
       ..optionName = name
-      ..fieldIdentifier = fieldIdentifier;
+      ..gridId = gridId
+      ..fieldId = fieldId;
 
     return GridEventNewSelectOption(payload).send();
   }

+ 4 - 4
frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart

@@ -191,7 +191,7 @@ class GridRowCache {
   }
 
   Future<void> _loadRow(String rowId) async {
-    final payload = GridRowIdPayloadPB.create()
+    final payload = GridRowIdPB.create()
       ..gridId = gridId
       ..blockId = block.id
       ..rowId = rowId;
@@ -297,7 +297,7 @@ class RowService {
   }
 
   Future<Either<OptionalRowPB, FlowyError>> getRow() {
-    final payload = GridRowIdPayloadPB.create()
+    final payload = GridRowIdPB.create()
       ..gridId = gridId
       ..blockId = blockId
       ..rowId = rowId;
@@ -306,7 +306,7 @@ class RowService {
   }
 
   Future<Either<Unit, FlowyError>> deleteRow() {
-    final payload = GridRowIdPayloadPB.create()
+    final payload = GridRowIdPB.create()
       ..gridId = gridId
       ..blockId = blockId
       ..rowId = rowId;
@@ -315,7 +315,7 @@ class RowService {
   }
 
   Future<Either<Unit, FlowyError>> duplicateRow() {
-    final payload = GridRowIdPayloadPB.create()
+    final payload = GridRowIdPB.create()
       ..gridId = gridId
       ..blockId = blockId
       ..rowId = rowId;

+ 5 - 1
frontend/app_flowy/lib/workspace/presentation/settings/widgets/settings_appearance_view.dart

@@ -1,10 +1,13 @@
 import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:app_flowy/workspace/application/appearance.dart';
+import 'package:app_flowy/workspace/presentation/widgets/toggle/toggle_style.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flowy_infra/theme.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 
+import '../../widgets/toggle/toggle.dart';
+
 class SettingsAppearanceView extends StatelessWidget {
   const SettingsAppearanceView({Key? key}) : super(key: key);
 
@@ -25,11 +28,12 @@ class SettingsAppearanceView extends StatelessWidget {
                   fontWeight: FontWeight.w500,
                 ),
               ),
-              Switch(
+              Toggle(
                 value: theme.isDark,
                 onChanged: (val) {
                   context.read<AppearanceSettingModel>().swapTheme();
                 },
+                style: ToggleStyle.big(theme),
               ),
               Text(
                 LocaleKeys.settings_appearance_darkLabel.tr(),

+ 82 - 19
frontend/app_flowy/lib/workspace/presentation/settings/widgets/settings_language_view.dart

@@ -1,18 +1,37 @@
+import 'package:app_flowy/generated/locale_keys.g.dart';
 import 'package:app_flowy/workspace/application/appearance.dart';
 import 'package:easy_localization/easy_localization.dart';
+import 'package:flowy_infra/theme.dart';
 import 'package:flutter/material.dart';
 import 'package:flowy_infra/language.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:provider/provider.dart';
 
 class SettingsLanguageView extends StatelessWidget {
   const SettingsLanguageView({Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
-    return SingleChildScrollView(
-      child: Column(
-        crossAxisAlignment: CrossAxisAlignment.start,
-        children: const [LanguageSelectorDropdown()],
+    context.watch<AppTheme>();
+    return ChangeNotifierProvider.value(
+      value: Provider.of<AppearanceSettingModel>(context, listen: true),
+      child: SingleChildScrollView(
+        child: Column(
+          crossAxisAlignment: CrossAxisAlignment.start,
+          children: [
+            Row(
+              children: [
+                Text(
+                  LocaleKeys.settings_menu_language.tr(),
+                  style: const TextStyle(
+                    fontSize: 14,
+                    fontWeight: FontWeight.w500,
+                  ),
+                ),
+                const LanguageSelectorDropdown(),
+              ],
+            ),
+          ],
+        ),
       ),
     );
   }
@@ -28,22 +47,66 @@ class LanguageSelectorDropdown extends StatefulWidget {
 }
 
 class _LanguageSelectorDropdownState extends State<LanguageSelectorDropdown> {
+  Color currHoverColor = Colors.white.withOpacity(0.0);
+  late Color themedHoverColor;
+  void hoverExitLanguage() {
+    setState(() {
+      currHoverColor = Colors.white.withOpacity(0.0);
+    });
+  }
+
+  void hoverEnterLanguage() {
+    setState(() {
+      currHoverColor = themedHoverColor;
+    });
+  }
+
   @override
   Widget build(BuildContext context) {
-    return DropdownButton<Locale>(
-      value: context.read<AppearanceSettingModel>().locale,
-      onChanged: (val) {
-        setState(() {
-          context.read<AppearanceSettingModel>().setLocale(context, val!);
-        });
-      },
-      autofocus: true,
-      items: EasyLocalization.of(context)!.supportedLocales.map((locale) {
-        return DropdownMenuItem<Locale>(
-          value: locale,
-          child: Text(languageFromLocale(locale)),
-        );
-      }).toList(),
+    final theme = context.watch<AppTheme>();
+    themedHoverColor = theme.main2;
+
+    return MouseRegion(
+      onEnter: (event) => {hoverEnterLanguage()},
+      onExit: (event) => {hoverExitLanguage()},
+      child: Container(
+        margin: const EdgeInsets.only(left: 8, right: 8),
+        decoration: BoxDecoration(
+          borderRadius: BorderRadius.circular(8),
+          color: currHoverColor,
+        ),
+        child: DropdownButtonHideUnderline(
+          child: DropdownButton<Locale>(
+            value: context.read<AppearanceSettingModel>().locale,
+            onChanged: (val) {
+              setState(() {
+                context.read<AppearanceSettingModel>().setLocale(context, val!);
+              });
+            },
+            icon: const Visibility(
+              child: (Icon(Icons.arrow_downward)),
+              visible: false,
+            ),
+            borderRadius: BorderRadius.circular(8),
+            items: EasyLocalization.of(context)!.supportedLocales.map((locale) {
+              return DropdownMenuItem<Locale>(
+                value: locale,
+                child: Padding(
+                  padding: const EdgeInsets.all(12.0),
+                  child: Text(
+                    languageFromLocale(locale),
+                    style: TextStyle(
+                      fontSize: 14,
+                      fontWeight: FontWeight.w500,
+                      color: theme.textColor,
+                    ),
+                  ),
+                ),
+              );
+            }).toList(),
+          ),
+        ),
+      ),
     );
   }
 }

+ 52 - 0
frontend/app_flowy/lib/workspace/presentation/widgets/toggle/toggle.dart

@@ -0,0 +1,52 @@
+import 'package:app_flowy/workspace/presentation/widgets/toggle/toggle_style.dart';
+import 'package:flutter/widgets.dart';
+
+class Toggle extends StatelessWidget {
+  final ToggleStyle style;
+  final bool value;
+  final void Function(bool) onChanged;
+  final EdgeInsets padding;
+
+  const Toggle({
+    Key? key,
+    required this.value,
+    required this.onChanged,
+    required this.style,
+    this.padding = const EdgeInsets.all(8.0),
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return GestureDetector(
+      onTap: (() => onChanged(value)),
+      child: Padding(
+        padding: padding,
+        child: Stack(
+          children: [
+            Container(
+              height: style.height,
+              width: style.width,
+              decoration: BoxDecoration(
+                color: value ? style.activeBackgroundColor : style.inactiveBackgroundColor,
+                borderRadius: BorderRadius.circular(style.height / 2),
+              ),
+            ),
+            AnimatedPositioned(
+              duration: const Duration(milliseconds: 150),
+              top: (style.height - style.thumbRadius) / 2,
+              left: value ? style.width - style.thumbRadius - 1 : 1,
+              child: Container(
+                height: style.thumbRadius,
+                width: style.thumbRadius,
+                decoration: BoxDecoration(
+                  color: style.thumbColor,
+                  borderRadius: BorderRadius.circular(style.thumbRadius / 2),
+                ),
+              ),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}

+ 38 - 0
frontend/app_flowy/lib/workspace/presentation/widgets/toggle/toggle_style.dart

@@ -0,0 +1,38 @@
+import 'package:flowy_infra/theme.dart';
+import 'package:flutter/painting.dart';
+import 'package:flutter/widgets.dart';
+
+class ToggleStyle {
+  final double height;
+  final double width;
+
+  final double thumbRadius;
+  final Color thumbColor;
+  final Color activeBackgroundColor;
+  final Color inactiveBackgroundColor;
+
+  ToggleStyle({
+    required this.height,
+    required this.width,
+    required this.thumbRadius,
+    required this.thumbColor,
+    required this.activeBackgroundColor,
+    required this.inactiveBackgroundColor,
+  });
+
+  ToggleStyle.big(AppTheme theme)
+      : height = 16,
+        width = 27,
+        thumbRadius = 14,
+        activeBackgroundColor = theme.main1,
+        inactiveBackgroundColor = theme.shader5,
+        thumbColor = theme.surface;
+
+  ToggleStyle.small(AppTheme theme)
+      : height = 10,
+        width = 16,
+        thumbRadius = 8,
+        activeBackgroundColor = theme.main1,
+        inactiveBackgroundColor = theme.shader5,
+        thumbColor = theme.surface;
+}

+ 2 - 0
frontend/app_flowy/packages/flowy_infra/lib/language.dart

@@ -26,6 +26,8 @@ String languageFromLocale(Locale locale) {
       }
     case "hu":
       return "Magyar";
+    case "id":
+      return "Bahasa";
     case "it":
       return "Italiano";
     case "ja":

+ 3 - 3
frontend/app_flowy/pubspec.lock

@@ -7,14 +7,14 @@ packages:
       name: _fe_analyzer_shared
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "38.0.0"
+    version: "42.0.0"
   analyzer:
-    dependency: transitive
+    dependency: "direct overridden"
     description:
       name: analyzer
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "3.4.1"
+    version: "4.3.0"
   animations:
     dependency: transitive
     description:

+ 1 - 1
frontend/rust-lib/flowy-folder/src/manager.rs

@@ -215,7 +215,7 @@ impl DefaultFolderBuilder {
         for app in workspace_rev.apps.iter() {
             for (index, view) in app.belongings.iter().enumerate() {
                 let view_data = if index == 0 {
-                    initial_read_me().to_delta_str()
+                    initial_read_me().to_json_str()
                 } else {
                     initial_quill_delta_string()
                 };

+ 4 - 4
frontend/rust-lib/flowy-folder/src/services/folder_editor.rs

@@ -10,7 +10,7 @@ use flowy_sync::{
     entities::{revision::Revision, ws_data::ServerRevisionWSData},
 };
 use lib_infra::future::FutureResult;
-use lib_ot::core::PlainTextAttributes;
+use lib_ot::core::PhantomAttributes;
 
 use parking_lot::RwLock;
 use std::sync::Arc;
@@ -80,7 +80,7 @@ impl FolderEditor {
     pub(crate) fn apply_change(&self, change: FolderChange) -> FlowyResult<()> {
         let FolderChange { delta, md5 } = change;
         let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
-        let delta_data = delta.to_delta_bytes();
+        let delta_data = delta.to_json_bytes();
         let revision = Revision::new(
             &self.rev_manager.object_id,
             base_rev_id,
@@ -132,7 +132,7 @@ impl FolderEditor {
 pub struct FolderRevisionCompactor();
 impl RevisionCompactor for FolderRevisionCompactor {
     fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
-        let delta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
-        Ok(delta.to_delta_bytes())
+        let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
+        Ok(delta.to_json_bytes())
     }
 }

+ 1 - 1
frontend/rust-lib/flowy-folder/src/services/persistence/mod.rs

@@ -110,7 +110,7 @@ impl FolderPersistence {
     pub async fn save_folder(&self, user_id: &str, folder_id: &FolderId, folder: FolderPad) -> FlowyResult<()> {
         let pool = self.database.db_pool()?;
         let json = folder.to_json()?;
-        let delta_data = PlainTextDeltaBuilder::new().insert(&json).build().to_delta_bytes();
+        let delta_data = PlainTextDeltaBuilder::new().insert(&json).build().to_json_bytes();
         let revision = Revision::initial_revision(user_id, folder_id.as_ref(), delta_data);
         let record = RevisionRecord {
             revision,

+ 4 - 4
frontend/rust-lib/flowy-folder/src/services/web_socket.rs

@@ -10,7 +10,7 @@ use flowy_sync::{
     },
 };
 use lib_infra::future::{BoxResultFuture, FutureResult};
-use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta};
+use lib_ot::core::{OperationTransformable, PhantomAttributes, PlainTextDelta};
 use parking_lot::RwLock;
 use std::{sync::Arc, time::Duration};
 
@@ -24,7 +24,7 @@ pub(crate) async fn make_folder_ws_manager(
 ) -> Arc<RevisionWebSocketManager> {
     let ws_data_provider = Arc::new(WSDataProvider::new(folder_id, Arc::new(rev_manager.clone())));
     let resolver = Arc::new(FolderConflictResolver { folder_pad });
-    let conflict_controller = ConflictController::<PlainTextAttributes>::new(
+    let conflict_controller = ConflictController::<PhantomAttributes>::new(
         user_id,
         resolver,
         Arc::new(ws_data_provider.clone()),
@@ -55,7 +55,7 @@ struct FolderConflictResolver {
     folder_pad: Arc<RwLock<FolderPad>>,
 }
 
-impl ConflictResolver<PlainTextAttributes> for FolderConflictResolver {
+impl ConflictResolver<PhantomAttributes> for FolderConflictResolver {
     fn compose_delta(&self, delta: PlainTextDelta) -> BoxResultFuture<DeltaMD5, FlowyError> {
         let folder_pad = self.folder_pad.clone();
         Box::pin(async move {
@@ -67,7 +67,7 @@ impl ConflictResolver<PlainTextAttributes> for FolderConflictResolver {
     fn transform_delta(
         &self,
         delta: PlainTextDelta,
-    ) -> BoxResultFuture<TransformDeltas<PlainTextAttributes>, FlowyError> {
+    ) -> BoxResultFuture<TransformDeltas<PhantomAttributes>, FlowyError> {
         let folder_pad = self.folder_pad.clone();
         Box::pin(async move {
             let read_guard = folder_pad.read();

+ 16 - 17
frontend/rust-lib/flowy-grid/src/entities/cell_entities.rs

@@ -1,4 +1,4 @@
-use crate::entities::{FieldIdentifierParams, GridFieldIdentifierPayloadPB};
+
 use flowy_derive::ProtoBuf;
 use flowy_error::ErrorCode;
 use flowy_grid_data_model::parser::NotEmptyStr;
@@ -8,23 +8,20 @@ use std::collections::HashMap;
 #[derive(ProtoBuf, Default)]
 pub struct CreateSelectOptionPayloadPB {
     #[pb(index = 1)]
-    pub field_identifier: GridFieldIdentifierPayloadPB,
+    pub field_id: String,
 
     #[pb(index = 2)]
+    pub grid_id: String,
+
+    #[pb(index = 3)]
     pub option_name: String,
 }
 
 pub struct CreateSelectOptionParams {
-    pub field_identifier: FieldIdentifierParams,
+    pub field_id: String,
+    pub grid_id: String,
     pub option_name: String,
-}
 
-impl std::ops::Deref for CreateSelectOptionParams {
-    type Target = FieldIdentifierParams;
-
-    fn deref(&self) -> &Self::Target {
-        &self.field_identifier
-    }
 }
 
 impl TryInto<CreateSelectOptionParams> for CreateSelectOptionPayloadPB {
@@ -32,16 +29,18 @@ impl TryInto<CreateSelectOptionParams> for CreateSelectOptionPayloadPB {
 
     fn try_into(self) -> Result<CreateSelectOptionParams, Self::Error> {
         let option_name = NotEmptyStr::parse(self.option_name).map_err(|_| ErrorCode::SelectOptionNameIsEmpty)?;
-        let field_identifier = self.field_identifier.try_into()?;
+        let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
+        let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
         Ok(CreateSelectOptionParams {
-            field_identifier,
+            field_id: field_id.0,
             option_name: option_name.0,
+            grid_id: grid_id.0,
         })
     }
 }
 
 #[derive(Debug, Clone, Default, ProtoBuf)]
-pub struct GridCellIdentifierPayloadPB {
+pub struct GridCellIdPB {
     #[pb(index = 1)]
     pub grid_id: String,
 
@@ -52,20 +51,20 @@ pub struct GridCellIdentifierPayloadPB {
     pub row_id: String,
 }
 
-pub struct CellIdentifierParams {
+pub struct GridCellIdParams {
     pub grid_id: String,
     pub field_id: String,
     pub row_id: String,
 }
 
-impl TryInto<CellIdentifierParams> for GridCellIdentifierPayloadPB {
+impl TryInto<GridCellIdParams> for GridCellIdPB {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<CellIdentifierParams, Self::Error> {
+    fn try_into(self) -> Result<GridCellIdParams, Self::Error> {
         let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
         let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
         let row_id = NotEmptyStr::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
-        Ok(CellIdentifierParams {
+        Ok(GridCellIdParams {
             grid_id: grid_id.0,
             field_id: field_id.0,
             row_id: row_id.0,

+ 97 - 9
frontend/rust-lib/flowy-grid/src/entities/field_entities.rs

@@ -155,6 +155,45 @@ pub struct GetEditFieldContextPayloadPB {
     pub field_type: FieldType,
 }
 
+
+
+#[derive(Debug, Default, ProtoBuf)]
+pub struct CreateFieldPayloadPB {
+    #[pb(index = 1)]
+    pub grid_id: String,
+
+    #[pb(index = 2)]
+    pub field_id: String,
+
+    #[pb(index = 3)]
+    pub field_type: FieldType,
+
+    #[pb(index = 4)]
+    pub create_if_not_exist: bool,
+}
+
+pub struct CreateFieldParams {
+    pub grid_id: String,
+    pub field_id: String,
+    pub field_type: FieldType,
+}
+
+impl TryInto<CreateFieldParams> for CreateFieldPayloadPB {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<CreateFieldParams, Self::Error> {
+        let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
+        let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
+        Ok(CreateFieldParams {
+            grid_id: grid_id.0,
+            field_id: field_id.0,
+            field_type: self.field_type,
+        })
+    }
+}
+
+
+
 #[derive(Debug, Default, ProtoBuf)]
 pub struct EditFieldPayloadPB {
     #[pb(index = 1)]
@@ -190,19 +229,34 @@ impl TryInto<EditFieldParams> for EditFieldPayloadPB {
     }
 }
 
-pub struct CreateFieldParams {
+#[derive(Debug, Default, ProtoBuf)]
+pub struct GridFieldTypeOptionIdPB {
+    #[pb(index = 1)]
     pub grid_id: String,
+
+    #[pb(index = 2)]
+    pub field_id: String,
+
+    #[pb(index = 3)]
     pub field_type: FieldType,
 }
 
-impl TryInto<CreateFieldParams> for EditFieldPayloadPB {
+
+pub struct GridFieldTypeOptionIdParams {
+    pub grid_id: String,
+    pub field_id: String,
+    pub field_type: FieldType,
+}
+
+impl TryInto<GridFieldTypeOptionIdParams> for GridFieldTypeOptionIdPB {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<CreateFieldParams, Self::Error> {
+    fn try_into(self) -> Result<GridFieldTypeOptionIdParams, Self::Error> {
         let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
-
-        Ok(CreateFieldParams {
+        let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
+        Ok(GridFieldTypeOptionIdParams {
             grid_id: grid_id.0,
+            field_id: field_id.0,
             field_type: self.field_type,
         })
     }
@@ -556,6 +610,16 @@ impl std::convert::From<FieldTypeRevision> for FieldType {
         }
     }
 }
+#[derive(Debug, Clone, Default, ProtoBuf)]
+pub struct DuplicateFieldPayloadPB {
+    #[pb(index = 1)]
+    pub field_id: String,
+
+    #[pb(index = 2)]
+    pub grid_id: String,
+}
+
+
 #[derive(Debug, Clone, Default, ProtoBuf)]
 pub struct GridFieldIdentifierPayloadPB {
     #[pb(index = 1)]
@@ -565,20 +629,44 @@ pub struct GridFieldIdentifierPayloadPB {
     pub grid_id: String,
 }
 
-pub struct FieldIdentifierParams {
+impl TryInto<GridFieldIdParams> for DuplicateFieldPayloadPB {
+    type Error = ErrorCode;
+
+    fn try_into(self) -> Result<GridFieldIdParams, Self::Error> {
+        let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
+        let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
+        Ok(GridFieldIdParams {
+            grid_id: grid_id.0,
+            field_id: field_id.0,
+        })
+    }
+}
+
+#[derive(Debug, Clone, Default, ProtoBuf)]
+pub struct DeleteFieldPayloadPB {
+    #[pb(index = 1)]
     pub field_id: String,
+
+    #[pb(index = 2)]
     pub grid_id: String,
 }
 
-impl TryInto<FieldIdentifierParams> for GridFieldIdentifierPayloadPB {
+impl TryInto<GridFieldIdParams> for DeleteFieldPayloadPB {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<FieldIdentifierParams, Self::Error> {
+    fn try_into(self) -> Result<GridFieldIdParams, Self::Error> {
         let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
         let field_id = NotEmptyStr::parse(self.field_id).map_err(|_| ErrorCode::FieldIdIsEmpty)?;
-        Ok(FieldIdentifierParams {
+        Ok(GridFieldIdParams {
             grid_id: grid_id.0,
             field_id: field_id.0,
         })
     }
 }
+
+pub struct GridFieldIdParams {
+    pub field_id: String,
+    pub grid_id: String,
+}
+
+

+ 7 - 12
frontend/rust-lib/flowy-grid/src/entities/row_entities.rs

@@ -2,8 +2,9 @@ use flowy_derive::ProtoBuf;
 use flowy_error::ErrorCode;
 use flowy_grid_data_model::parser::NotEmptyStr;
 
-#[derive(ProtoBuf, Default)]
-pub struct GridRowIdPayloadPB {
+
+#[derive(Debug, Default, Clone, ProtoBuf)]
+pub struct GridRowIdPB {
     #[pb(index = 1)]
     pub grid_id: String,
 
@@ -14,27 +15,21 @@ pub struct GridRowIdPayloadPB {
     pub row_id: String,
 }
 
-#[derive(Debug, Default, Clone, ProtoBuf)]
-pub struct GridRowIdPB {
-    #[pb(index = 1)]
+pub struct GridRowIdParams {
     pub grid_id: String,
-
-    #[pb(index = 2)]
     pub block_id: String,
-
-    #[pb(index = 3)]
     pub row_id: String,
 }
 
-impl TryInto<GridRowIdPB> for GridRowIdPayloadPB {
+impl TryInto<GridRowIdParams> for GridRowIdPB {
     type Error = ErrorCode;
 
-    fn try_into(self) -> Result<GridRowIdPB, Self::Error> {
+    fn try_into(self) -> Result<GridRowIdParams, Self::Error> {
         let grid_id = NotEmptyStr::parse(self.grid_id).map_err(|_| ErrorCode::GridIdIsEmpty)?;
         let block_id = NotEmptyStr::parse(self.block_id).map_err(|_| ErrorCode::BlockIdIsEmpty)?;
         let row_id = NotEmptyStr::parse(self.row_id).map_err(|_| ErrorCode::RowIdIsEmpty)?;
 
-        Ok(GridRowIdPB {
+        Ok(GridRowIdParams {
             grid_id: grid_id.0,
             block_id: block_id.0,
             row_id: row_id.0,

+ 17 - 17
frontend/rust-lib/flowy-grid/src/event_handler.rs

@@ -113,10 +113,10 @@ pub(crate) async fn update_field_type_option_handler(
 
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn delete_field_handler(
-    data: Data<GridFieldIdentifierPayloadPB>,
+    data: Data<DeleteFieldPayloadPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
-    let params: FieldIdentifierParams = data.into_inner().try_into()?;
+    let params: GridFieldIdParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     let _ = editor.delete_field(&params.field_id).await?;
     Ok(())
@@ -151,10 +151,10 @@ pub(crate) async fn switch_to_field_handler(
 
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn duplicate_field_handler(
-    data: Data<GridFieldIdentifierPayloadPB>,
+    data: Data<DuplicateFieldPayloadPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
-    let params: FieldIdentifierParams = data.into_inner().try_into()?;
+    let params: GridFieldIdParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     let _ = editor.duplicate_field(&params.field_id).await?;
     Ok(())
@@ -163,10 +163,10 @@ pub(crate) async fn duplicate_field_handler(
 /// Return the FieldTypeOptionData if the Field exists otherwise return record not found error.
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn get_field_type_option_data_handler(
-    data: Data<EditFieldPayloadPB>,
+    data: Data<GridFieldTypeOptionIdPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<FieldTypeOptionDataPB, FlowyError> {
-    let params: EditFieldParams = data.into_inner().try_into()?;
+    let params: GridFieldTypeOptionIdParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     match editor.get_field_rev(&params.field_id).await {
         None => Err(FlowyError::record_not_found()),
@@ -186,7 +186,7 @@ pub(crate) async fn get_field_type_option_data_handler(
 /// Create FieldMeta and save it. Return the FieldTypeOptionData.
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn create_field_type_option_data_handler(
-    data: Data<EditFieldPayloadPB>,
+    data: Data<CreateFieldPayloadPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<FieldTypeOptionDataPB, FlowyError> {
     let params: CreateFieldParams = data.into_inner().try_into()?;
@@ -227,10 +227,10 @@ async fn get_type_option_data(field_rev: &FieldRevision, field_type: &FieldType)
 
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn get_row_handler(
-    data: Data<GridRowIdPayloadPB>,
+    data: Data<GridRowIdPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<OptionalRowPB, FlowyError> {
-    let params: GridRowIdPB = data.into_inner().try_into()?;
+    let params: GridRowIdParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     let row = editor
         .get_row_rev(&params.row_id)
@@ -242,10 +242,10 @@ pub(crate) async fn get_row_handler(
 
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn delete_row_handler(
-    data: Data<GridRowIdPayloadPB>,
+    data: Data<GridRowIdPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
-    let params: GridRowIdPB = data.into_inner().try_into()?;
+    let params: GridRowIdParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     let _ = editor.delete_row(&params.row_id).await?;
     Ok(())
@@ -253,10 +253,10 @@ pub(crate) async fn delete_row_handler(
 
 #[tracing::instrument(level = "debug", skip(data, manager), err)]
 pub(crate) async fn duplicate_row_handler(
-    data: Data<GridRowIdPayloadPB>,
+    data: Data<GridRowIdPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> Result<(), FlowyError> {
-    let params: GridRowIdPB = data.into_inner().try_into()?;
+    let params: GridRowIdParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     let _ = editor.duplicate_row(&params.row_id).await?;
     Ok(())
@@ -275,10 +275,10 @@ pub(crate) async fn create_row_handler(
 
 // #[tracing::instrument(level = "debug", skip_all, err)]
 pub(crate) async fn get_cell_handler(
-    data: Data<GridCellIdentifierPayloadPB>,
+    data: Data<GridCellIdPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<GridCellPB, FlowyError> {
-    let params: CellIdentifierParams = data.into_inner().try_into()?;
+    let params: GridCellIdParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     match editor.get_cell(&params).await {
         None => data_result(GridCellPB::empty(&params.field_id)),
@@ -357,10 +357,10 @@ pub(crate) async fn update_select_option_handler(
 
 #[tracing::instrument(level = "trace", skip(data, manager), err)]
 pub(crate) async fn get_select_option_handler(
-    data: Data<GridCellIdentifierPayloadPB>,
+    data: Data<GridCellIdPB>,
     manager: AppData<Arc<GridManager>>,
 ) -> DataResult<SelectOptionCellDataPB, FlowyError> {
-    let params: CellIdentifierParams = data.into_inner().try_into()?;
+    let params: GridCellIdParams = data.into_inner().try_into()?;
     let editor = manager.get_grid_editor(&params.grid_id)?;
     match editor.get_field_rev(&params.field_id).await {
         None => {

+ 9 - 9
frontend/rust-lib/flowy-grid/src/event_map.rs

@@ -69,28 +69,28 @@ pub enum GridEvent {
     #[event(input = "InsertFieldPayloadPB")]
     InsertField = 13,
 
-    #[event(input = "GridFieldIdentifierPayloadPB")]
+    #[event(input = "DeleteFieldPayloadPB")]
     DeleteField = 14,
 
     #[event(input = "EditFieldPayloadPB", output = "FieldTypeOptionDataPB")]
     SwitchToField = 20,
 
-    #[event(input = "GridFieldIdentifierPayloadPB")]
+    #[event(input = "DuplicateFieldPayloadPB")]
     DuplicateField = 21,
 
     #[event(input = "MoveItemPayloadPB")]
     MoveItem = 22,
 
-    #[event(input = "EditFieldPayloadPB", output = "FieldTypeOptionDataPB")]
+    #[event(input = "GridFieldTypeOptionIdPB", output = "FieldTypeOptionDataPB")]
     GetFieldTypeOption = 23,
 
-    #[event(input = "EditFieldPayloadPB", output = "FieldTypeOptionDataPB")]
+    #[event(input = "CreateFieldPayloadPB", output = "FieldTypeOptionDataPB")]
     CreateFieldTypeOption = 24,
 
     #[event(input = "CreateSelectOptionPayloadPB", output = "SelectOptionPB")]
     NewSelectOption = 30,
 
-    #[event(input = "GridCellIdentifierPayloadPB", output = "SelectOptionCellDataPB")]
+    #[event(input = "GridCellIdPB", output = "SelectOptionCellDataPB")]
     GetSelectOptionCellData = 31,
 
     #[event(input = "SelectOptionChangesetPayloadPB")]
@@ -99,16 +99,16 @@ pub enum GridEvent {
     #[event(input = "CreateRowPayloadPB", output = "GridRowPB")]
     CreateRow = 50,
 
-    #[event(input = "GridRowIdPayloadPB", output = "OptionalRowPB")]
+    #[event(input = "GridRowIdPB", output = "OptionalRowPB")]
     GetRow = 51,
 
-    #[event(input = "GridRowIdPayloadPB")]
+    #[event(input = "GridRowIdPB")]
     DeleteRow = 52,
 
-    #[event(input = "GridRowIdPayloadPB")]
+    #[event(input = "GridRowIdPB")]
     DuplicateRow = 53,
 
-    #[event(input = "GridCellIdentifierPayloadPB", output = "GridCellPB")]
+    #[event(input = "GridCellIdPB", output = "GridCellPB")]
     GetCell = 70,
 
     #[event(input = "CellChangesetPB")]

+ 2 - 2
frontend/rust-lib/flowy-grid/src/manager.rs

@@ -192,7 +192,7 @@ pub async fn make_grid_view_data(
 
         // Create grid's block
         let grid_block_delta = make_grid_block_delta(block_meta_data);
-        let block_delta_data = grid_block_delta.to_delta_bytes();
+        let block_delta_data = grid_block_delta.to_json_bytes();
         let repeated_revision: RepeatedRevision =
             Revision::initial_revision(user_id, block_id, block_delta_data).into();
         let _ = grid_manager.create_grid_block(&block_id, repeated_revision).await?;
@@ -202,7 +202,7 @@ pub async fn make_grid_view_data(
 
     // Create grid
     let grid_meta_delta = make_grid_delta(&grid_rev);
-    let grid_delta_data = grid_meta_delta.to_delta_bytes();
+    let grid_delta_data = grid_meta_delta.to_json_bytes();
     let repeated_revision: RepeatedRevision =
         Revision::initial_revision(user_id, view_id, grid_delta_data.clone()).into();
     let _ = grid_manager.create_grid(view_id, repeated_revision).await?;

+ 4 - 4
frontend/rust-lib/flowy-grid/src/services/block_revision_editor.rs

@@ -7,7 +7,7 @@ use flowy_sync::client_grid::{GridBlockMetaChange, GridBlockRevisionPad};
 use flowy_sync::entities::revision::Revision;
 use flowy_sync::util::make_delta_from_revisions;
 use lib_infra::future::FutureResult;
-use lib_ot::core::PlainTextAttributes;
+use lib_ot::core::PhantomAttributes;
 use std::borrow::Cow;
 use std::sync::Arc;
 use tokio::sync::RwLock;
@@ -161,7 +161,7 @@ impl GridBlockRevisionEditor {
         let GridBlockMetaChange { delta, md5 } = change;
         let user_id = self.user_id.clone();
         let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
-        let delta_data = delta.to_delta_bytes();
+        let delta_data = delta.to_json_bytes();
         let revision = Revision::new(
             &self.rev_manager.object_id,
             base_rev_id,
@@ -200,7 +200,7 @@ impl RevisionObjectBuilder for GridBlockMetaPadBuilder {
 pub struct GridBlockRevisionCompactor();
 impl RevisionCompactor for GridBlockRevisionCompactor {
     fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
-        let delta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
-        Ok(delta.to_delta_bytes())
+        let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
+        Ok(delta.to_json_bytes())
     }
 }

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/checkbox_type_option/checkbox_tests.rs

@@ -1,7 +1,7 @@
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
-    use crate::services::cell::{apply_cell_data_changeset, decode_any_cell_data, CellDataOperation};
+    use crate::services::cell::CellDataOperation;
     use crate::services::field::type_options::checkbox_type_option::*;
     use crate::services::field::FieldBuilder;
     use flowy_grid_data_model::revision::FieldRevision;

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_tests.rs

@@ -1,7 +1,7 @@
 #[cfg(test)]
 mod tests {
     use crate::entities::FieldType;
-    use crate::services::cell::{CellDataChangeset, CellDataOperation};
+    use crate::services::cell::CellDataOperation;
     use crate::services::field::*;
     // use crate::services::field::{DateCellChangeset, DateCellData, DateFormat, DateTypeOption, TimeFormat};
     use flowy_grid_data_model::revision::FieldRevision;

+ 4 - 4
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option/date_type_option_entities.rs

@@ -1,5 +1,5 @@
 use crate::entities::CellChangesetPB;
-use crate::entities::{CellIdentifierParams, GridCellIdentifierPayloadPB};
+use crate::entities::{GridCellIdParams, GridCellIdPB};
 use crate::services::cell::{CellBytesParser, FromCellChangeset, FromCellString};
 use bytes::Bytes;
 
@@ -24,7 +24,7 @@ pub struct DateCellDataPB {
 #[derive(Clone, Debug, Default, ProtoBuf)]
 pub struct DateChangesetPayloadPB {
     #[pb(index = 1)]
-    pub cell_identifier: GridCellIdentifierPayloadPB,
+    pub cell_identifier: GridCellIdPB,
 
     #[pb(index = 2, one_of)]
     pub date: Option<String>,
@@ -34,7 +34,7 @@ pub struct DateChangesetPayloadPB {
 }
 
 pub struct DateChangesetParams {
-    pub cell_identifier: CellIdentifierParams,
+    pub cell_identifier: GridCellIdParams,
     pub date: Option<String>,
     pub time: Option<String>,
 }
@@ -43,7 +43,7 @@ impl TryInto<DateChangesetParams> for DateChangesetPayloadPB {
     type Error = ErrorCode;
 
     fn try_into(self) -> Result<DateChangesetParams, Self::Error> {
-        let cell_identifier: CellIdentifierParams = self.cell_identifier.try_into()?;
+        let cell_identifier: GridCellIdParams = self.cell_identifier.try_into()?;
         Ok(DateChangesetParams {
             cell_identifier,
             date: self.date,

+ 6 - 6
frontend/rust-lib/flowy-grid/src/services/field/type_options/selection_type_option/select_option.rs

@@ -1,4 +1,4 @@
-use crate::entities::{CellChangesetPB, CellIdentifierParams, FieldType, GridCellIdentifierPayloadPB};
+use crate::entities::{CellChangesetPB, GridCellIdParams, FieldType, GridCellIdPB};
 use crate::services::cell::{CellBytes, CellBytesParser, CellData, CellDisplayable, FromCellChangeset, FromCellString};
 use crate::services::field::{MultiSelectTypeOption, SingleSelectTypeOptionPB};
 use bytes::Bytes;
@@ -225,7 +225,7 @@ impl CellBytesParser for SelectOptionCellDataParser {
 #[derive(Clone, Debug, Default, ProtoBuf)]
 pub struct SelectOptionCellChangesetPayloadPB {
     #[pb(index = 1)]
-    pub cell_identifier: GridCellIdentifierPayloadPB,
+    pub cell_identifier: GridCellIdPB,
 
     #[pb(index = 2, one_of)]
     pub insert_option_id: Option<String>,
@@ -235,7 +235,7 @@ pub struct SelectOptionCellChangesetPayloadPB {
 }
 
 pub struct SelectOptionCellChangesetParams {
-    pub cell_identifier: CellIdentifierParams,
+    pub cell_identifier: GridCellIdParams,
     pub insert_option_id: Option<String>,
     pub delete_option_id: Option<String>,
 }
@@ -260,7 +260,7 @@ impl TryInto<SelectOptionCellChangesetParams> for SelectOptionCellChangesetPaylo
     type Error = ErrorCode;
 
     fn try_into(self) -> Result<SelectOptionCellChangesetParams, Self::Error> {
-        let cell_identifier: CellIdentifierParams = self.cell_identifier.try_into()?;
+        let cell_identifier: GridCellIdParams = self.cell_identifier.try_into()?;
         let insert_option_id = match self.insert_option_id {
             None => None,
             Some(insert_option_id) => Some(
@@ -334,7 +334,7 @@ pub struct SelectOptionCellDataPB {
 #[derive(Clone, Debug, Default, ProtoBuf)]
 pub struct SelectOptionChangesetPayloadPB {
     #[pb(index = 1)]
-    pub cell_identifier: GridCellIdentifierPayloadPB,
+    pub cell_identifier: GridCellIdPB,
 
     #[pb(index = 2, one_of)]
     pub insert_option: Option<SelectOptionPB>,
@@ -347,7 +347,7 @@ pub struct SelectOptionChangesetPayloadPB {
 }
 
 pub struct SelectOptionChangeset {
-    pub cell_identifier: CellIdentifierParams,
+    pub cell_identifier: GridCellIdParams,
     pub insert_option: Option<SelectOptionPB>,
     pub update_option: Option<SelectOptionPB>,
     pub delete_option: Option<SelectOptionPB>,

+ 7 - 7
frontend/rust-lib/flowy-grid/src/services/grid_editor.rs

@@ -1,5 +1,5 @@
 use crate::dart_notification::{send_dart_notification, GridNotification};
-use crate::entities::CellIdentifierParams;
+use crate::entities::GridCellIdParams;
 use crate::entities::*;
 use crate::manager::{GridTaskSchedulerRwLock, GridUser};
 use crate::services::block_manager::GridBlockManager;
@@ -21,7 +21,7 @@ use flowy_sync::entities::revision::Revision;
 use flowy_sync::errors::CollaborateResult;
 use flowy_sync::util::make_delta_from_revisions;
 use lib_infra::future::FutureResult;
-use lib_ot::core::PlainTextAttributes;
+use lib_ot::core::PhantomAttributes;
 use std::collections::HashMap;
 use std::sync::Arc;
 use tokio::sync::RwLock;
@@ -339,12 +339,12 @@ impl GridRevisionEditor {
         Ok(())
     }
 
-    pub async fn get_cell(&self, params: &CellIdentifierParams) -> Option<GridCellPB> {
+    pub async fn get_cell(&self, params: &GridCellIdParams) -> Option<GridCellPB> {
         let cell_bytes = self.get_cell_bytes(params).await?;
         Some(GridCellPB::new(&params.field_id, cell_bytes.to_vec()))
     }
 
-    pub async fn get_cell_bytes(&self, params: &CellIdentifierParams) -> Option<CellBytes> {
+    pub async fn get_cell_bytes(&self, params: &GridCellIdParams) -> Option<CellBytes> {
         let field_rev = self.get_field_rev(&params.field_id).await?;
         let row_rev = self.block_manager.get_row_rev(&params.row_id).await.ok()??;
 
@@ -573,7 +573,7 @@ impl GridRevisionEditor {
         let GridChangeset { delta, md5 } = change;
         let user_id = self.user.user_id()?;
         let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
-        let delta_data = delta.to_delta_bytes();
+        let delta_data = delta.to_json_bytes();
         let revision = Revision::new(
             &self.rev_manager.object_id,
             base_rev_id,
@@ -664,8 +664,8 @@ impl RevisionCloudService for GridRevisionCloudService {
 pub struct GridRevisionCompactor();
 impl RevisionCompactor for GridRevisionCompactor {
     fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
-        let delta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
-        Ok(delta.to_delta_bytes())
+        let delta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
+        Ok(delta.to_json_bytes())
     }
 }
 

+ 1 - 1
frontend/rust-lib/flowy-grid/src/services/persistence/migration.rs

@@ -48,7 +48,7 @@ impl GridMigration {
         let pool = self.database.db_pool()?;
         let grid_rev_pad = self.get_grid_revision_pad(grid_id).await?;
         let json = grid_rev_pad.json_str()?;
-        let delta_data = PlainTextDeltaBuilder::new().insert(&json).build().to_delta_bytes();
+        let delta_data = PlainTextDeltaBuilder::new().insert(&json).build().to_json_bytes();
         let revision = Revision::initial_revision(&user_id, grid_id, delta_data);
         let record = RevisionRecord::new(revision);
         //

+ 3 - 3
frontend/rust-lib/flowy-grid/tests/grid/block_test/script.rs

@@ -2,7 +2,7 @@ use crate::grid::block_test::script::RowScript::{AssertCell, CreateRow};
 use crate::grid::block_test::util::GridRowTestBuilder;
 use crate::grid::grid_editor::GridEditorTest;
 
-use flowy_grid::entities::{CellIdentifierParams, FieldType, GridRowPB};
+use flowy_grid::entities::{GridCellIdParams, FieldType, GridRowPB};
 use flowy_grid::services::field::*;
 use flowy_grid_data_model::revision::{
     GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
@@ -109,7 +109,7 @@ impl GridRowTest {
                 field_type,
                 expected,
             } => {
-                let id = CellIdentifierParams {
+                let id = GridCellIdParams {
                     grid_id: self.grid_id.clone(),
                     field_id,
                     row_id,
@@ -154,7 +154,7 @@ impl GridRowTest {
         }
     }
 
-    async fn compare_cell_content(&self, cell_id: CellIdentifierParams, field_type: FieldType, expected: String) {
+    async fn compare_cell_content(&self, cell_id: GridCellIdParams, field_type: FieldType, expected: String) {
         match field_type {
             FieldType::RichText => {
                 let cell_data = self

+ 4 - 4
frontend/rust-lib/flowy-revision/src/conflict_resolve.rs

@@ -9,7 +9,7 @@ use flowy_sync::{
     util::make_delta_from_revisions,
 };
 use lib_infra::future::BoxResultFuture;
-use lib_ot::core::{Attributes, Delta, PlainTextAttributes};
+use lib_ot::core::{Attributes, Delta, PhantomAttributes};
 use lib_ot::rich_text::RichTextAttributes;
 use serde::de::DeserializeOwned;
 use std::{convert::TryFrom, sync::Arc};
@@ -31,7 +31,7 @@ pub trait ConflictRevisionSink: Send + Sync + 'static {
 }
 
 pub type RichTextConflictController = ConflictController<RichTextAttributes>;
-pub type PlainTextConflictController = ConflictController<PlainTextAttributes>;
+pub type PlainTextConflictController = ConflictController<PhantomAttributes>;
 
 pub struct ConflictController<T>
 where
@@ -154,7 +154,7 @@ where
         &rev_manager.object_id,
         base_rev_id,
         rev_id,
-        client_delta.to_delta_bytes(),
+        client_delta.to_json_bytes(),
         user_id,
         md5.clone(),
     );
@@ -166,7 +166,7 @@ where
                 &rev_manager.object_id,
                 base_rev_id,
                 rev_id,
-                server_delta.to_delta_bytes(),
+                server_delta.to_json_bytes(),
                 user_id,
                 md5,
             );

+ 1 - 1
frontend/rust-lib/flowy-text-block/src/editor.rs

@@ -238,7 +238,7 @@ impl RevisionObjectBuilder for TextBlockInfoBuilder {
 
         Result::<DocumentPB, FlowyError>::Ok(DocumentPB {
             block_id: object_id.to_owned(),
-            text: delta.to_delta_str(),
+            text: delta.to_json_str(),
             rev_id,
             base_rev_id,
         })

+ 2 - 2
frontend/rust-lib/flowy-text-block/src/queue.rs

@@ -175,7 +175,7 @@ impl EditBlockQueue {
     }
 
     async fn save_local_delta(&self, delta: RichTextDelta, md5: String) -> Result<RevId, FlowyError> {
-        let delta_data = delta.to_delta_bytes();
+        let delta_data = delta.to_json_bytes();
         let (base_rev_id, rev_id) = self.rev_manager.next_rev_id_pair();
         let user_id = self.user.user_id()?;
         let revision = Revision::new(
@@ -195,7 +195,7 @@ pub(crate) struct TextBlockRevisionCompactor();
 impl RevisionCompactor for TextBlockRevisionCompactor {
     fn bytes_from_revisions(&self, revisions: Vec<Revision>) -> FlowyResult<Bytes> {
         let delta = make_delta_from_revisions::<RichTextAttributes>(revisions)?;
-        Ok(delta.to_delta_bytes())
+        Ok(delta.to_json_bytes())
     }
 }
 

+ 1 - 1
frontend/rust-lib/flowy-text-block/tests/document/script.rs

@@ -75,7 +75,7 @@ impl TextBlockEditorTest {
                 let delta = self.editor.text_block_delta().await.unwrap();
                 if expected_delta != delta {
                     eprintln!("✅ expect: {}", expected,);
-                    eprintln!("❌ receive: {}", delta.to_delta_str());
+                    eprintln!("❌ receive: {}", delta.to_json_str());
                 }
                 assert_eq!(expected_delta, delta);
             }

+ 6 - 6
frontend/rust-lib/flowy-text-block/tests/editor/attribute_test.rs

@@ -762,19 +762,19 @@ fn attributes_preserve_list_format_on_merge() {
 
 #[test]
 fn delta_compose() {
-    let mut delta = RichTextDelta::from_delta_str(r#"[{"insert":"\n"}]"#).unwrap();
+    let mut delta = RichTextDelta::from_json_str(r#"[{"insert":"\n"}]"#).unwrap();
     let deltas = vec![
-        RichTextDelta::from_delta_str(r#"[{"retain":1,"attributes":{"list":"unchecked"}}]"#).unwrap(),
-        RichTextDelta::from_delta_str(r#"[{"insert":"a"}]"#).unwrap(),
-        RichTextDelta::from_delta_str(r#"[{"retain":1},{"insert":"\n","attributes":{"list":"unchecked"}}]"#).unwrap(),
-        RichTextDelta::from_delta_str(r#"[{"retain":2},{"retain":1,"attributes":{"list":""}}]"#).unwrap(),
+        RichTextDelta::from_json_str(r#"[{"retain":1,"attributes":{"list":"unchecked"}}]"#).unwrap(),
+        RichTextDelta::from_json_str(r#"[{"insert":"a"}]"#).unwrap(),
+        RichTextDelta::from_json_str(r#"[{"retain":1},{"insert":"\n","attributes":{"list":"unchecked"}}]"#).unwrap(),
+        RichTextDelta::from_json_str(r#"[{"retain":2},{"retain":1,"attributes":{"list":""}}]"#).unwrap(),
     ];
 
     for d in deltas {
         delta = delta.compose(&d).unwrap();
     }
     assert_eq!(
-        delta.to_delta_str(),
+        delta.to_json_str(),
         r#"[{"insert":"a"},{"insert":"\n","attributes":{"list":"unchecked"}},{"insert":"\n"}]"#
     );
 

+ 13 - 13
frontend/rust-lib/flowy-text-block/tests/editor/mod.rs

@@ -108,20 +108,20 @@ impl TestBuilder {
             TestOp::Insert(delta_i, s, index) => {
                 let document = &mut self.documents[*delta_i];
                 let delta = document.insert(*index, s).unwrap();
-                tracing::debug!("Insert delta: {}", delta.to_delta_str());
+                tracing::debug!("Insert delta: {}", delta.to_json_str());
 
                 self.deltas.insert(*delta_i, Some(delta));
             }
             TestOp::Delete(delta_i, iv) => {
                 let document = &mut self.documents[*delta_i];
                 let delta = document.replace(*iv, "").unwrap();
-                tracing::trace!("Delete delta: {}", delta.to_delta_str());
+                tracing::trace!("Delete delta: {}", delta.to_json_str());
                 self.deltas.insert(*delta_i, Some(delta));
             }
             TestOp::Replace(delta_i, iv, s) => {
                 let document = &mut self.documents[*delta_i];
                 let delta = document.replace(*iv, s).unwrap();
-                tracing::trace!("Replace delta: {}", delta.to_delta_str());
+                tracing::trace!("Replace delta: {}", delta.to_json_str());
                 self.deltas.insert(*delta_i, Some(delta));
             }
             TestOp::InsertBold(delta_i, s, iv) => {
@@ -133,7 +133,7 @@ impl TestBuilder {
                 let document = &mut self.documents[*delta_i];
                 let attribute = RichTextAttribute::Bold(*enable);
                 let delta = document.format(*iv, attribute).unwrap();
-                tracing::trace!("Bold delta: {}", delta.to_delta_str());
+                tracing::trace!("Bold delta: {}", delta.to_json_str());
                 self.deltas.insert(*delta_i, Some(delta));
             }
             TestOp::Italic(delta_i, iv, enable) => {
@@ -143,28 +143,28 @@ impl TestBuilder {
                     false => RichTextAttribute::Italic(false),
                 };
                 let delta = document.format(*iv, attribute).unwrap();
-                tracing::trace!("Italic delta: {}", delta.to_delta_str());
+                tracing::trace!("Italic delta: {}", delta.to_json_str());
                 self.deltas.insert(*delta_i, Some(delta));
             }
             TestOp::Header(delta_i, iv, level) => {
                 let document = &mut self.documents[*delta_i];
                 let attribute = RichTextAttribute::Header(*level);
                 let delta = document.format(*iv, attribute).unwrap();
-                tracing::trace!("Header delta: {}", delta.to_delta_str());
+                tracing::trace!("Header delta: {}", delta.to_json_str());
                 self.deltas.insert(*delta_i, Some(delta));
             }
             TestOp::Link(delta_i, iv, link) => {
                 let document = &mut self.documents[*delta_i];
                 let attribute = RichTextAttribute::Link(link.to_owned());
                 let delta = document.format(*iv, attribute).unwrap();
-                tracing::trace!("Link delta: {}", delta.to_delta_str());
+                tracing::trace!("Link delta: {}", delta.to_json_str());
                 self.deltas.insert(*delta_i, Some(delta));
             }
             TestOp::Bullet(delta_i, iv, enable) => {
                 let document = &mut self.documents[*delta_i];
                 let attribute = RichTextAttribute::Bullet(*enable);
                 let delta = document.format(*iv, attribute).unwrap();
-                tracing::debug!("Bullet delta: {}", delta.to_delta_str());
+                tracing::debug!("Bullet delta: {}", delta.to_json_str());
 
                 self.deltas.insert(*delta_i, Some(delta));
             }
@@ -194,15 +194,15 @@ impl TestBuilder {
                 let delta_a = &self.documents[*delta_a_i].delta();
                 let delta_b = &self.documents[*delta_b_i].delta();
                 tracing::debug!("Invert: ");
-                tracing::debug!("a: {}", delta_a.to_delta_str());
-                tracing::debug!("b: {}", delta_b.to_delta_str());
+                tracing::debug!("a: {}", delta_a.to_json_str());
+                tracing::debug!("b: {}", delta_b.to_json_str());
 
                 let (_, b_prime) = delta_a.transform(delta_b).unwrap();
                 let undo = b_prime.invert(delta_a);
 
                 let new_delta = delta_a.compose(&b_prime).unwrap();
-                tracing::debug!("new delta: {}", new_delta.to_delta_str());
-                tracing::debug!("undo delta: {}", undo.to_delta_str());
+                tracing::debug!("new delta: {}", new_delta.to_json_str());
+                tracing::debug!("undo delta: {}", undo.to_json_str());
 
                 let new_delta_after_undo = new_delta.compose(&undo).unwrap();
 
@@ -238,7 +238,7 @@ impl TestBuilder {
             }
 
             TestOp::AssertPrimeJson(doc_i, expected) => {
-                let prime_json = self.primes[*doc_i].as_ref().unwrap().to_delta_str();
+                let prime_json = self.primes[*doc_i].as_ref().unwrap().to_json_str();
                 let expected_prime: RichTextDelta = serde_json::from_str(expected).unwrap();
                 let target_prime: RichTextDelta = serde_json::from_str(&prime_json).unwrap();
 

+ 137 - 98
frontend/rust-lib/flowy-text-block/tests/editor/op_test.rs

@@ -2,6 +2,7 @@
 use crate::editor::{Rng, TestBuilder, TestOp::*};
 use flowy_sync::client_document::{NewlineDoc, PlainDoc};
 use lib_ot::{
+    core::Interval,
     core::*,
     rich_text::{AttributeBuilder, RichTextAttribute, RichTextAttributes, RichTextDelta},
 };
@@ -39,23 +40,23 @@ fn attributes_insert_text_at_middle() {
 #[test]
 fn delta_get_ops_in_interval_1() {
     let mut delta = RichTextDelta::default();
-    let insert_a = OpBuilder::insert("123").build();
-    let insert_b = OpBuilder::insert("4").build();
+    let insert_a = OperationBuilder::insert("123").build();
+    let insert_b = OperationBuilder::insert("4").build();
 
     delta.add(insert_a.clone());
     delta.add(insert_b.clone());
 
-    let mut iterator = DeltaIter::from_interval(&delta, Interval::new(0, 4));
+    let mut iterator = DeltaIterator::from_interval(&delta, Interval::new(0, 4));
     assert_eq!(iterator.ops(), delta.ops);
 }
 
 #[test]
 fn delta_get_ops_in_interval_2() {
     let mut delta = RichTextDelta::default();
-    let insert_a = OpBuilder::insert("123").build();
-    let insert_b = OpBuilder::insert("4").build();
-    let insert_c = OpBuilder::insert("5").build();
-    let retain_a = OpBuilder::retain(3).build();
+    let insert_a = OperationBuilder::insert("123").build();
+    let insert_b = OperationBuilder::insert("4").build();
+    let insert_c = OperationBuilder::insert("5").build();
+    let retain_a = OperationBuilder::retain(3).build();
 
     delta.add(insert_a.clone());
     delta.add(retain_a.clone());
@@ -63,32 +64,32 @@ fn delta_get_ops_in_interval_2() {
     delta.add(insert_c.clone());
 
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(0, 2)).ops(),
-        vec![OpBuilder::insert("12").build()]
+        DeltaIterator::from_interval(&delta, Interval::new(0, 2)).ops(),
+        vec![OperationBuilder::insert("12").build()]
     );
 
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(1, 3)).ops(),
-        vec![OpBuilder::insert("23").build()]
+        DeltaIterator::from_interval(&delta, Interval::new(1, 3)).ops(),
+        vec![OperationBuilder::insert("23").build()]
     );
 
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(0, 3)).ops(),
+        DeltaIterator::from_interval(&delta, Interval::new(0, 3)).ops(),
         vec![insert_a.clone()]
     );
 
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(0, 4)).ops(),
-        vec![insert_a.clone(), OpBuilder::retain(1).build()]
+        DeltaIterator::from_interval(&delta, Interval::new(0, 4)).ops(),
+        vec![insert_a.clone(), OperationBuilder::retain(1).build()]
     );
 
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(0, 6)).ops(),
+        DeltaIterator::from_interval(&delta, Interval::new(0, 6)).ops(),
         vec![insert_a.clone(), retain_a.clone()]
     );
 
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(0, 7)).ops(),
+        DeltaIterator::from_interval(&delta, Interval::new(0, 7)).ops(),
         vec![insert_a.clone(), retain_a.clone(), insert_b.clone()]
     );
 }
@@ -96,54 +97,60 @@ fn delta_get_ops_in_interval_2() {
 #[test]
 fn delta_get_ops_in_interval_3() {
     let mut delta = RichTextDelta::default();
-    let insert_a = OpBuilder::insert("123456").build();
+    let insert_a = OperationBuilder::insert("123456").build();
     delta.add(insert_a.clone());
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(3, 5)).ops(),
-        vec![OpBuilder::insert("45").build()]
+        DeltaIterator::from_interval(&delta, Interval::new(3, 5)).ops(),
+        vec![OperationBuilder::insert("45").build()]
     );
 }
 
 #[test]
 fn delta_get_ops_in_interval_4() {
     let mut delta = RichTextDelta::default();
-    let insert_a = OpBuilder::insert("12").build();
-    let insert_b = OpBuilder::insert("34").build();
-    let insert_c = OpBuilder::insert("56").build();
+    let insert_a = OperationBuilder::insert("12").build();
+    let insert_b = OperationBuilder::insert("34").build();
+    let insert_c = OperationBuilder::insert("56").build();
 
     delta.ops.push(insert_a.clone());
     delta.ops.push(insert_b.clone());
     delta.ops.push(insert_c.clone());
 
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(0, 2)).ops(),
+        DeltaIterator::from_interval(&delta, Interval::new(0, 2)).ops(),
         vec![insert_a]
     );
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(2, 4)).ops(),
+        DeltaIterator::from_interval(&delta, Interval::new(2, 4)).ops(),
         vec![insert_b]
     );
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(4, 6)).ops(),
+        DeltaIterator::from_interval(&delta, Interval::new(4, 6)).ops(),
         vec![insert_c]
     );
 
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(2, 5)).ops(),
-        vec![OpBuilder::insert("34").build(), OpBuilder::insert("5").build()]
+        DeltaIterator::from_interval(&delta, Interval::new(2, 5)).ops(),
+        vec![
+            OperationBuilder::insert("34").build(),
+            OperationBuilder::insert("5").build()
+        ]
     );
 }
 
 #[test]
 fn delta_get_ops_in_interval_5() {
     let mut delta = RichTextDelta::default();
-    let insert_a = OpBuilder::insert("123456").build();
-    let insert_b = OpBuilder::insert("789").build();
+    let insert_a = OperationBuilder::insert("123456").build();
+    let insert_b = OperationBuilder::insert("789").build();
     delta.ops.push(insert_a.clone());
     delta.ops.push(insert_b.clone());
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(4, 8)).ops(),
-        vec![OpBuilder::insert("56").build(), OpBuilder::insert("78").build()]
+        DeltaIterator::from_interval(&delta, Interval::new(4, 8)).ops(),
+        vec![
+            OperationBuilder::insert("56").build(),
+            OperationBuilder::insert("78").build()
+        ]
     );
 
     // assert_eq!(
@@ -155,54 +162,60 @@ fn delta_get_ops_in_interval_5() {
 #[test]
 fn delta_get_ops_in_interval_6() {
     let mut delta = RichTextDelta::default();
-    let insert_a = OpBuilder::insert("12345678").build();
+    let insert_a = OperationBuilder::insert("12345678").build();
     delta.add(insert_a.clone());
     assert_eq!(
-        DeltaIter::from_interval(&delta, Interval::new(4, 6)).ops(),
-        vec![OpBuilder::insert("56").build()]
+        DeltaIterator::from_interval(&delta, Interval::new(4, 6)).ops(),
+        vec![OperationBuilder::insert("56").build()]
     );
 }
 
 #[test]
 fn delta_get_ops_in_interval_7() {
     let mut delta = RichTextDelta::default();
-    let insert_a = OpBuilder::insert("12345").build();
-    let retain_a = OpBuilder::retain(3).build();
+    let insert_a = OperationBuilder::insert("12345").build();
+    let retain_a = OperationBuilder::retain(3).build();
 
     delta.add(insert_a.clone());
     delta.add(retain_a.clone());
 
-    let mut iter_1 = DeltaIter::from_offset(&delta, 2);
-    assert_eq!(iter_1.next_op().unwrap(), OpBuilder::insert("345").build());
-    assert_eq!(iter_1.next_op().unwrap(), OpBuilder::retain(3).build());
+    let mut iter_1 = DeltaIterator::from_offset(&delta, 2);
+    assert_eq!(iter_1.next_op().unwrap(), OperationBuilder::insert("345").build());
+    assert_eq!(iter_1.next_op().unwrap(), OperationBuilder::retain(3).build());
 
-    let mut iter_2 = DeltaIter::new(&delta);
-    assert_eq!(iter_2.next_op_with_len(2).unwrap(), OpBuilder::insert("12").build());
-    assert_eq!(iter_2.next_op().unwrap(), OpBuilder::insert("345").build());
+    let mut iter_2 = DeltaIterator::new(&delta);
+    assert_eq!(
+        iter_2.next_op_with_len(2).unwrap(),
+        OperationBuilder::insert("12").build()
+    );
+    assert_eq!(iter_2.next_op().unwrap(), OperationBuilder::insert("345").build());
 
-    assert_eq!(iter_2.next_op().unwrap(), OpBuilder::retain(3).build());
+    assert_eq!(iter_2.next_op().unwrap(), OperationBuilder::retain(3).build());
 }
 
 #[test]
 fn delta_op_seek() {
     let mut delta = RichTextDelta::default();
-    let insert_a = OpBuilder::insert("12345").build();
-    let retain_a = OpBuilder::retain(3).build();
+    let insert_a = OperationBuilder::insert("12345").build();
+    let retain_a = OperationBuilder::retain(3).build();
     delta.add(insert_a.clone());
     delta.add(retain_a.clone());
-    let mut iter = DeltaIter::new(&delta);
+    let mut iter = DeltaIterator::new(&delta);
     iter.seek::<OpMetric>(1);
-    assert_eq!(iter.next_op().unwrap(), OpBuilder::retain(3).build());
+    assert_eq!(iter.next_op().unwrap(), retain_a);
 }
 
 #[test]
 fn delta_utf16_code_unit_seek() {
     let mut delta = RichTextDelta::default();
-    delta.add(OpBuilder::insert("12345").build());
+    delta.add(OperationBuilder::insert("12345").build());
 
-    let mut iter = DeltaIter::new(&delta);
+    let mut iter = DeltaIterator::new(&delta);
     iter.seek::<Utf16CodeUnitMetric>(3);
-    assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("45").build());
+    assert_eq!(
+        iter.next_op_with_len(2).unwrap(),
+        OperationBuilder::insert("45").build()
+    );
 }
 
 #[test]
@@ -213,77 +226,92 @@ fn delta_utf16_code_unit_seek_with_attributes() {
         .add_attr(RichTextAttribute::Italic(true))
         .build();
 
-    delta.add(OpBuilder::insert("1234").attributes(attributes.clone()).build());
-    delta.add(OpBuilder::insert("\n").build());
+    delta.add(OperationBuilder::insert("1234").attributes(attributes.clone()).build());
+    delta.add(OperationBuilder::insert("\n").build());
 
-    let mut iter = DeltaIter::new(&delta);
+    let mut iter = DeltaIterator::new(&delta);
     iter.seek::<Utf16CodeUnitMetric>(0);
 
     assert_eq!(
         iter.next_op_with_len(4).unwrap(),
-        OpBuilder::insert("1234").attributes(attributes).build(),
+        OperationBuilder::insert("1234").attributes(attributes).build(),
     );
 }
 
 #[test]
 fn delta_next_op_len() {
     let mut delta = RichTextDelta::default();
-    delta.add(OpBuilder::insert("12345").build());
-    let mut iter = DeltaIter::new(&delta);
-    assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("12").build());
-    assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("34").build());
-    assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("5").build());
+    delta.add(OperationBuilder::insert("12345").build());
+    let mut iter = DeltaIterator::new(&delta);
+    assert_eq!(
+        iter.next_op_with_len(2).unwrap(),
+        OperationBuilder::insert("12").build()
+    );
+    assert_eq!(
+        iter.next_op_with_len(2).unwrap(),
+        OperationBuilder::insert("34").build()
+    );
+    assert_eq!(iter.next_op_with_len(2).unwrap(), OperationBuilder::insert("5").build());
     assert_eq!(iter.next_op_with_len(1), None);
 }
 
 #[test]
 fn delta_next_op_len_with_chinese() {
     let mut delta = RichTextDelta::default();
-    delta.add(OpBuilder::insert("你好").build());
+    delta.add(OperationBuilder::insert("你好").build());
 
-    let mut iter = DeltaIter::new(&delta);
+    let mut iter = DeltaIterator::new(&delta);
     assert_eq!(iter.next_op_len().unwrap(), 2);
-    assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("你好").build());
+    assert_eq!(
+        iter.next_op_with_len(2).unwrap(),
+        OperationBuilder::insert("你好").build()
+    );
 }
 
 #[test]
 fn delta_next_op_len_with_english() {
     let mut delta = RichTextDelta::default();
-    delta.add(OpBuilder::insert("ab").build());
-    let mut iter = DeltaIter::new(&delta);
+    delta.add(OperationBuilder::insert("ab").build());
+    let mut iter = DeltaIterator::new(&delta);
     assert_eq!(iter.next_op_len().unwrap(), 2);
-    assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::insert("ab").build());
+    assert_eq!(
+        iter.next_op_with_len(2).unwrap(),
+        OperationBuilder::insert("ab").build()
+    );
 }
 
 #[test]
 fn delta_next_op_len_after_seek() {
     let mut delta = RichTextDelta::default();
-    delta.add(OpBuilder::insert("12345").build());
-    let mut iter = DeltaIter::new(&delta);
+    delta.add(OperationBuilder::insert("12345").build());
+    let mut iter = DeltaIterator::new(&delta);
     assert_eq!(iter.next_op_len().unwrap(), 5);
     iter.seek::<Utf16CodeUnitMetric>(3);
     assert_eq!(iter.next_op_len().unwrap(), 2);
-    assert_eq!(iter.next_op_with_len(1).unwrap(), OpBuilder::insert("4").build());
+    assert_eq!(iter.next_op_with_len(1).unwrap(), OperationBuilder::insert("4").build());
     assert_eq!(iter.next_op_len().unwrap(), 1);
-    assert_eq!(iter.next_op().unwrap(), OpBuilder::insert("5").build());
+    assert_eq!(iter.next_op().unwrap(), OperationBuilder::insert("5").build());
 }
 
 #[test]
 fn delta_next_op_len_none() {
     let mut delta = RichTextDelta::default();
-    delta.add(OpBuilder::insert("12345").build());
-    let mut iter = DeltaIter::new(&delta);
+    delta.add(OperationBuilder::insert("12345").build());
+    let mut iter = DeltaIterator::new(&delta);
 
     assert_eq!(iter.next_op_len().unwrap(), 5);
-    assert_eq!(iter.next_op_with_len(5).unwrap(), OpBuilder::insert("12345").build());
+    assert_eq!(
+        iter.next_op_with_len(5).unwrap(),
+        OperationBuilder::insert("12345").build()
+    );
     assert_eq!(iter.next_op_len(), None);
 }
 
 #[test]
 fn delta_next_op_with_len_zero() {
     let mut delta = RichTextDelta::default();
-    delta.add(OpBuilder::insert("12345").build());
-    let mut iter = DeltaIter::new(&delta);
+    delta.add(OperationBuilder::insert("12345").build());
+    let mut iter = DeltaIterator::new(&delta);
     assert_eq!(iter.next_op_with_len(0), None,);
     assert_eq!(iter.next_op_len().unwrap(), 5);
 }
@@ -291,14 +319,14 @@ fn delta_next_op_with_len_zero() {
 #[test]
 fn delta_next_op_with_len_cross_op_return_last() {
     let mut delta = RichTextDelta::default();
-    delta.add(OpBuilder::insert("12345").build());
-    delta.add(OpBuilder::retain(1).build());
-    delta.add(OpBuilder::insert("678").build());
+    delta.add(OperationBuilder::insert("12345").build());
+    delta.add(OperationBuilder::retain(1).build());
+    delta.add(OperationBuilder::insert("678").build());
 
-    let mut iter = DeltaIter::new(&delta);
+    let mut iter = DeltaIterator::new(&delta);
     iter.seek::<Utf16CodeUnitMetric>(4);
     assert_eq!(iter.next_op_len().unwrap(), 1);
-    assert_eq!(iter.next_op_with_len(2).unwrap(), OpBuilder::retain(1).build());
+    assert_eq!(iter.next_op_with_len(2).unwrap(), OperationBuilder::retain(1).build());
 }
 
 #[test]
@@ -342,18 +370,17 @@ fn apply_1000() {
 }
 
 #[test]
-fn apply() {
-    let s = "hello world,".to_owned();
-    let mut delta_a = RichTextDelta::default();
-    delta_a.insert(&s, RichTextAttributes::default());
-
-    let mut delta_b = RichTextDelta::default();
-    delta_b.retain(s.len(), RichTextAttributes::default());
-    delta_b.insert("appflowy", RichTextAttributes::default());
+fn apply_test() {
+    let s = "hello";
+    let delta_a = PlainTextDeltaBuilder::new().insert(s).build();
+    let delta_b = PlainTextDeltaBuilder::new()
+        .retain(s.len())
+        .insert(", AppFlowy")
+        .build();
 
-    let after_a = delta_a.apply("").unwrap();
+    let after_a = delta_a.content_str().unwrap();
     let after_b = delta_b.apply(&after_a).unwrap();
-    assert_eq!("hello world,appflowy", &after_b);
+    assert_eq!("hello, AppFlowy", &after_b);
 }
 
 #[test]
@@ -384,6 +411,17 @@ fn invert() {
     }
 }
 
+#[test]
+fn invert_test() {
+    let s = "hello world";
+    let delta = PlainTextDeltaBuilder::new().insert(s).build();
+    let invert_delta = delta.invert_str("");
+    assert_eq!(delta.utf16_base_len, invert_delta.utf16_target_len);
+    assert_eq!(delta.utf16_target_len, invert_delta.utf16_base_len);
+
+    assert_eq!(invert_delta.apply(s).unwrap(), "")
+}
+
 #[test]
 fn empty_ops() {
     let mut delta = RichTextDelta::default();
@@ -415,23 +453,24 @@ fn ops_merging() {
     assert_eq!(delta.ops.len(), 0);
     delta.retain(2, RichTextAttributes::default());
     assert_eq!(delta.ops.len(), 1);
-    assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(2).build()));
+    assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(2).build()));
     delta.retain(3, RichTextAttributes::default());
     assert_eq!(delta.ops.len(), 1);
-    assert_eq!(delta.ops.last(), Some(&OpBuilder::retain(5).build()));
+    assert_eq!(delta.ops.last(), Some(&OperationBuilder::retain(5).build()));
     delta.insert("abc", RichTextAttributes::default());
     assert_eq!(delta.ops.len(), 2);
-    assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abc").build()));
+    assert_eq!(delta.ops.last(), Some(&OperationBuilder::insert("abc").build()));
     delta.insert("xyz", RichTextAttributes::default());
     assert_eq!(delta.ops.len(), 2);
-    assert_eq!(delta.ops.last(), Some(&OpBuilder::insert("abcxyz").build()));
+    assert_eq!(delta.ops.last(), Some(&OperationBuilder::insert("abcxyz").build()));
     delta.delete(1);
     assert_eq!(delta.ops.len(), 3);
-    assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(1).build()));
+    assert_eq!(delta.ops.last(), Some(&OperationBuilder::delete(1).build()));
     delta.delete(1);
     assert_eq!(delta.ops.len(), 3);
-    assert_eq!(delta.ops.last(), Some(&OpBuilder::delete(2).build()));
+    assert_eq!(delta.ops.last(), Some(&OperationBuilder::delete(2).build()));
 }
+
 #[test]
 fn is_noop() {
     let mut delta = RichTextDelta::default();
@@ -582,11 +621,11 @@ fn transform_two_conflict_non_seq_delta() {
 #[test]
 fn delta_invert_no_attribute_delta() {
     let mut delta = RichTextDelta::default();
-    delta.add(OpBuilder::insert("123").build());
+    delta.add(OperationBuilder::insert("123").build());
 
     let mut change = RichTextDelta::default();
-    change.add(OpBuilder::retain(3).build());
-    change.add(OpBuilder::insert("456").build());
+    change.add(OperationBuilder::retain(3).build());
+    change.add(OperationBuilder::insert("456").build());
     let undo = change.invert(&delta);
 
     let new_delta = delta.compose(&change).unwrap();

+ 7 - 7
frontend/rust-lib/flowy-text-block/tests/editor/serde_test.rs

@@ -11,7 +11,7 @@ fn operation_insert_serialize_test() {
         .add_attr(RichTextAttribute::Bold(true))
         .add_attr(RichTextAttribute::Italic(true))
         .build();
-    let operation = OpBuilder::insert("123").attributes(attributes).build();
+    let operation = OperationBuilder::insert("123").attributes(attributes).build();
     let json = serde_json::to_string(&operation).unwrap();
     eprintln!("{}", json);
 
@@ -42,7 +42,7 @@ fn attributes_serialize_test() {
         .add_attr(RichTextAttribute::Bold(true))
         .add_attr(RichTextAttribute::Italic(true))
         .build();
-    let retain = OpBuilder::insert("123").attributes(attributes).build();
+    let retain = OperationBuilder::insert("123").attributes(attributes).build();
 
     let json = serde_json::to_string(&retain).unwrap();
     eprintln!("{}", json);
@@ -56,7 +56,7 @@ fn delta_serialize_multi_attribute_test() {
         .add_attr(RichTextAttribute::Bold(true))
         .add_attr(RichTextAttribute::Italic(true))
         .build();
-    let retain = OpBuilder::insert("123").attributes(attributes).build();
+    let retain = OperationBuilder::insert("123").attributes(attributes).build();
 
     delta.add(retain);
     delta.add(Operation::Retain(5.into()));
@@ -65,7 +65,7 @@ fn delta_serialize_multi_attribute_test() {
     let json = serde_json::to_string(&delta).unwrap();
     eprintln!("{}", json);
 
-    let delta_from_json = Delta::from_delta_str(&json).unwrap();
+    let delta_from_json = Delta::from_json_str(&json).unwrap();
     assert_eq!(delta_from_json, delta);
 }
 
@@ -77,7 +77,7 @@ fn delta_deserialize_test() {
         {"retain":2,"attributes":{"italic":"true","bold":"true"}},
         {"retain":2,"attributes":{"italic":true,"bold":true}}
      ]"#;
-    let delta = RichTextDelta::from_delta_str(json).unwrap();
+    let delta = RichTextDelta::from_json_str(json).unwrap();
     eprintln!("{}", delta);
 }
 
@@ -86,13 +86,13 @@ fn delta_deserialize_null_test() {
     let json = r#"[
         {"retain":7,"attributes":{"bold":null}}
      ]"#;
-    let delta1 = RichTextDelta::from_delta_str(json).unwrap();
+    let delta1 = RichTextDelta::from_json_str(json).unwrap();
 
     let mut attribute = RichTextAttribute::Bold(true);
     attribute.value = RichTextAttributeValue(None);
     let delta2 = DeltaBuilder::new().retain_with_attributes(7, attribute.into()).build();
 
-    assert_eq!(delta2.to_delta_str(), r#"[{"retain":7,"attributes":{"bold":""}}]"#);
+    assert_eq!(delta2.to_json_str(), r#"[{"retain":7,"attributes":{"bold":""}}]"#);
     assert_eq!(delta1, delta2);
 }
 

+ 1 - 0
frontend/rust-lib/flowy-text-block/tests/editor/undo_redo_test.rs

@@ -108,6 +108,7 @@ fn history_bold_redo_with_lagging() {
 fn history_delete_undo() {
     let ops = vec![
         Insert(0, "123", 0),
+        Wait(RECORD_THRESHOLD),
         AssertDocJson(0, r#"[{"insert":"123"}]"#),
         Delete(0, Interval::new(0, 3)),
         AssertDocJson(0, r#"[]"#),

+ 7 - 4
frontend/scripts/install_dev_env/install_linux.sh

@@ -50,6 +50,13 @@ flutter doctor
 printMessage "Setting up githooks."
 git config core.hooksPath .githooks
 
+# Install go-gitlint 
+printMessage "Installing go-gitlint."
+GOLINT_FILENAME="go-gitlint_1.1.0_linux_x86_64.tar.gz"
+wget https://github.com/llorllale/go-gitlint/releases/download/1.1.0/${GOLINT_FILENAME}
+tar -zxv --directory .githooks/. -f ${GOLINT_FILENAME} gitlint 
+rm ${GOLINT_FILENAME}
+
 # Change to the frontend directory
 cd frontend
 
@@ -61,10 +68,6 @@ cargo install --force cargo-make
 printMessage "Installing duckscript."
 cargo install --force duckscript_cli
 
-# Install CommitLint
-printMessage "Installing CommitLint."
-npm install @commitlint/cli @commitlint/config-conventional --save-dev
-
 # Check prerequisites
 printMessage "Checking prerequisites."
 cargo make flowy_dev

+ 7 - 4
frontend/scripts/install_dev_env/install_macos.sh

@@ -50,6 +50,13 @@ flutter doctor
 printMessage "Setting up githooks."
 git config core.hooksPath .githooks
 
+# Install go-gitlint 
+printMessage "Installing go-gitlint."
+GOLINT_FILENAME="go-gitlint_1.1.0_osx_x86_64.tar.gz"
+wget https://github.com/llorllale/go-gitlint/releases/download/1.1.0/${GOLINT_FILENAME}
+tar -zxv --directory .githooks/. -f ${GOLINT_FILENAME} gitlint 
+rm ${GOLINT_FILENAME}
+
 # Change to the frontend directory
 cd frontend
 
@@ -61,10 +68,6 @@ cargo install --force cargo-make
 printMessage "Installing duckscript."
 cargo install --force duckscript_cli
 
-# Install CommitLint
-printMessagae "Installing CommitLint."
-npm install @commitlint/cli @commitlint/config-conventional --save-dev
-
 # Check prerequisites
 printMessage "Checking prerequisites."
 cargo make flowy_dev

+ 0 - 39
frontend/scripts/makefile/githooks.toml

@@ -1,39 +0,0 @@
-[tasks.install-commitlint.mac]
-script = [
-    """
-    brew install npm
-    npm install @commitlint/cli @commitlint/config-conventional --save-dev
-
-    git config core.hooksPath .githooks
-    """,
-]
-script_runner = "@shell"
-
-[tasks.install-commitlint.windows]
-script = [
-    """
-    echo "WIP"
-
-    git config core.hooksPath .githooks
-    """,
-]
-script_runner = "@duckscript"
-
-[tasks.install-commitlint.linux]
-script = [
-    """
-    if command -v apt &> /dev/null
-    then
-      echo "Installing node.js (sudo apt install nodejs)"
-      sudo apt install nodejs
-    else
-    echo "Installing node.js (sudo pacman -S nodejs)"
-      sudo pacman -S nodejs
-    fi
-
-    npm install @commitlint/cli @commitlint/config-conventional --save-dev
-
-    git config core.hooksPath .githooks
-    """,
-]
-script_runner = "@shell"

+ 0 - 6
package.json

@@ -1,6 +0,0 @@
-{
-  "devDependencies": {
-    "@commitlint/cli": "^16.1.0",
-    "@commitlint/config-conventional": "^16.0.0"
-  }
-}

+ 3 - 3
shared-lib/flowy-sync/src/client_document/default/mod.rs

@@ -7,13 +7,13 @@ pub fn initial_quill_delta() -> RichTextDelta {
 
 #[inline]
 pub fn initial_quill_delta_string() -> String {
-    initial_quill_delta().to_delta_str()
+    initial_quill_delta().to_json_str()
 }
 
 #[inline]
 pub fn initial_read_me() -> RichTextDelta {
     let json = include_str!("READ_ME.json");
-    RichTextDelta::from_delta_str(json).unwrap()
+    RichTextDelta::from_json_str(json).unwrap()
 }
 
 #[cfg(test)]
@@ -22,6 +22,6 @@ mod tests {
 
     #[test]
     fn load_read_me() {
-        println!("{}", initial_read_me().to_delta_str());
+        println!("{}", initial_read_me().to_json_str());
     }
 }

+ 5 - 5
shared-lib/flowy-sync/src/client_document/document_pad.rs

@@ -55,16 +55,16 @@ impl ClientDocument {
     }
 
     pub fn from_json(json: &str) -> Result<Self, CollaborateError> {
-        let delta = RichTextDelta::from_delta_str(json)?;
+        let delta = RichTextDelta::from_json_str(json)?;
         Ok(Self::from_delta(delta))
     }
 
     pub fn delta_str(&self) -> String {
-        self.delta.to_delta_str()
+        self.delta.to_json_str()
     }
 
     pub fn to_bytes(&self) -> Bytes {
-        self.delta.to_delta_bytes()
+        self.delta.to_json_bytes()
     }
 
     pub fn to_plain_string(&self) -> String {
@@ -85,7 +85,7 @@ impl ClientDocument {
     }
 
     pub fn set_delta(&mut self, data: RichTextDelta) {
-        tracing::trace!("document: {}", data.to_delta_str());
+        tracing::trace!("document: {}", data.to_json_str());
         self.delta = data;
 
         match &self.notify {
@@ -97,7 +97,7 @@ impl ClientDocument {
     }
 
     pub fn compose_delta(&mut self, delta: RichTextDelta) -> Result<(), CollaborateError> {
-        tracing::trace!("{} compose {}", &self.delta.to_delta_str(), delta.to_delta_str());
+        tracing::trace!("{} compose {}", &self.delta.to_json_str(), delta.to_json_str());
         let composed_delta = self.delta.compose(&delta)?;
         let mut undo_delta = delta.invert(&self.delta);
 

+ 2 - 2
shared-lib/flowy-sync/src/client_document/extensions/delete/preserve_line_format_merge.rs

@@ -1,6 +1,6 @@
 use crate::{client_document::DeleteExt, util::is_newline};
 use lib_ot::{
-    core::{Attributes, DeltaBuilder, DeltaIter, Interval, Utf16CodeUnitMetric, NEW_LINE},
+    core::{Attributes, DeltaBuilder, DeltaIterator, Interval, Utf16CodeUnitMetric, NEW_LINE},
     rich_text::{plain_attributes, RichTextDelta},
 };
 
@@ -16,7 +16,7 @@ impl DeleteExt for PreserveLineFormatOnMerge {
         }
 
         // seek to the  interval start pos. e.g. You backspace enter pos
-        let mut iter = DeltaIter::from_offset(delta, interval.start);
+        let mut iter = DeltaIterator::from_offset(delta, interval.start);
 
         // op will be the "\n"
         let newline_op = iter.next_op_with_len(1)?;

+ 2 - 2
shared-lib/flowy-sync/src/client_document/extensions/format/resolve_block_format.rs

@@ -1,5 +1,5 @@
 use lib_ot::{
-    core::{DeltaBuilder, DeltaIter, Interval},
+    core::{DeltaBuilder, DeltaIterator, Interval},
     rich_text::{plain_attributes, AttributeScope, RichTextAttribute, RichTextDelta},
 };
 
@@ -20,7 +20,7 @@ impl FormatExt for ResolveBlockFormat {
         }
 
         let mut new_delta = DeltaBuilder::new().retain(interval.start).build();
-        let mut iter = DeltaIter::from_offset(delta, interval.start);
+        let mut iter = DeltaIterator::from_offset(delta, interval.start);
         let mut start = 0;
         let end = interval.size();
         while start < end && iter.has_next() {

+ 2 - 2
shared-lib/flowy-sync/src/client_document/extensions/format/resolve_inline_format.rs

@@ -1,5 +1,5 @@
 use lib_ot::{
-    core::{DeltaBuilder, DeltaIter, Interval},
+    core::{DeltaBuilder, DeltaIterator, Interval},
     rich_text::{AttributeScope, RichTextAttribute, RichTextDelta},
 };
 
@@ -19,7 +19,7 @@ impl FormatExt for ResolveInlineFormat {
             return None;
         }
         let mut new_delta = DeltaBuilder::new().retain(interval.start).build();
-        let mut iter = DeltaIter::from_offset(delta, interval.start);
+        let mut iter = DeltaIterator::from_offset(delta, interval.start);
         let mut start = 0;
         let end = interval.size();
 

+ 3 - 5
shared-lib/flowy-sync/src/client_document/extensions/insert/auto_exit_block.rs

@@ -1,8 +1,6 @@
 use crate::{client_document::InsertExt, util::is_newline};
-use lib_ot::{
-    core::{is_empty_line_at_index, DeltaBuilder, DeltaIter},
-    rich_text::{attributes_except_header, RichTextAttributeKey, RichTextDelta},
-};
+use lib_ot::core::{is_empty_line_at_index, DeltaBuilder, DeltaIterator};
+use lib_ot::rich_text::{attributes_except_header, RichTextAttributeKey, RichTextDelta};
 
 pub struct AutoExitBlock {}
 
@@ -21,7 +19,7 @@ impl InsertExt for AutoExitBlock {
             return None;
         }
 
-        let mut iter = DeltaIter::from_offset(delta, index);
+        let mut iter = DeltaIterator::from_offset(delta, index);
         let next = iter.next_op()?;
         let mut attributes = next.get_attributes();
 

+ 2 - 2
shared-lib/flowy-sync/src/client_document/extensions/insert/auto_format.rs

@@ -1,6 +1,6 @@
 use crate::{client_document::InsertExt, util::is_whitespace};
 use lib_ot::{
-    core::{count_utf16_code_units, DeltaBuilder, DeltaIter},
+    core::{count_utf16_code_units, DeltaBuilder, DeltaIterator},
     rich_text::{plain_attributes, RichTextAttribute, RichTextAttributes, RichTextDelta},
 };
 use std::cmp::min;
@@ -17,7 +17,7 @@ impl InsertExt for AutoFormatExt {
         if !is_whitespace(text) {
             return None;
         }
-        let mut iter = DeltaIter::new(delta);
+        let mut iter = DeltaIterator::new(delta);
         if let Some(prev) = iter.next_op_with_len(index) {
             match AutoFormat::parse(prev.get_data()) {
                 None => {}

+ 2 - 2
shared-lib/flowy-sync/src/client_document/extensions/insert/default_insert.rs

@@ -1,6 +1,6 @@
 use crate::client_document::InsertExt;
 use lib_ot::{
-    core::{Attributes, DeltaBuilder, DeltaIter, NEW_LINE},
+    core::{Attributes, DeltaBuilder, DeltaIterator, NEW_LINE},
     rich_text::{RichTextAttributeKey, RichTextAttributes, RichTextDelta},
 };
 
@@ -11,7 +11,7 @@ impl InsertExt for DefaultInsertAttribute {
     }
 
     fn apply(&self, delta: &RichTextDelta, replace_len: usize, text: &str, index: usize) -> Option<RichTextDelta> {
-        let iter = DeltaIter::new(delta);
+        let iter = DeltaIterator::new(delta);
         let mut attributes = RichTextAttributes::new();
 
         // Enable each line split by "\n" remains the block attributes. for example:

+ 2 - 2
shared-lib/flowy-sync/src/client_document/extensions/insert/preserve_block_format.rs

@@ -1,6 +1,6 @@
 use crate::{client_document::InsertExt, util::is_newline};
 use lib_ot::{
-    core::{DeltaBuilder, DeltaIter, NEW_LINE},
+    core::{DeltaBuilder, DeltaIterator, NEW_LINE},
     rich_text::{
         attributes_except_header, plain_attributes, RichTextAttribute, RichTextAttributeKey, RichTextAttributes,
         RichTextDelta,
@@ -18,7 +18,7 @@ impl InsertExt for PreserveBlockFormatOnInsert {
             return None;
         }
 
-        let mut iter = DeltaIter::from_offset(delta, index);
+        let mut iter = DeltaIterator::from_offset(delta, index);
         match iter.next_op_with_newline() {
             None => {}
             Some((newline_op, offset)) => {

+ 3 - 3
shared-lib/flowy-sync/src/client_document/extensions/insert/preserve_inline_format.rs

@@ -3,7 +3,7 @@ use crate::{
     util::{contain_newline, is_newline},
 };
 use lib_ot::{
-    core::{DeltaBuilder, DeltaIter, OpNewline, NEW_LINE},
+    core::{DeltaBuilder, DeltaIterator, OpNewline, NEW_LINE},
     rich_text::{plain_attributes, RichTextAttributeKey, RichTextDelta},
 };
 
@@ -18,7 +18,7 @@ impl InsertExt for PreserveInlineFormat {
             return None;
         }
 
-        let mut iter = DeltaIter::new(delta);
+        let mut iter = DeltaIterator::new(delta);
         let prev = iter.next_op_with_len(index)?;
         if OpNewline::parse(&prev).is_contain() {
             return None;
@@ -64,7 +64,7 @@ impl InsertExt for PreserveLineFormatOnSplit {
             return None;
         }
 
-        let mut iter = DeltaIter::new(delta);
+        let mut iter = DeltaIterator::new(delta);
         let prev = iter.next_op_with_len(index)?;
         if OpNewline::parse(&prev).is_end() {
             return None;

+ 2 - 2
shared-lib/flowy-sync/src/client_document/extensions/insert/reset_format_on_new_line.rs

@@ -1,6 +1,6 @@
 use crate::{client_document::InsertExt, util::is_newline};
 use lib_ot::{
-    core::{DeltaBuilder, DeltaIter, Utf16CodeUnitMetric, NEW_LINE},
+    core::{DeltaBuilder, DeltaIterator, Utf16CodeUnitMetric, NEW_LINE},
     rich_text::{RichTextAttributeKey, RichTextAttributes, RichTextDelta},
 };
 
@@ -15,7 +15,7 @@ impl InsertExt for ResetLineFormatOnNewLine {
             return None;
         }
 
-        let mut iter = DeltaIter::new(delta);
+        let mut iter = DeltaIterator::new(delta);
         iter.seek::<Utf16CodeUnitMetric>(index);
         let next_op = iter.next_op()?;
         if !next_op.get_data().starts_with(NEW_LINE) {

+ 5 - 5
shared-lib/flowy-sync/src/client_folder/builder.rs

@@ -7,7 +7,7 @@ use crate::{
 };
 
 use flowy_folder_data_model::revision::{TrashRevision, WorkspaceRevision};
-use lib_ot::core::{PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
+use lib_ot::core::{PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder};
 use serde::{Deserialize, Serialize};
 use std::sync::Arc;
 
@@ -41,9 +41,9 @@ impl FolderPadBuilder {
         }
 
         // TODO: Reconvert from history if delta.to_str() failed.
-        let folder_json = delta.to_str()?;
-        let mut folder: FolderPad = serde_json::from_str(&folder_json).map_err(|e| {
-            tracing::error!("Deserialize folder from json failed: {}", folder_json);
+        let content = delta.content_str()?;
+        let mut folder: FolderPad = serde_json::from_str(&content).map_err(|e| {
+            tracing::error!("Deserialize folder from {} failed", content);
             return CollaborateError::internal().context(format!("Deserialize delta to folder failed: {}", e));
         })?;
         folder.delta = delta;
@@ -51,7 +51,7 @@ impl FolderPadBuilder {
     }
 
     pub(crate) fn build_with_revisions(self, revisions: Vec<Revision>) -> CollaborateResult<FolderPad> {
-        let folder_delta: FolderDelta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
+        let folder_delta: FolderDelta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
         self.build_with_delta(folder_delta)
     }
 

+ 3 - 3
shared-lib/flowy-sync/src/client_folder/folder_pad.rs

@@ -295,7 +295,7 @@ impl FolderPad {
     }
 
     pub fn md5(&self) -> String {
-        md5(&self.delta.to_delta_bytes())
+        md5(&self.delta.to_json_bytes())
     }
 
     pub fn to_json(&self) -> CollaborateResult<String> {
@@ -315,7 +315,7 @@ impl FolderPad {
             Some(_) => {
                 let old = cloned_self.to_json()?;
                 let new = self.to_json()?;
-                match cal_diff::<PlainTextAttributes>(old, new) {
+                match cal_diff::<PhantomAttributes>(old, new) {
                     None => Ok(None),
                     Some(delta) => {
                         self.delta = self.delta.compose(&delta)?;
@@ -350,7 +350,7 @@ impl FolderPad {
             Some(_) => {
                 let old = cloned_self.to_json()?;
                 let new = self.to_json()?;
-                match cal_diff::<PlainTextAttributes>(old, new) {
+                match cal_diff::<PhantomAttributes>(old, new) {
                     None => Ok(None),
                     Some(delta) => {
                         self.delta = self.delta.compose(&delta)?;

+ 15 - 15
shared-lib/flowy-sync/src/client_grid/grid_block_revsion_pad.rs

@@ -4,7 +4,7 @@ use crate::util::{cal_diff, make_delta_from_revisions};
 use flowy_grid_data_model::revision::{
     gen_block_id, gen_row_id, CellRevision, GridBlockRevision, RowMetaChangeset, RowRevision,
 };
-use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
+use lib_ot::core::{OperationTransformable, PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder};
 use std::borrow::Cow;
 use std::collections::HashMap;
 use std::sync::Arc;
@@ -46,7 +46,7 @@ impl GridBlockRevisionPad {
     }
 
     pub fn from_delta(delta: GridBlockRevisionDelta) -> CollaborateResult<Self> {
-        let s = delta.to_str()?;
+        let s = delta.content_str()?;
         let block_revision: GridBlockRevision = serde_json::from_str(&s).map_err(|e| {
             let msg = format!("Deserialize delta to block meta failed: {}", e);
             tracing::error!("{}", s);
@@ -56,7 +56,7 @@ impl GridBlockRevisionPad {
     }
 
     pub fn from_revisions(_grid_id: &str, revisions: Vec<Revision>) -> CollaborateResult<Self> {
-        let block_delta: GridBlockRevisionDelta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
+        let block_delta: GridBlockRevisionDelta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
         Self::from_delta(block_delta)
     }
 
@@ -195,10 +195,10 @@ impl GridBlockRevisionPad {
             Some(_) => {
                 let old = cloned_self.to_json()?;
                 let new = self.to_json()?;
-                match cal_diff::<PlainTextAttributes>(old, new) {
+                match cal_diff::<PhantomAttributes>(old, new) {
                     None => Ok(None),
                     Some(delta) => {
-                        tracing::trace!("[GridBlockMeta] Composing delta {}", delta.to_delta_str());
+                        tracing::trace!("[GridBlockMeta] Composing delta {}", delta.to_json_str());
                         // tracing::debug!(
                         //     "[GridBlockMeta] current delta: {}",
                         //     self.delta.to_str().unwrap_or_else(|_| "".to_string())
@@ -231,11 +231,11 @@ impl GridBlockRevisionPad {
     }
 
     pub fn md5(&self) -> String {
-        md5(&self.delta.to_delta_bytes())
+        md5(&self.delta.to_json_bytes())
     }
 
     pub fn delta_str(&self) -> String {
-        self.delta.to_delta_str()
+        self.delta.to_json_str()
     }
 }
 
@@ -252,7 +252,7 @@ pub fn make_grid_block_delta(block_rev: &GridBlockRevision) -> GridBlockRevision
 
 pub fn make_grid_block_revisions(user_id: &str, grid_block_meta_data: &GridBlockRevision) -> RepeatedRevision {
     let delta = make_grid_block_delta(grid_block_meta_data);
-    let bytes = delta.to_delta_bytes();
+    let bytes = delta.to_json_bytes();
     let revision = Revision::initial_revision(user_id, &grid_block_meta_data.block_id, bytes);
     revision.into()
 }
@@ -289,7 +289,7 @@ mod tests {
         let change = pad.add_row_rev(row.clone(), None).unwrap().unwrap();
         assert_eq!(pad.rows.first().unwrap().as_ref(), &row);
         assert_eq!(
-            change.delta.to_delta_str(),
+            change.delta.to_json_str(),
             r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
         );
     }
@@ -303,19 +303,19 @@ mod tests {
 
         let change = pad.add_row_rev(row_1.clone(), None).unwrap().unwrap();
         assert_eq!(
-            change.delta.to_delta_str(),
+            change.delta.to_json_str(),
             r#"[{"retain":24},{"insert":"{\"id\":\"1\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
         );
 
         let change = pad.add_row_rev(row_2.clone(), None).unwrap().unwrap();
         assert_eq!(
-            change.delta.to_delta_str(),
+            change.delta.to_json_str(),
             r#"[{"retain":90},{"insert":",{\"id\":\"2\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
         );
 
         let change = pad.add_row_rev(row_3.clone(), Some("2".to_string())).unwrap().unwrap();
         assert_eq!(
-            change.delta.to_delta_str(),
+            change.delta.to_json_str(),
             r#"[{"retain":157},{"insert":",{\"id\":\"3\",\"block_id\":\"1\",\"cells\":[],\"height\":0,\"visibility\":false}"},{"retain":2}]"#
         );
 
@@ -381,7 +381,7 @@ mod tests {
         let _ = pad.add_row_rev(row.clone(), None).unwrap().unwrap();
         let change = pad.delete_rows(vec![Cow::Borrowed(&row.id)]).unwrap().unwrap();
         assert_eq!(
-            change.delta.to_delta_str(),
+            change.delta.to_json_str(),
             r#"[{"retain":24},{"delete":66},{"retain":2}]"#
         );
 
@@ -410,7 +410,7 @@ mod tests {
         let change = pad.update_row(changeset).unwrap().unwrap();
 
         assert_eq!(
-            change.delta.to_delta_str(),
+            change.delta.to_json_str(),
             r#"[{"retain":69},{"insert":"10"},{"retain":15},{"insert":"tru"},{"delete":4},{"retain":4}]"#
         );
 
@@ -422,7 +422,7 @@ mod tests {
 
     fn test_pad() -> GridBlockRevisionPad {
         let delta =
-            GridBlockRevisionDelta::from_delta_str(r#"[{"insert":"{\"block_id\":\"1\",\"rows\":[]}"}]"#).unwrap();
+            GridBlockRevisionDelta::from_json_str(r#"[{"insert":"{\"block_id\":\"1\",\"rows\":[]}"}]"#).unwrap();
         GridBlockRevisionPad::from_delta(delta).unwrap()
     }
 }

+ 9 - 9
shared-lib/flowy-sync/src/client_grid/grid_revision_pad.rs

@@ -9,7 +9,7 @@ use flowy_grid_data_model::revision::{
     GridLayoutRevision, GridRevision, GridSettingRevision, GridSortRevision,
 };
 use lib_infra::util::move_vec_element;
-use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta, PlainTextDeltaBuilder};
+use lib_ot::core::{OperationTransformable, PhantomAttributes, PlainTextDelta, PlainTextDeltaBuilder};
 use std::collections::HashMap;
 use std::sync::Arc;
 
@@ -52,8 +52,8 @@ impl GridRevisionPad {
     }
 
     pub fn from_delta(delta: GridRevisionDelta) -> CollaborateResult<Self> {
-        let s = delta.to_str()?;
-        let grid: GridRevision = serde_json::from_str(&s)
+        let content = delta.content_str()?;
+        let grid: GridRevision = serde_json::from_str(&content)
             .map_err(|e| CollaborateError::internal().context(format!("Deserialize delta to grid failed: {}", e)))?;
 
         Ok(Self {
@@ -63,7 +63,7 @@ impl GridRevisionPad {
     }
 
     pub fn from_revisions(revisions: Vec<Revision>) -> CollaborateResult<Self> {
-        let grid_delta: GridRevisionDelta = make_delta_from_revisions::<PlainTextAttributes>(revisions)?;
+        let grid_delta: GridRevisionDelta = make_delta_from_revisions::<PhantomAttributes>(revisions)?;
         Self::from_delta(grid_delta)
     }
 
@@ -457,15 +457,15 @@ impl GridRevisionPad {
     }
 
     pub fn md5(&self) -> String {
-        md5(&self.delta.to_delta_bytes())
+        md5(&self.delta.to_json_bytes())
     }
 
     pub fn delta_str(&self) -> String {
-        self.delta.to_delta_str()
+        self.delta.to_json_str()
     }
 
     pub fn delta_bytes(&self) -> Bytes {
-        self.delta.to_delta_bytes()
+        self.delta.to_json_bytes()
     }
 
     pub fn fields(&self) -> &[Arc<FieldRevision>] {
@@ -482,7 +482,7 @@ impl GridRevisionPad {
             Some(_) => {
                 let old = make_grid_rev_json_str(&cloned_grid)?;
                 let new = self.json_str()?;
-                match cal_diff::<PlainTextAttributes>(old, new) {
+                match cal_diff::<PhantomAttributes>(old, new) {
                     None => Ok(None),
                     Some(delta) => {
                         self.delta = self.delta.compose(&delta)?;
@@ -553,7 +553,7 @@ pub fn make_grid_delta(grid_rev: &GridRevision) -> GridRevisionDelta {
 
 pub fn make_grid_revisions(user_id: &str, grid_rev: &GridRevision) -> RepeatedRevision {
     let delta = make_grid_delta(grid_rev);
-    let bytes = delta.to_delta_bytes();
+    let bytes = delta.to_json_bytes();
     let revision = Revision::initial_revision(user_id, &grid_rev.grid_id, bytes);
     revision.into()
 }

+ 1 - 1
shared-lib/flowy-sync/src/entities/revision.rs

@@ -89,7 +89,7 @@ impl std::fmt::Debug for Revision {
         let _ = f.write_fmt(format_args!("rev_id {}, ", self.rev_id))?;
         match RichTextDelta::from_bytes(&self.delta_data) {
             Ok(delta) => {
-                let _ = f.write_fmt(format_args!("delta {:?}", delta.to_delta_str()))?;
+                let _ = f.write_fmt(format_args!("delta {:?}", delta.to_json_str()))?;
             }
             Err(e) => {
                 let _ = f.write_fmt(format_args!("delta {:?}", e))?;

+ 1 - 1
shared-lib/flowy-sync/src/entities/text_block.rs

@@ -46,7 +46,7 @@ impl std::convert::TryFrom<Revision> for DocumentPB {
         }
 
         let delta = RichTextDelta::from_bytes(&revision.delta_data)?;
-        let doc_json = delta.to_delta_str();
+        let doc_json = delta.to_json_str();
 
         Ok(DocumentPB {
             block_id: revision.object_id,

+ 1 - 1
shared-lib/flowy-sync/src/server_document/document_pad.rs

@@ -39,7 +39,7 @@ impl RevisionSyncObject<RichTextAttributes> for ServerDocument {
     }
 
     fn to_json(&self) -> String {
-        self.delta.to_delta_str()
+        self.delta.to_json_str()
     }
 
     fn set_delta(&mut self, new_delta: Delta<RichTextAttributes>) {

+ 2 - 2
shared-lib/flowy-sync/src/server_folder/folder_manager.rs

@@ -13,7 +13,7 @@ use crate::{
 use async_stream::stream;
 use futures::stream::StreamExt;
 use lib_infra::future::BoxResultFuture;
-use lib_ot::core::PlainTextAttributes;
+use lib_ot::core::PhantomAttributes;
 use std::{collections::HashMap, fmt::Debug, sync::Arc};
 use tokio::{
     sync::{mpsc, oneshot, RwLock},
@@ -188,7 +188,7 @@ impl ServerFolderManager {
     }
 }
 
-type FolderRevisionSynchronizer = RevisionSynchronizer<PlainTextAttributes>;
+type FolderRevisionSynchronizer = RevisionSynchronizer<PhantomAttributes>;
 
 struct OpenFolderHandler {
     folder_id: String,

+ 3 - 3
shared-lib/flowy-sync/src/server_folder/folder_pad.rs

@@ -1,5 +1,5 @@
 use crate::{entities::folder::FolderDelta, errors::CollaborateError, synchronizer::RevisionSyncObject};
-use lib_ot::core::{OperationTransformable, PlainTextAttributes, PlainTextDelta};
+use lib_ot::core::{OperationTransformable, PhantomAttributes, PlainTextDelta};
 
 pub struct ServerFolder {
     folder_id: String,
@@ -15,7 +15,7 @@ impl ServerFolder {
     }
 }
 
-impl RevisionSyncObject<PlainTextAttributes> for ServerFolder {
+impl RevisionSyncObject<PhantomAttributes> for ServerFolder {
     fn id(&self) -> &str {
         &self.folder_id
     }
@@ -32,7 +32,7 @@ impl RevisionSyncObject<PlainTextAttributes> for ServerFolder {
     }
 
     fn to_json(&self) -> String {
-        self.delta.to_delta_str()
+        self.delta.to_json_str()
     }
 
     fn set_delta(&mut self, new_delta: PlainTextDelta) {

+ 2 - 2
shared-lib/flowy-sync/src/util.rs

@@ -149,7 +149,7 @@ pub fn make_folder_from_revisions_pb(
         folder_delta = folder_delta.compose(&delta)?;
     }
 
-    let text = folder_delta.to_delta_str();
+    let text = folder_delta.to_json_str();
     Ok(Some(FolderInfo {
         folder_id: folder_id.to_string(),
         text,
@@ -183,7 +183,7 @@ pub fn make_document_from_revision_pbs(
         delta = delta.compose(&new_delta)?;
     }
 
-    let text = delta.to_delta_str();
+    let text = delta.to_json_str();
 
     Ok(Some(DocumentPB {
         block_id: doc_id.to_owned(),

+ 13 - 2
shared-lib/lib-ot/src/core/delta/builder.rs

@@ -1,7 +1,12 @@
-use crate::core::{trim, Attributes, Delta, PlainTextAttributes};
+use crate::core::delta::{trim, Delta};
+use crate::core::operation::{Attributes, PhantomAttributes};
 
-pub type PlainTextDeltaBuilder = DeltaBuilder<PlainTextAttributes>;
+pub type PlainTextDeltaBuilder = DeltaBuilder<PhantomAttributes>;
 
+/// A builder for creating new [Delta] objects.
+///
+/// Note that all edit operations must be sorted; the start point of each
+/// interval must be no less than the end point of the previous one.
 pub struct DeltaBuilder<T: Attributes> {
     delta: Delta<T>,
 }
@@ -23,6 +28,8 @@ where
         DeltaBuilder::default()
     }
 
+    /// Retain the 'n' characters with the attributes. Use 'retain' instead if you don't
+    /// need any attributes.
     pub fn retain_with_attributes(mut self, n: usize, attrs: T) -> Self {
         self.delta.retain(n, attrs);
         self
@@ -33,11 +40,14 @@ where
         self
     }
 
+    /// Deletes the given interval. Panics if interval is not properly sorted.
     pub fn delete(mut self, n: usize) -> Self {
         self.delta.delete(n);
         self
     }
 
+    /// Inserts the string with attributes. Use 'insert' instead if you don't
+    /// need any attributes.
     pub fn insert_with_attributes(mut self, s: &str, attrs: T) -> Self {
         self.delta.insert(s, attrs);
         self
@@ -53,6 +63,7 @@ where
         self
     }
 
+    /// Builds the `Delta`
     pub fn build(self) -> Delta<T> {
         self.delta
     }

+ 77 - 44
shared-lib/lib-ot/src/core/delta/cursor.rs

@@ -1,33 +1,51 @@
 #![allow(clippy::while_let_on_iterator)]
-use crate::{
-    core::{Attributes, Delta, Interval, Operation},
-    errors::{ErrorBuilder, OTError, OTErrorCode},
-};
+use crate::core::delta::Delta;
+use crate::core::interval::Interval;
+use crate::core::operation::{Attributes, Operation};
+use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
 use std::{cmp::min, iter::Enumerate, slice::Iter};
 
+/// A [DeltaCursor] is used to iterate the delta and return the corresponding delta.
 #[derive(Debug)]
-pub struct OpCursor<'a, T: Attributes> {
+pub struct DeltaCursor<'a, T: Attributes> {
     pub(crate) delta: &'a Delta<T>,
     pub(crate) origin_iv: Interval,
     pub(crate) consume_iv: Interval,
     pub(crate) consume_count: usize,
-    pub(crate) op_index: usize,
+    pub(crate) op_offset: usize,
     iter: Enumerate<Iter<'a, Operation<T>>>,
     next_op: Option<Operation<T>>,
 }
 
-impl<'a, T> OpCursor<'a, T>
+impl<'a, T> DeltaCursor<'a, T>
 where
     T: Attributes,
 {
-    pub fn new(delta: &'a Delta<T>, interval: Interval) -> OpCursor<'a, T> {
+    /// # Arguments
+    ///
+    /// * `delta`: The delta you want to iterate over.
+    /// * `interval`: The range for the cursor movement.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use lib_ot::core::{DeltaIterator, Interval, OperationBuilder};
+    /// use lib_ot::rich_text::RichTextDelta;
+    /// let mut delta = RichTextDelta::default();   
+    /// let op_1 = OperationBuilder::insert("123").build();    
+    /// let op_2 = OperationBuilder::insert("4").build();
+    /// delta.add(op_1.clone());
+    /// delta.add(op_2.clone());
+    /// assert_eq!(DeltaIterator::from_interval(&delta, Interval::new(0, 3)).ops(), vec![op_1.clone()]);
+    /// ```
+    pub fn new(delta: &'a Delta<T>, interval: Interval) -> DeltaCursor<'a, T> {
         // debug_assert!(interval.start <= delta.target_len);
         let mut cursor = Self {
             delta,
             origin_iv: interval,
             consume_iv: interval,
             consume_count: 0,
-            op_index: 0,
+            op_offset: 0,
             iter: delta.ops.iter().enumerate(),
             next_op: None,
         };
@@ -35,17 +53,37 @@ where
         cursor
     }
 
-    // get the next operation interval
+    /// Returns the next operation interval
     pub fn next_iv(&self) -> Interval {
         self.next_iv_with_len(None).unwrap_or_else(|| Interval::new(0, 0))
     }
 
-    pub fn next_op(&mut self) -> Option<Operation<T>> {
+    /// Returns the next operation
+    pub fn get_next_op(&mut self) -> Option<Operation<T>> {
         self.next_with_len(None)
     }
 
-    // get the last operation before the end.
-    // checkout the delta_next_op_with_len_cross_op_return_last test for more detail
+    /// Returns the reference of the next operation
+    pub fn next_op(&self) -> Option<&Operation<T>> {
+        let mut next_op = self.next_op.as_ref();
+        if next_op.is_none() {
+            let mut offset = 0;
+            for op in &self.delta.ops {
+                offset += op.len();
+                if offset > self.consume_count {
+                    next_op = Some(op);
+                    break;
+                }
+            }
+        }
+        next_op
+    }
+
+    /// # Arguments
+    ///
+    /// * `expected_len`: Return the next operation with the specified length.
+    ///
+    ///
     pub fn next_with_len(&mut self, expected_len: Option<usize>) -> Option<Operation<T>> {
         let mut find_op = None;
         let holder = self.next_op.clone();
@@ -97,17 +135,24 @@ where
     }
 
     pub fn has_next(&self) -> bool {
-        self.next_iter_op().is_some()
+        self.next_op().is_some()
     }
 
-    fn descend(&mut self, index: usize) {
-        self.consume_iv.start += index;
+    /// Finds the op within the current offset.
+    /// This function sets the start of the consume_iv to the offset, updates the consume_count
+    /// and the next_op reference.
+    ///
+    /// # Arguments
+    ///
+    /// * `offset`: Represents the offset of the delta string, in Utf16CodeUnit unit.
+    fn descend(&mut self, offset: usize) {
+        self.consume_iv.start += offset;
 
         if self.consume_count >= self.consume_iv.start {
             return;
         }
         while let Some((o_index, op)) = self.iter.next() {
-            self.op_index = o_index;
+            self.op_offset = o_index;
             let start = self.consume_count;
             let end = start + op.len();
             let intersect = Interval::new(start, end).intersect(self.consume_iv);
@@ -121,7 +166,7 @@ where
     }
 
     fn next_iv_with_len(&self, expected_len: Option<usize>) -> Option<Interval> {
-        let op = self.next_iter_op()?;
+        let op = self.next_op()?;
         let start = self.consume_count;
         let end = match expected_len {
             None => self.consume_count + op.len(),
@@ -132,31 +177,16 @@ where
         let interval = intersect.translate_neg(start);
         Some(interval)
     }
-
-    pub fn next_iter_op(&self) -> Option<&Operation<T>> {
-        let mut next_op = self.next_op.as_ref();
-        if next_op.is_none() {
-            let mut offset = 0;
-            for op in &self.delta.ops {
-                offset += op.len();
-                if offset > self.consume_count {
-                    next_op = Some(op);
-                    break;
-                }
-            }
-        }
-        next_op
-    }
 }
 
-fn find_next<'a, T>(cursor: &mut OpCursor<'a, T>) -> Option<&'a Operation<T>>
+fn find_next<'a, T>(cursor: &mut DeltaCursor<'a, T>) -> Option<&'a Operation<T>>
 where
     T: Attributes,
 {
     match cursor.iter.next() {
         None => None,
         Some((o_index, op)) => {
-            cursor.op_index = o_index;
+            cursor.op_offset = o_index;
             Some(op)
         }
     }
@@ -164,31 +194,34 @@ where
 
 type SeekResult = Result<(), OTError>;
 pub trait Metric {
-    fn seek<T: Attributes>(cursor: &mut OpCursor<T>, offset: usize) -> SeekResult;
+    fn seek<T: Attributes>(cursor: &mut DeltaCursor<T>, offset: usize) -> SeekResult;
 }
 
+/// [OpMetric] is used by [DeltaIterator] for seeking operations
+/// The unit of the movement is Operation
 pub struct OpMetric();
 
 impl Metric for OpMetric {
-    fn seek<T: Attributes>(cursor: &mut OpCursor<T>, offset: usize) -> SeekResult {
-        let _ = check_bound(cursor.op_index, offset)?;
-        let mut seek_cursor = OpCursor::new(cursor.delta, cursor.origin_iv);
-        let mut cur_offset = 0;
+    fn seek<T: Attributes>(cursor: &mut DeltaCursor<T>, op_offset: usize) -> SeekResult {
+        let _ = check_bound(cursor.op_offset, op_offset)?;
+        let mut seek_cursor = DeltaCursor::new(cursor.delta, cursor.origin_iv);
+
         while let Some((_, op)) = seek_cursor.iter.next() {
-            cur_offset += op.len();
-            if cur_offset > offset {
+            cursor.descend(op.len());
+            if cursor.op_offset >= op_offset {
                 break;
             }
         }
-        cursor.descend(cur_offset);
         Ok(())
     }
 }
 
+/// [Utf16CodeUnitMetric] is used by [DeltaIterator] for seeking operations.
+/// The unit of the movement is Utf16CodeUnit
 pub struct Utf16CodeUnitMetric();
 
 impl Metric for Utf16CodeUnitMetric {
-    fn seek<T: Attributes>(cursor: &mut OpCursor<T>, offset: usize) -> SeekResult {
+    fn seek<T: Attributes>(cursor: &mut DeltaCursor<T>, offset: usize) -> SeekResult {
         if offset > 0 {
             let _ = check_bound(cursor.consume_count, offset)?;
             let _ = cursor.next_with_len(Some(offset));

+ 122 - 55
shared-lib/lib-ot/src/core/delta/delta.rs

@@ -1,8 +1,9 @@
-use crate::{
-    core::{operation::*, DeltaIter, FlowyStr, Interval, OperationTransformable, MAX_IV_LEN},
-    errors::{ErrorBuilder, OTError, OTErrorCode},
-};
+use crate::errors::{ErrorBuilder, OTError, OTErrorCode};
 
+use crate::core::delta::{DeltaIterator, MAX_IV_LEN};
+use crate::core::flowy_str::FlowyStr;
+use crate::core::interval::Interval;
+use crate::core::operation::{Attributes, Operation, OperationBuilder, OperationTransformable, PhantomAttributes};
 use bytes::Bytes;
 use serde::de::DeserializeOwned;
 use std::{
@@ -13,13 +14,25 @@ use std::{
     str::FromStr,
 };
 
-pub type PlainTextDelta = Delta<PlainTextAttributes>;
+pub type PlainTextDelta = Delta<PhantomAttributes>;
 
-// TODO: optimize the memory usage with Arc::make_mut or Cow
+/// A [Delta] contains list of operations that consists of 'Retain', 'Delete' and 'Insert' operation.
+/// Check out the [Operation] for more details. It describes the document as a sequence of
+/// operations.
+///
+/// If the [T] supports 'serde', that will enable delta to serialize to JSON or deserialize from
+/// a JSON string.
+///
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct Delta<T: Attributes> {
     pub ops: Vec<Operation<T>>,
+
+    /// 'Delete' and 'Retain' operation will update the [utf16_base_len]
+    /// Transforming the other delta, it requires the utf16_base_len must be equal.  
     pub utf16_base_len: usize,
+
+    /// Represents the current len of the delta.
+    /// 'Insert' and 'Retain' operation will update the [utf16_target_len]
     pub utf16_target_len: usize,
 }
 
@@ -81,6 +94,7 @@ where
         }
     }
 
+    /// Adding an operation. It will be added in sequence.
     pub fn add(&mut self, op: Operation<T>) {
         match op {
             Operation::Delete(i) => self.delete(i),
@@ -89,6 +103,7 @@ where
         }
     }
 
+    /// Creating a [Delete] operation with len [n]
     pub fn delete(&mut self, n: usize) {
         if n == 0 {
             return;
@@ -97,10 +112,11 @@ where
         if let Some(Operation::Delete(n_last)) = self.ops.last_mut() {
             *n_last += n;
         } else {
-            self.ops.push(OpBuilder::delete(n).build());
+            self.ops.push(OperationBuilder::delete(n).build());
         }
     }
 
+    /// Creating a [Insert] operation with string, [s].
     pub fn insert(&mut self, s: &str, attributes: T) {
         let s: FlowyStr = s.into();
         if s.is_empty() {
@@ -119,10 +135,10 @@ where
             }
             [.., op_last @ Operation::<T>::Delete(_)] => {
                 let new_last = op_last.clone();
-                *op_last = OpBuilder::<T>::insert(&s).attributes(attributes).build();
+                *op_last = OperationBuilder::<T>::insert(&s).attributes(attributes).build();
                 Some(new_last)
             }
-            _ => Some(OpBuilder::<T>::insert(&s).attributes(attributes).build()),
+            _ => Some(OperationBuilder::<T>::insert(&s).attributes(attributes).build()),
         };
 
         match new_last {
@@ -131,6 +147,7 @@ where
         }
     }
 
+    /// Creating a [Retain] operation with len, [n].
     pub fn retain(&mut self, n: usize, attributes: T) {
         if n == 0 {
             return;
@@ -143,24 +160,48 @@ where
                 self.ops.push(new_op);
             }
         } else {
-            self.ops.push(OpBuilder::<T>::retain(n).attributes(attributes).build());
+            self.ops
+                .push(OperationBuilder::<T>::retain(n).attributes(attributes).build());
         }
     }
 
-    /// Applies an operation to a string, returning a new string.
-    pub fn apply(&self, s: &str) -> Result<String, OTError> {
-        let s: FlowyStr = s.into();
-        if s.utf16_size() != self.utf16_base_len {
+    /// Return the a new string described by this delta. The new string will contains the input string.
+    /// The length of the [applied_str] must be equal to the the [utf16_base_len].
+    ///
+    /// # Arguments
+    ///
+    /// * `applied_str`: A string represents the utf16_base_len content. it will be consumed by the [retain]
+    /// or [delete] operations.
+    ///
+    ///
+    /// # Examples
+    ///
+    /// ```
+    ///  use lib_ot::core::PlainTextDeltaBuilder;
+    ///  let s = "hello";
+    ///  let delta_a = PlainTextDeltaBuilder::new().insert(s).build();
+    ///  let delta_b = PlainTextDeltaBuilder::new()
+    ///         .retain(s.len())
+    ///         .insert(", AppFlowy")
+    ///         .build();
+    ///
+    ///  let after_a = delta_a.content_str().unwrap();
+    ///  let after_b = delta_b.apply(&after_a).unwrap();
+    ///  assert_eq!("hello, AppFlowy", &after_b);
+    /// ```
+    pub fn apply(&self, applied_str: &str) -> Result<String, OTError> {
+        let applied_str: FlowyStr = applied_str.into();
+        if applied_str.utf16_size() != self.utf16_base_len {
             return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength)
                 .msg(format!(
-                    "Expected: {}, received: {}",
+                    "Expected: {}, but received: {}",
                     self.utf16_base_len,
-                    s.utf16_size()
+                    applied_str.utf16_size()
                 ))
                 .build());
         }
         let mut new_s = String::new();
-        let code_point_iter = &mut s.utf16_code_unit_iter();
+        let code_point_iter = &mut applied_str.utf16_code_unit_iter();
         for op in &self.ops {
             match &op {
                 Operation::Retain(retain) => {
@@ -181,34 +222,60 @@ where
         Ok(new_s)
     }
 
-    /// Computes the inverse of an operation. The inverse of an operation is the
-    /// operation that reverts the effects of the operation
-    pub fn invert_str(&self, s: &str) -> Self {
+    /// Computes the inverse [Delta]. The inverse of an operation is the
+    /// operation that reverts the effects of the operation     
+    /// # Arguments
+    ///
+    /// * `inverted_s`: A string represents the utf16_base_len content. The len of [inverted_s]
+    /// must equal to the [utf16_base_len], it will be consumed by the [retain] or [delete] operations.
+    ///
+    /// If the delta's operations just contain a insert operation. The inverted_s must be empty string.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    ///  use lib_ot::core::PlainTextDeltaBuilder;
+    ///  let s = "hello world";
+    ///  let delta = PlainTextDeltaBuilder::new().insert(s).build();
+    ///  let invert_delta = delta.invert_str(s);
+    ///  assert_eq!(delta.utf16_base_len, invert_delta.utf16_target_len);
+    ///  assert_eq!(delta.utf16_target_len, invert_delta.utf16_base_len);
+    ///
+    ///  assert_eq!(invert_delta.apply(s).unwrap(), "")
+    ///
+    /// ```
+    ///
+    pub fn invert_str(&self, inverted_s: &str) -> Self {
         let mut inverted = Delta::default();
-        let chars = &mut s.chars();
+        let inverted_s: FlowyStr = inverted_s.into();
+        let code_point_iter = &mut inverted_s.utf16_code_unit_iter();
+
         for op in &self.ops {
             match &op {
                 Operation::Retain(retain) => {
                     inverted.retain(retain.n, T::default());
-                    // TODO: use advance_by instead, but it's unstable now
-                    // chars.advance_by(retain.num)
                     for _ in 0..retain.n {
-                        chars.next();
+                        code_point_iter.next();
                     }
                 }
                 Operation::Insert(insert) => {
                     inverted.delete(insert.utf16_size());
                 }
                 Operation::Delete(delete) => {
-                    inverted.insert(&chars.take(*delete as usize).collect::<String>(), op.get_attributes());
+                    let bytes = code_point_iter
+                        .take(*delete as usize)
+                        .into_iter()
+                        .flat_map(|a| str::from_utf8(a.0).ok())
+                        .collect::<String>();
+
+                    inverted.insert(&bytes, op.get_attributes());
                 }
             }
         }
         inverted
     }
 
-    /// Checks if this operation has no effect.
-    #[inline]
+    /// Return true if the delta doesn't contain any [Insert] or [Delete] operations.
     pub fn is_noop(&self) -> bool {
         matches!(self.ops.as_slice(), [] | [Operation::Retain(_)])
     }
@@ -231,8 +298,8 @@ where
         Self: Sized,
     {
         let mut new_delta = Delta::default();
-        let mut iter = DeltaIter::new(self);
-        let mut other_iter = DeltaIter::new(other);
+        let mut iter = DeltaIterator::new(self);
+        let mut other_iter = DeltaIterator::new(other);
 
         while iter.has_next() || other_iter.has_next() {
             if other_iter.is_next_insert() {
@@ -252,10 +319,10 @@ where
 
             let op = iter
                 .next_op_with_len(length)
-                .unwrap_or_else(|| OpBuilder::retain(length).build());
+                .unwrap_or_else(|| OperationBuilder::retain(length).build());
             let other_op = other_iter
                 .next_op_with_len(length)
-                .unwrap_or_else(|| OpBuilder::retain(length).build());
+                .unwrap_or_else(|| OperationBuilder::retain(length).build());
 
             // debug_assert_eq!(op.len(), other_op.len(), "Composing delta failed,");
 
@@ -263,12 +330,16 @@ where
                 (Operation::Retain(retain), Operation::Retain(other_retain)) => {
                     let composed_attrs = retain.attributes.compose(&other_retain.attributes)?;
 
-                    new_delta.add(OpBuilder::retain(retain.n).attributes(composed_attrs).build())
+                    new_delta.add(OperationBuilder::retain(retain.n).attributes(composed_attrs).build())
                 }
                 (Operation::Insert(insert), Operation::Retain(other_retain)) => {
                     let mut composed_attrs = insert.attributes.compose(&other_retain.attributes)?;
                     composed_attrs.remove_empty();
-                    new_delta.add(OpBuilder::insert(op.get_data()).attributes(composed_attrs).build())
+                    new_delta.add(
+                        OperationBuilder::insert(op.get_data())
+                            .attributes(composed_attrs)
+                            .build(),
+                    )
                 }
                 (Operation::Retain(_), Operation::Delete(_)) => {
                     new_delta.add(other_op);
@@ -331,7 +402,7 @@ where
                         Ordering::Less => {
                             a_prime.retain(retain.n, composed_attrs.clone());
                             b_prime.retain(retain.n, composed_attrs.clone());
-                            next_op2 = Some(OpBuilder::retain(o_retain.n - retain.n).build());
+                            next_op2 = Some(OperationBuilder::retain(o_retain.n - retain.n).build());
                             next_op1 = ops1.next();
                         }
                         Ordering::Equal => {
@@ -343,14 +414,14 @@ where
                         Ordering::Greater => {
                             a_prime.retain(o_retain.n, composed_attrs.clone());
                             b_prime.retain(o_retain.n, composed_attrs.clone());
-                            next_op1 = Some(OpBuilder::retain(retain.n - o_retain.n).build());
+                            next_op1 = Some(OperationBuilder::retain(retain.n - o_retain.n).build());
                             next_op2 = ops2.next();
                         }
                     };
                 }
                 (Some(Operation::Delete(i)), Some(Operation::Delete(j))) => match i.cmp(j) {
                     Ordering::Less => {
-                        next_op2 = Some(OpBuilder::delete(*j - *i).build());
+                        next_op2 = Some(OperationBuilder::delete(*j - *i).build());
                         next_op1 = ops1.next();
                     }
                     Ordering::Equal => {
@@ -358,7 +429,7 @@ where
                         next_op2 = ops2.next();
                     }
                     Ordering::Greater => {
-                        next_op1 = Some(OpBuilder::delete(*i - *j).build());
+                        next_op1 = Some(OperationBuilder::delete(*i - *j).build());
                         next_op2 = ops2.next();
                     }
                 },
@@ -366,7 +437,7 @@ where
                     match i.cmp(o_retain) {
                         Ordering::Less => {
                             a_prime.delete(*i);
-                            next_op2 = Some(OpBuilder::retain(o_retain.n - *i).build());
+                            next_op2 = Some(OperationBuilder::retain(o_retain.n - *i).build());
                             next_op1 = ops1.next();
                         }
                         Ordering::Equal => {
@@ -376,7 +447,7 @@ where
                         }
                         Ordering::Greater => {
                             a_prime.delete(o_retain.n);
-                            next_op1 = Some(OpBuilder::delete(*i - o_retain.n).build());
+                            next_op1 = Some(OperationBuilder::delete(*i - o_retain.n).build());
                             next_op2 = ops2.next();
                         }
                     };
@@ -385,7 +456,7 @@ where
                     match retain.cmp(j) {
                         Ordering::Less => {
                             b_prime.delete(retain.n);
-                            next_op2 = Some(OpBuilder::delete(*j - retain.n).build());
+                            next_op2 = Some(OperationBuilder::delete(*j - retain.n).build());
                             next_op1 = ops1.next();
                         }
                         Ordering::Equal => {
@@ -395,7 +466,7 @@ where
                         }
                         Ordering::Greater => {
                             b_prime.delete(*j);
-                            next_op1 = Some(OpBuilder::retain(retain.n - *j).build());
+                            next_op1 = Some(OperationBuilder::retain(retain.n - *j).build());
                             next_op2 = ops2.next();
                         }
                     };
@@ -407,21 +478,17 @@ where
 
     fn invert(&self, other: &Self) -> Self {
         let mut inverted = Delta::default();
-        if other.is_empty() {
-            return inverted;
-        }
-
         let mut index = 0;
         for op in &self.ops {
             let len: usize = op.len() as usize;
             match op {
                 Operation::Delete(n) => {
-                    invert_from_other(&mut inverted, other, op, index, index + *n);
+                    invert_other(&mut inverted, other, op, index, index + *n);
                     index += len;
                 }
                 Operation::Retain(_) => {
                     match op.has_attribute() {
-                        true => invert_from_other(&mut inverted, other, op, index, index + len),
+                        true => invert_other(&mut inverted, other, op, index, index + len),
                         false => {
                             // tracing::trace!("invert retain: {} by retain {} {}", op, len,
                             // op.get_attributes());
@@ -452,7 +519,7 @@ where
     }
 }
 
-fn invert_from_other<T: Attributes>(
+fn invert_other<T: Attributes>(
     base: &mut Delta<T>,
     other: &Delta<T>,
     operation: &Operation<T>,
@@ -460,7 +527,7 @@ fn invert_from_other<T: Attributes>(
     end: usize,
 ) {
     tracing::trace!("invert op: {} [{}:{}]", operation, start, end);
-    let other_ops = DeltaIter::from_interval(other, Interval::new(start, end)).ops();
+    let other_ops = DeltaIterator::from_interval(other, Interval::new(start, end)).ops();
     other_ops.into_iter().for_each(|other_op| match operation {
         Operation::Delete(_n) => {
             // tracing::trace!("invert delete: {} by add {}", n, other_op);
@@ -501,7 +568,7 @@ impl<T> Delta<T>
 where
     T: Attributes + DeserializeOwned,
 {
-    pub fn from_delta_str(json: &str) -> Result<Self, OTError> {
+    pub fn from_json_str(json: &str) -> Result<Self, OTError> {
         let delta = serde_json::from_str(json).map_err(|e| {
             tracing::trace!("Deserialize failed: {:?}", e);
             tracing::trace!("{:?}", json);
@@ -512,7 +579,7 @@ where
 
     pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Result<Self, OTError> {
         let json = str::from_utf8(bytes.as_ref())?.to_owned();
-        let val = Self::from_delta_str(&json)?;
+        let val = Self::from_json_str(&json)?;
         Ok(val)
     }
 }
@@ -521,16 +588,16 @@ impl<T> Delta<T>
 where
     T: Attributes + serde::Serialize,
 {
-    pub fn to_delta_str(&self) -> String {
+    pub fn to_json_str(&self) -> String {
         serde_json::to_string(self).unwrap_or_else(|_| "".to_owned())
     }
 
-    pub fn to_str(&self) -> Result<String, OTError> {
+    pub fn content_str(&self) -> Result<String, OTError> {
         self.apply("")
     }
 
-    pub fn to_delta_bytes(&self) -> Bytes {
-        let json = self.to_delta_str();
+    pub fn to_json_bytes(&self) -> Bytes {
+        let json = self.to_json_str();
         Bytes::from(json.into_bytes())
     }
 }

+ 2 - 1
shared-lib/lib-ot/src/core/delta/delta_serde.rs

@@ -1,4 +1,5 @@
-use crate::core::{Attributes, Delta};
+use crate::core::delta::Delta;
+use crate::core::operation::Attributes;
 use serde::{
     de::{SeqAccess, Visitor},
     ser::SerializeSeq,

+ 17 - 17
shared-lib/lib-ot/src/core/delta/iterator.rs

@@ -1,17 +1,17 @@
 use super::cursor::*;
-use crate::{
-    core::{Attributes, Delta, Interval, Operation, NEW_LINE},
-    rich_text::RichTextAttributes,
-};
+use crate::core::delta::{Delta, NEW_LINE};
+use crate::core::interval::Interval;
+use crate::core::operation::{Attributes, Operation};
+use crate::rich_text::RichTextAttributes;
 use std::ops::{Deref, DerefMut};
 
 pub(crate) const MAX_IV_LEN: usize = i32::MAX as usize;
 
-pub struct DeltaIter<'a, T: Attributes> {
-    cursor: OpCursor<'a, T>,
+pub struct DeltaIterator<'a, T: Attributes> {
+    cursor: DeltaCursor<'a, T>,
 }
 
-impl<'a, T> DeltaIter<'a, T>
+impl<'a, T> DeltaIterator<'a, T>
 where
     T: Attributes,
 {
@@ -28,7 +28,7 @@ where
     }
 
     pub fn from_interval(delta: &'a Delta<T>, interval: Interval) -> Self {
-        let cursor = OpCursor::new(delta, interval);
+        let cursor = DeltaCursor::new(delta, interval);
         Self { cursor }
     }
 
@@ -46,7 +46,7 @@ where
     }
 
     pub fn next_op(&mut self) -> Option<Operation<T>> {
-        self.cursor.next_op()
+        self.cursor.get_next_op()
     }
 
     pub fn next_op_with_len(&mut self, len: usize) -> Option<Operation<T>> {
@@ -80,28 +80,28 @@ where
     }
 
     pub fn is_next_insert(&self) -> bool {
-        match self.cursor.next_iter_op() {
+        match self.cursor.next_op() {
             None => false,
             Some(op) => op.is_insert(),
         }
     }
 
     pub fn is_next_retain(&self) -> bool {
-        match self.cursor.next_iter_op() {
+        match self.cursor.next_op() {
             None => false,
             Some(op) => op.is_retain(),
         }
     }
 
     pub fn is_next_delete(&self) -> bool {
-        match self.cursor.next_iter_op() {
+        match self.cursor.next_op() {
             None => false,
             Some(op) => op.is_delete(),
         }
     }
 }
 
-impl<'a, T> Iterator for DeltaIter<'a, T>
+impl<'a, T> Iterator for DeltaIterator<'a, T>
 where
     T: Attributes,
 {
@@ -112,7 +112,7 @@ where
 }
 
 pub fn is_empty_line_at_index(delta: &Delta<RichTextAttributes>, index: usize) -> bool {
-    let mut iter = DeltaIter::new(delta);
+    let mut iter = DeltaIterator::new(delta);
     let (prev, next) = (iter.next_op_with_len(index), iter.next_op());
     if prev.is_none() {
         return true;
@@ -128,7 +128,7 @@ pub fn is_empty_line_at_index(delta: &Delta<RichTextAttributes>, index: usize) -
 }
 
 pub struct AttributesIter<'a, T: Attributes> {
-    delta_iter: DeltaIter<'a, T>,
+    delta_iter: DeltaIterator<'a, T>,
 }
 
 impl<'a, T> AttributesIter<'a, T>
@@ -141,7 +141,7 @@ where
     }
 
     pub fn from_interval(delta: &'a Delta<T>, interval: Interval) -> Self {
-        let delta_iter = DeltaIter::from_interval(delta, interval);
+        let delta_iter = DeltaIterator::from_interval(delta, interval);
         Self { delta_iter }
     }
 
@@ -157,7 +157,7 @@ impl<'a, T> Deref for AttributesIter<'a, T>
 where
     T: Attributes,
 {
-    type Target = DeltaIter<'a, T>;
+    type Target = DeltaIterator<'a, T>;
 
     fn deref(&self) -> &Self::Target {
         &self.delta_iter

+ 3 - 2
shared-lib/lib-ot/src/core/flowy_str.rs

@@ -177,7 +177,7 @@ impl<'a> FlowyUtf16CodePointIterator<'a> {
     }
 }
 
-use crate::core::Interval;
+use crate::core::interval::Interval;
 use std::str;
 
 impl<'a> Iterator for FlowyUtf16CodePointIterator<'a> {
@@ -226,7 +226,8 @@ pub fn len_utf8_from_first_byte(b: u8) -> usize {
 
 #[cfg(test)]
 mod tests {
-    use crate::core::{FlowyStr, Interval};
+    use crate::core::flowy_str::FlowyStr;
+    use crate::core::interval::Interval;
 
     #[test]
     fn flowy_str_code_unit() {

+ 1 - 1
shared-lib/lib-ot/src/core/interval.rs

@@ -157,7 +157,7 @@ impl From<RangeToInclusive<usize>> for Interval {
 
 #[cfg(test)]
 mod tests {
-    use crate::core::Interval;
+    use crate::core::interval::Interval;
 
     #[test]
     fn contains() {

+ 0 - 21
shared-lib/lib-ot/src/core/mod.rs

@@ -3,28 +3,7 @@ mod flowy_str;
 mod interval;
 mod operation;
 
-use crate::errors::OTError;
 pub use delta::*;
 pub use flowy_str::*;
 pub use interval::*;
 pub use operation::*;
-
-pub trait OperationTransformable {
-    /// Merges the operation with `other` into one operation while preserving
-    /// the changes of both.
-    fn compose(&self, other: &Self) -> Result<Self, OTError>
-    where
-        Self: Sized;
-    /// Transforms two operations a and b that happened concurrently and
-    /// produces two operations a' and b'.
-    ///  (a', b') = a.transform(b)
-    ///  a.compose(b') = b.compose(a')
-    fn transform(&self, other: &Self) -> Result<(Self, Self), OTError>
-    where
-        Self: Sized;
-    /// Inverts the operation with `other` to produces undo operation.
-    /// undo = a.invert(b)
-    /// new_b = b.compose(a)
-    /// b = new_b.compose(undo)
-    fn invert(&self, other: &Self) -> Self;
-}

+ 15 - 17
shared-lib/lib-ot/src/core/operation/builder.rs

@@ -1,40 +1,38 @@
-use crate::{
-    core::{Attributes, Operation, PlainTextAttributes},
-    rich_text::RichTextAttributes,
-};
+use crate::core::operation::{Attributes, Operation, PhantomAttributes};
+use crate::rich_text::RichTextAttributes;
 
-pub type RichTextOpBuilder = OpBuilder<RichTextAttributes>;
-pub type PlainTextOpBuilder = OpBuilder<PlainTextAttributes>;
+pub type RichTextOpBuilder = OperationBuilder<RichTextAttributes>;
+pub type PlainTextOpBuilder = OperationBuilder<PhantomAttributes>;
 
-pub struct OpBuilder<T: Attributes> {
+pub struct OperationBuilder<T: Attributes> {
     ty: Operation<T>,
     attrs: T,
 }
 
-impl<T> OpBuilder<T>
+impl<T> OperationBuilder<T>
 where
     T: Attributes,
 {
-    pub fn new(ty: Operation<T>) -> OpBuilder<T> {
-        OpBuilder {
+    pub fn new(ty: Operation<T>) -> OperationBuilder<T> {
+        OperationBuilder {
             ty,
             attrs: T::default(),
         }
     }
 
-    pub fn retain(n: usize) -> OpBuilder<T> {
-        OpBuilder::new(Operation::Retain(n.into()))
+    pub fn retain(n: usize) -> OperationBuilder<T> {
+        OperationBuilder::new(Operation::Retain(n.into()))
     }
 
-    pub fn delete(n: usize) -> OpBuilder<T> {
-        OpBuilder::new(Operation::Delete(n))
+    pub fn delete(n: usize) -> OperationBuilder<T> {
+        OperationBuilder::new(Operation::Delete(n))
     }
 
-    pub fn insert(s: &str) -> OpBuilder<T> {
-        OpBuilder::new(Operation::Insert(s.into()))
+    pub fn insert(s: &str) -> OperationBuilder<T> {
+        OperationBuilder::new(Operation::Insert(s.into()))
     }
 
-    pub fn attributes(mut self, attrs: T) -> OpBuilder<T> {
+    pub fn attributes(mut self, attrs: T) -> OperationBuilder<T> {
         self.attrs = attrs;
         self
     }

+ 103 - 39
shared-lib/lib-ot/src/core/operation/operation.rs

@@ -1,8 +1,9 @@
-use crate::{
-    core::{FlowyStr, Interval, OpBuilder, OperationTransformable},
-    errors::OTError,
-};
+use crate::core::flowy_str::FlowyStr;
+use crate::core::interval::Interval;
+use crate::core::operation::OperationBuilder;
+use crate::errors::OTError;
 use serde::{Deserialize, Serialize, __private::Formatter};
+use std::fmt::Display;
 use std::{
     cmp::min,
     fmt,
@@ -10,15 +11,89 @@ use std::{
     ops::{Deref, DerefMut},
 };
 
-pub trait Attributes: fmt::Display + Eq + PartialEq + Default + Clone + Debug + OperationTransformable {
-    fn is_empty(&self) -> bool;
+pub trait OperationTransformable {
+    /// Merges the operation with `other` into one operation while preserving
+    /// the changes of both.    
+    ///
+    /// # Arguments
+    ///
+    /// * `other`: The delta gonna to merge.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    ///  use lib_ot::core::{OperationTransformable, PlainTextDeltaBuilder};
+    ///  let document = PlainTextDeltaBuilder::new().build();
+    ///  let delta = PlainTextDeltaBuilder::new().insert("abc").build();
+    ///  let new_document = document.compose(&delta).unwrap();
+    ///  assert_eq!(new_document.content_str().unwrap(), "abc".to_owned());
+    /// ```
+    fn compose(&self, other: &Self) -> Result<Self, OTError>
+    where
+        Self: Sized;
+
+    /// Transforms two operations a and b that happened concurrently and
+    /// produces two operations a' and b'.
+    ///  (a', b') = a.transform(b)
+    ///  a.compose(b') = b.compose(a')    
+    ///
+    fn transform(&self, other: &Self) -> Result<(Self, Self), OTError>
+    where
+        Self: Sized;
+
+    /// Returns the invert delta from the other. It can be used to do the undo operation.
+    ///
+    /// # Arguments
+    ///
+    /// * `other`:  Generate the undo delta for [Other]. [Other] can compose the undo delta to return
+    /// to the previous state.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use lib_ot::core::{OperationTransformable, PlainTextDeltaBuilder};
+    /// let original_document = PlainTextDeltaBuilder::new().build();
+    /// let delta = PlainTextDeltaBuilder::new().insert("abc").build();
+    ///
+    /// let undo_delta = delta.invert(&original_document);
+    /// let new_document = original_document.compose(&delta).unwrap();
+    /// let document = new_document.compose(&undo_delta).unwrap();
+    ///
+    /// assert_eq!(original_document, document);
+    ///
+    /// ```
+    fn invert(&self, other: &Self) -> Self;
+}
+
+/// Each operation can carry attributes. For example, the [RichTextAttributes] has a list of key/value attributes.
+/// Such as { bold: true, italic: true }.  
+///
+/// Because [Operation] is generic over the T, so you must specify the T. For example, the [PlainTextDelta]. It use
+/// use [PhantomAttributes] as the T. [PhantomAttributes] does nothing, just a phantom.
+///
+pub trait Attributes: Default + Display + Eq + PartialEq + Clone + Debug + OperationTransformable {
+    fn is_empty(&self) -> bool {
+        true
+    }
 
-    // Remove the empty attribute which value is None.
-    fn remove_empty(&mut self);
+    /// Remove the empty attribute which value is None.
+    fn remove_empty(&mut self) {
+        // Do nothing
+    }
 
-    fn extend_other(&mut self, other: Self);
+    fn extend_other(&mut self, _other: Self) {
+        // Do nothing
+    }
 }
 
+/// [Operation] consists of three types.
+/// * Delete
+/// * Retain
+/// * Insert
+///
+/// The [T] should support serde if you want to serialize/deserialize the operation
+/// to json string. You could check out the operation_serde.rs for more information.
+///
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum Operation<T: Attributes> {
     Delete(usize),
@@ -77,22 +152,22 @@ where
         let right;
         match self {
             Operation::Delete(n) => {
-                left = Some(OpBuilder::<T>::delete(index).build());
-                right = Some(OpBuilder::<T>::delete(*n - index).build());
+                left = Some(OperationBuilder::<T>::delete(index).build());
+                right = Some(OperationBuilder::<T>::delete(*n - index).build());
             }
             Operation::Retain(retain) => {
-                left = Some(OpBuilder::<T>::delete(index).build());
-                right = Some(OpBuilder::<T>::delete(retain.n - index).build());
+                left = Some(OperationBuilder::<T>::delete(index).build());
+                right = Some(OperationBuilder::<T>::delete(retain.n - index).build());
             }
             Operation::Insert(insert) => {
                 let attributes = self.get_attributes();
                 left = Some(
-                    OpBuilder::<T>::insert(&insert.s[0..index])
+                    OperationBuilder::<T>::insert(&insert.s[0..index])
                         .attributes(attributes.clone())
                         .build(),
                 );
                 right = Some(
-                    OpBuilder::<T>::insert(&insert.s[index..insert.utf16_size()])
+                    OperationBuilder::<T>::insert(&insert.s[index..insert.utf16_size()])
                         .attributes(attributes)
                         .build(),
                 );
@@ -104,16 +179,18 @@ where
 
     pub fn shrink(&self, interval: Interval) -> Option<Operation<T>> {
         let op = match self {
-            Operation::Delete(n) => OpBuilder::delete(min(*n, interval.size())).build(),
-            Operation::Retain(retain) => OpBuilder::retain(min(retain.n, interval.size()))
+            Operation::Delete(n) => OperationBuilder::delete(min(*n, interval.size())).build(),
+            Operation::Retain(retain) => OperationBuilder::retain(min(retain.n, interval.size()))
                 .attributes(retain.attributes.clone())
                 .build(),
             Operation::Insert(insert) => {
                 if interval.start > insert.utf16_size() {
-                    OpBuilder::insert("").build()
+                    OperationBuilder::insert("").build()
                 } else {
                     let s = insert.s.sub_str(interval).unwrap_or_else(|| "".to_owned());
-                    OpBuilder::insert(&s).attributes(insert.attributes.clone()).build()
+                    OperationBuilder::insert(&s)
+                        .attributes(insert.attributes.clone())
+                        .build()
                 }
             }
         };
@@ -178,9 +255,7 @@ where
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Retain<T: Attributes> {
-    // #[serde(rename(serialize = "retain", deserialize = "retain"))]
     pub n: usize,
-    // #[serde(skip_serializing_if = "is_empty")]
     pub attributes: T,
 }
 
@@ -212,7 +287,7 @@ where
             self.n += n;
             None
         } else {
-            Some(OpBuilder::retain(n).attributes(attributes).build())
+            Some(OperationBuilder::retain(n).attributes(attributes).build())
         }
     }
 
@@ -255,10 +330,7 @@ where
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct Insert<T: Attributes> {
-    // #[serde(rename(serialize = "insert", deserialize = "insert"))]
     pub s: FlowyStr,
-
-    // #[serde(skip_serializing_if = "is_empty")]
     pub attributes: T,
 }
 
@@ -296,7 +368,7 @@ where
             self.s += s;
             None
         } else {
-            Some(OpBuilder::<T>::insert(s).attributes(attributes).build())
+            Some(OperationBuilder::<T>::insert(s).attributes(attributes).build())
         }
     }
 
@@ -339,24 +411,16 @@ where
 }
 
 #[derive(Debug, Clone, Eq, PartialEq, Default, Serialize, Deserialize)]
-pub struct PlainTextAttributes();
-impl fmt::Display for PlainTextAttributes {
+pub struct PhantomAttributes();
+impl fmt::Display for PhantomAttributes {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str("PlainAttributes")
+        f.write_str("PhantomAttributes")
     }
 }
 
-impl Attributes for PlainTextAttributes {
-    fn is_empty(&self) -> bool {
-        true
-    }
-
-    fn remove_empty(&mut self) {}
-
-    fn extend_other(&mut self, _other: Self) {}
-}
+impl Attributes for PhantomAttributes {}
 
-impl OperationTransformable for PlainTextAttributes {
+impl OperationTransformable for PhantomAttributes {
     fn compose(&self, _other: &Self) -> Result<Self, OTError> {
         Ok(self.clone())
     }

+ 2 - 1
shared-lib/lib-ot/src/core/operation/operation_serde.rs

@@ -1,4 +1,5 @@
-use crate::core::{Attributes, FlowyStr, Insert, Operation, Retain};
+use crate::core::flowy_str::FlowyStr;
+use crate::core::operation::{Attributes, Insert, Operation, Retain};
 use serde::{
     de,
     de::{MapAccess, SeqAccess, Visitor},

+ 2 - 6
shared-lib/lib-ot/src/rich_text/attributes.rs

@@ -1,10 +1,6 @@
 #![allow(non_snake_case)]
-use crate::{
-    block_attribute,
-    core::{Attributes, Operation, OperationTransformable},
-    errors::OTError,
-    ignore_attribute, inline_attribute, list_attribute,
-};
+use crate::core::{Attributes, Operation, OperationTransformable};
+use crate::{block_attribute, errors::OTError, ignore_attribute, inline_attribute, list_attribute};
 use lazy_static::lazy_static;
 use std::{
     collections::{HashMap, HashSet},

+ 2 - 4
shared-lib/lib-ot/src/rich_text/delta.rs

@@ -1,7 +1,5 @@
-use crate::{
-    core::{Delta, DeltaBuilder},
-    rich_text::RichTextAttributes,
-};
+use crate::core::{Delta, DeltaBuilder};
+use crate::rich_text::RichTextAttributes;
 
 pub type RichTextDelta = Delta<RichTextAttributes>;
 pub type RichTextDeltaBuilder = DeltaBuilder<RichTextAttributes>;