Prechádzať zdrojové kódy

move flowy-ot document to flowy-document

appflowy 3 rokov pred
rodič
commit
2b9bc7a0cd
66 zmenil súbory, kde vykonal 606 pridanie a 517 odobranie
  1. 49 34
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/doc.pb.dart
  2. 13 12
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/doc.pbjson.dart
  3. 6 0
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/errors.pbenum.dart
  4. 4 1
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/errors.pbjson.dart
  5. 1 1
      rust-lib/flowy-database/migrations/2021-07-22-234458_flowy-editor/up.sql
  6. 1 0
      rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/down.sql
  7. 1 0
      rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/up.sql
  8. 8 2
      rust-lib/flowy-database/src/schema.rs
  9. 2 2
      rust-lib/flowy-derive/src/derive_cache/derive_cache.rs
  10. 10 0
      rust-lib/flowy-document/Cargo.toml
  11. 6 3
      rust-lib/flowy-document/src/entities/doc/doc.rs
  12. 15 0
      rust-lib/flowy-document/src/errors.rs
  13. 1 1
      rust-lib/flowy-document/src/lib.rs
  14. 16 27
      rust-lib/flowy-document/src/module.rs
  15. 137 98
      rust-lib/flowy-document/src/protobuf/model/doc.rs
  16. 39 23
      rust-lib/flowy-document/src/protobuf/model/errors.rs
  17. 4 3
      rust-lib/flowy-document/src/protobuf/proto/doc.proto
  18. 3 0
      rust-lib/flowy-document/src/protobuf/proto/errors.proto
  19. 48 0
      rust-lib/flowy-document/src/services/cache.rs
  20. 3 3
      rust-lib/flowy-document/src/services/doc/document/data.rs
  21. 39 23
      rust-lib/flowy-document/src/services/doc/document/document.rs
  22. 0 0
      rust-lib/flowy-document/src/services/doc/document/mod.rs
  23. 0 0
      rust-lib/flowy-document/src/services/doc/document/selection.rs
  24. 2 4
      rust-lib/flowy-document/src/services/doc/extensions/delete/default_delete.rs
  25. 0 0
      rust-lib/flowy-document/src/services/doc/extensions/delete/mod.rs
  26. 2 4
      rust-lib/flowy-document/src/services/doc/extensions/delete/preserve_line_format_merge.rs
  27. 0 0
      rust-lib/flowy-document/src/services/doc/extensions/format/format_at_position.rs
  28. 2 4
      rust-lib/flowy-document/src/services/doc/extensions/format/helper.rs
  29. 0 0
      rust-lib/flowy-document/src/services/doc/extensions/format/mod.rs
  30. 4 6
      rust-lib/flowy-document/src/services/doc/extensions/format/resolve_block_format.rs
  31. 4 6
      rust-lib/flowy-document/src/services/doc/extensions/format/resolve_inline_format.rs
  32. 2 6
      rust-lib/flowy-document/src/services/doc/extensions/insert/auto_exit_block.rs
  33. 5 9
      rust-lib/flowy-document/src/services/doc/extensions/insert/auto_format.rs
  34. 2 4
      rust-lib/flowy-document/src/services/doc/extensions/insert/default_insert.rs
  35. 2 2
      rust-lib/flowy-document/src/services/doc/extensions/insert/mod.rs
  36. 11 3
      rust-lib/flowy-document/src/services/doc/extensions/insert/preserve_block_format.rs
  37. 4 6
      rust-lib/flowy-document/src/services/doc/extensions/insert/preserve_inline_format.rs
  38. 2 4
      rust-lib/flowy-document/src/services/doc/extensions/insert/reset_format_on_new_line.rs
  39. 1 1
      rust-lib/flowy-document/src/services/doc/extensions/mod.rs
  40. 1 14
      rust-lib/flowy-document/src/services/doc/history.rs
  41. 0 0
      rust-lib/flowy-document/src/services/doc/mod.rs
  42. 1 1
      rust-lib/flowy-document/src/services/doc/util.rs
  43. 1 1
      rust-lib/flowy-document/src/services/doc/view.rs
  44. 76 32
      rust-lib/flowy-document/src/services/doc_controller.rs
  45. 3 1
      rust-lib/flowy-document/src/services/mod.rs
  46. 28 15
      rust-lib/flowy-document/src/services/open_doc.rs
  47. 0 94
      rust-lib/flowy-document/src/services/open_doc/manager.rs
  48. 0 5
      rust-lib/flowy-document/src/services/open_doc/mod.rs
  49. 2 2
      rust-lib/flowy-document/src/services/server/mod.rs
  50. 3 3
      rust-lib/flowy-document/src/services/server/server_api.rs
  51. 2 2
      rust-lib/flowy-document/src/services/server/server_api_mock.rs
  52. 6 5
      rust-lib/flowy-document/src/sql_tables/doc/doc_table.rs
  53. 3 7
      rust-lib/flowy-document/tests/editor/attribute_test.rs
  54. 0 1
      rust-lib/flowy-document/tests/editor/main.rs
  55. 7 4
      rust-lib/flowy-document/tests/editor/mod.rs
  56. 3 5
      rust-lib/flowy-document/tests/editor/op_test.rs
  57. 2 4
      rust-lib/flowy-document/tests/editor/serde_test.rs
  58. 3 7
      rust-lib/flowy-document/tests/editor/undo_redo_test.rs
  59. 1 0
      rust-lib/flowy-document/tests/main.rs
  60. 0 7
      rust-lib/flowy-ot/Cargo.toml
  61. 0 2
      rust-lib/flowy-ot/src/lib.rs
  62. 0 1
      rust-lib/flowy-ot/src/server/mod.rs
  63. 10 7
      rust-lib/flowy-workspace/src/entities/view/view_update.rs
  64. 1 1
      rust-lib/flowy-workspace/src/event.rs
  65. 2 2
      rust-lib/flowy-workspace/src/handlers/view_handler.rs
  66. 2 2
      rust-lib/flowy-workspace/src/services/view_controller.rs

+ 49 - 34
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/doc.pb.dart

@@ -7,6 +7,7 @@
 
 import 'dart:core' as $core;
 
+import 'package:fixnum/fixnum.dart' as $fixnum;
 import 'package:protobuf/protobuf.dart' as $pb;
 
 class CreateDocParams extends $pb.GeneratedMessage {
@@ -74,6 +75,7 @@ class Doc extends $pb.GeneratedMessage {
   static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Doc', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
     ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY)
+    ..aInt64(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'revision')
     ..hasRequiredFields = false
   ;
 
@@ -81,6 +83,7 @@ class Doc extends $pb.GeneratedMessage {
   factory Doc({
     $core.String? id,
     $core.List<$core.int>? data,
+    $fixnum.Int64? revision,
   }) {
     final _result = create();
     if (id != null) {
@@ -89,6 +92,9 @@ class Doc extends $pb.GeneratedMessage {
     if (data != null) {
       _result.data = data;
     }
+    if (revision != null) {
+      _result.revision = revision;
+    }
     return _result;
   }
   factory Doc.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
@@ -129,49 +135,58 @@ class Doc extends $pb.GeneratedMessage {
   $core.bool hasData() => $_has(1);
   @$pb.TagNumber(2)
   void clearData() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $fixnum.Int64 get revision => $_getI64(2);
+  @$pb.TagNumber(3)
+  set revision($fixnum.Int64 v) { $_setInt64(2, v); }
+  @$pb.TagNumber(3)
+  $core.bool hasRevision() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearRevision() => clearField(3);
 }
 
-class SaveDocParams extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SaveDocParams', createEmptyInstance: create)
+class UpdateDocParams extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'UpdateDocParams', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
-    ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY)
+    ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'docData', $pb.PbFieldType.OY)
     ..hasRequiredFields = false
   ;
 
-  SaveDocParams._() : super();
-  factory SaveDocParams({
+  UpdateDocParams._() : super();
+  factory UpdateDocParams({
     $core.String? id,
-    $core.List<$core.int>? data,
+    $core.List<$core.int>? docData,
   }) {
     final _result = create();
     if (id != null) {
       _result.id = id;
     }
-    if (data != null) {
-      _result.data = data;
+    if (docData != null) {
+      _result.docData = docData;
     }
     return _result;
   }
-  factory SaveDocParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory SaveDocParams.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory UpdateDocParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory UpdateDocParams.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
   'Will be removed in next major version')
-  SaveDocParams clone() => SaveDocParams()..mergeFromMessage(this);
+  UpdateDocParams clone() => UpdateDocParams()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  SaveDocParams copyWith(void Function(SaveDocParams) updates) => super.copyWith((message) => updates(message as SaveDocParams)) as SaveDocParams; // ignore: deprecated_member_use
+  UpdateDocParams copyWith(void Function(UpdateDocParams) updates) => super.copyWith((message) => updates(message as UpdateDocParams)) as UpdateDocParams; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static SaveDocParams create() => SaveDocParams._();
-  SaveDocParams createEmptyInstance() => create();
-  static $pb.PbList<SaveDocParams> createRepeated() => $pb.PbList<SaveDocParams>();
+  static UpdateDocParams create() => UpdateDocParams._();
+  UpdateDocParams createEmptyInstance() => create();
+  static $pb.PbList<UpdateDocParams> createRepeated() => $pb.PbList<UpdateDocParams>();
   @$core.pragma('dart2js:noInline')
-  static SaveDocParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SaveDocParams>(create);
-  static SaveDocParams? _defaultInstance;
+  static UpdateDocParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<UpdateDocParams>(create);
+  static UpdateDocParams? _defaultInstance;
 
   @$pb.TagNumber(1)
   $core.String get id => $_getSZ(0);
@@ -183,24 +198,24 @@ class SaveDocParams extends $pb.GeneratedMessage {
   void clearId() => clearField(1);
 
   @$pb.TagNumber(2)
-  $core.List<$core.int> get data => $_getN(1);
+  $core.List<$core.int> get docData => $_getN(1);
   @$pb.TagNumber(2)
-  set data($core.List<$core.int> v) { $_setBytes(1, v); }
+  set docData($core.List<$core.int> v) { $_setBytes(1, v); }
   @$pb.TagNumber(2)
-  $core.bool hasData() => $_has(1);
+  $core.bool hasDocData() => $_has(1);
   @$pb.TagNumber(2)
-  void clearData() => clearField(2);
+  void clearDocData() => clearField(2);
 }
 
-class ApplyChangesetParams extends $pb.GeneratedMessage {
-  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ApplyChangesetParams', createEmptyInstance: create)
+class DocChangeset extends $pb.GeneratedMessage {
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'DocChangeset', createEmptyInstance: create)
     ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
     ..a<$core.List<$core.int>>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'data', $pb.PbFieldType.OY)
     ..hasRequiredFields = false
   ;
 
-  ApplyChangesetParams._() : super();
-  factory ApplyChangesetParams({
+  DocChangeset._() : super();
+  factory DocChangeset({
     $core.String? id,
     $core.List<$core.int>? data,
   }) {
@@ -213,26 +228,26 @@ class ApplyChangesetParams extends $pb.GeneratedMessage {
     }
     return _result;
   }
-  factory ApplyChangesetParams.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
-  factory ApplyChangesetParams.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
+  factory DocChangeset.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
+  factory DocChangeset.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
   'Will be removed in next major version')
-  ApplyChangesetParams clone() => ApplyChangesetParams()..mergeFromMessage(this);
+  DocChangeset clone() => DocChangeset()..mergeFromMessage(this);
   @$core.Deprecated(
   'Using this can add significant overhead to your binary. '
   'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
   'Will be removed in next major version')
-  ApplyChangesetParams copyWith(void Function(ApplyChangesetParams) updates) => super.copyWith((message) => updates(message as ApplyChangesetParams)) as ApplyChangesetParams; // ignore: deprecated_member_use
+  DocChangeset copyWith(void Function(DocChangeset) updates) => super.copyWith((message) => updates(message as DocChangeset)) as DocChangeset; // ignore: deprecated_member_use
   $pb.BuilderInfo get info_ => _i;
   @$core.pragma('dart2js:noInline')
-  static ApplyChangesetParams create() => ApplyChangesetParams._();
-  ApplyChangesetParams createEmptyInstance() => create();
-  static $pb.PbList<ApplyChangesetParams> createRepeated() => $pb.PbList<ApplyChangesetParams>();
+  static DocChangeset create() => DocChangeset._();
+  DocChangeset createEmptyInstance() => create();
+  static $pb.PbList<DocChangeset> createRepeated() => $pb.PbList<DocChangeset>();
   @$core.pragma('dart2js:noInline')
-  static ApplyChangesetParams getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ApplyChangesetParams>(create);
-  static ApplyChangesetParams? _defaultInstance;
+  static DocChangeset getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<DocChangeset>(create);
+  static DocChangeset? _defaultInstance;
 
   @$pb.TagNumber(1)
   $core.String get id => $_getSZ(0);

+ 13 - 12
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/doc.pbjson.dart

@@ -25,33 +25,34 @@ const Doc$json = const {
   '2': const [
     const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
     const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'},
+    const {'1': 'revision', '3': 3, '4': 1, '5': 3, '10': 'revision'},
   ],
 };
 
 /// Descriptor for `Doc`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List docDescriptor = $convert.base64Decode('CgNEb2MSDgoCaWQYASABKAlSAmlkEhIKBGRhdGEYAiABKAxSBGRhdGE=');
-@$core.Deprecated('Use saveDocParamsDescriptor instead')
-const SaveDocParams$json = const {
-  '1': 'SaveDocParams',
+final $typed_data.Uint8List docDescriptor = $convert.base64Decode('CgNEb2MSDgoCaWQYASABKAlSAmlkEhIKBGRhdGEYAiABKAxSBGRhdGESGgoIcmV2aXNpb24YAyABKANSCHJldmlzaW9u');
+@$core.Deprecated('Use updateDocParamsDescriptor instead')
+const UpdateDocParams$json = const {
+  '1': 'UpdateDocParams',
   '2': const [
     const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
-    const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'},
+    const {'1': 'doc_data', '3': 2, '4': 1, '5': 12, '10': 'docData'},
   ],
 };
 
-/// Descriptor for `SaveDocParams`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List saveDocParamsDescriptor = $convert.base64Decode('Cg1TYXZlRG9jUGFyYW1zEg4KAmlkGAEgASgJUgJpZBISCgRkYXRhGAIgASgMUgRkYXRh');
-@$core.Deprecated('Use applyChangesetParamsDescriptor instead')
-const ApplyChangesetParams$json = const {
-  '1': 'ApplyChangesetParams',
+/// Descriptor for `UpdateDocParams`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List updateDocParamsDescriptor = $convert.base64Decode('Cg9VcGRhdGVEb2NQYXJhbXMSDgoCaWQYASABKAlSAmlkEhkKCGRvY19kYXRhGAIgASgMUgdkb2NEYXRh');
+@$core.Deprecated('Use docChangesetDescriptor instead')
+const DocChangeset$json = const {
+  '1': 'DocChangeset',
   '2': const [
     const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'},
     const {'1': 'data', '3': 2, '4': 1, '5': 12, '10': 'data'},
   ],
 };
 
-/// Descriptor for `ApplyChangesetParams`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List applyChangesetParamsDescriptor = $convert.base64Decode('ChRBcHBseUNoYW5nZXNldFBhcmFtcxIOCgJpZBgBIAEoCVICaWQSEgoEZGF0YRgCIAEoDFIEZGF0YQ==');
+/// Descriptor for `DocChangeset`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List docChangesetDescriptor = $convert.base64Decode('CgxEb2NDaGFuZ2VzZXQSDgoCaWQYASABKAlSAmlkEhIKBGRhdGEYAiABKAxSBGRhdGE=');
 @$core.Deprecated('Use queryDocParamsDescriptor instead')
 const QueryDocParams$json = const {
   '1': 'QueryDocParams',

+ 6 - 0
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/errors.pbenum.dart

@@ -13,6 +13,9 @@ class ErrorCode extends $pb.ProtobufEnum {
   static const ErrorCode DocIdInvalid = ErrorCode._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DocIdInvalid');
   static const ErrorCode DocNotfound = ErrorCode._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DocNotfound');
   static const ErrorCode WsConnectError = ErrorCode._(10, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'WsConnectError');
+  static const ErrorCode UndoFail = ErrorCode._(200, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UndoFail');
+  static const ErrorCode RedoFail = ErrorCode._(201, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RedoFail');
+  static const ErrorCode OutOfBound = ErrorCode._(202, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OutOfBound');
   static const ErrorCode UserUnauthorized = ErrorCode._(999, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserUnauthorized');
   static const ErrorCode InternalError = ErrorCode._(1000, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InternalError');
 
@@ -20,6 +23,9 @@ class ErrorCode extends $pb.ProtobufEnum {
     DocIdInvalid,
     DocNotfound,
     WsConnectError,
+    UndoFail,
+    RedoFail,
+    OutOfBound,
     UserUnauthorized,
     InternalError,
   ];

+ 4 - 1
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-document/errors.pbjson.dart

@@ -15,13 +15,16 @@ const ErrorCode$json = const {
     const {'1': 'DocIdInvalid', '2': 0},
     const {'1': 'DocNotfound', '2': 1},
     const {'1': 'WsConnectError', '2': 10},
+    const {'1': 'UndoFail', '2': 200},
+    const {'1': 'RedoFail', '2': 201},
+    const {'1': 'OutOfBound', '2': 202},
     const {'1': 'UserUnauthorized', '2': 999},
     const {'1': 'InternalError', '2': 1000},
   ],
 };
 
 /// Descriptor for `ErrorCode`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSEAoMRG9jSWRJbnZhbGlkEAASDwoLRG9jTm90Zm91bmQQARISCg5Xc0Nvbm5lY3RFcnJvchAKEhUKEFVzZXJVbmF1dGhvcml6ZWQQ5wcSEgoNSW50ZXJuYWxFcnJvchDoBw==');
+final $typed_data.Uint8List errorCodeDescriptor = $convert.base64Decode('CglFcnJvckNvZGUSEAoMRG9jSWRJbnZhbGlkEAASDwoLRG9jTm90Zm91bmQQARISCg5Xc0Nvbm5lY3RFcnJvchAKEg0KCFVuZG9GYWlsEMgBEg0KCFJlZG9GYWlsEMkBEg8KCk91dE9mQm91bmQQygESFQoQVXNlclVuYXV0aG9yaXplZBDnBxISCg1JbnRlcm5hbEVycm9yEOgH');
 @$core.Deprecated('Use docErrorDescriptor instead')
 const DocError$json = const {
   '1': 'DocError',

+ 1 - 1
rust-lib/flowy-database/migrations/2021-07-22-234458_flowy-editor/up.sql

@@ -2,5 +2,5 @@
 CREATE TABLE doc_table (
     id TEXT NOT NULL PRIMARY KEY,
     data BLOB NOT NULL DEFAULT (x''),
-    version BIGINT NOT NULL DEFAULT 0
+    revision BIGINT NOT NULL DEFAULT 0
 );

+ 1 - 0
rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/down.sql

@@ -0,0 +1 @@
+-- This file should undo anything in `up.sql`

+ 1 - 0
rust-lib/flowy-database/migrations/2021-09-22-074638_flowy-doc-op/up.sql

@@ -0,0 +1 @@
+-- Your SQL goes here

+ 8 - 2
rust-lib/flowy-database/src/schema.rs

@@ -17,7 +17,7 @@ table! {
     doc_table (id) {
         id -> Text,
         data -> Binary,
-        version -> BigInt,
+        revision -> BigInt,
     }
 }
 
@@ -58,4 +58,10 @@ table! {
     }
 }
 
-allow_tables_to_appear_in_same_query!(app_table, doc_table, user_table, view_table, workspace_table,);
+allow_tables_to_appear_in_same_query!(
+    app_table,
+    doc_table,
+    user_table,
+    view_table,
+    workspace_table,
+);

+ 2 - 2
rust-lib/flowy-derive/src/derive_cache/derive_cache.rs

@@ -57,8 +57,8 @@ pub fn category_from_str(type_str: &str) -> TypeCategory {
         | "WsMessage"
         | "CreateDocParams"
         | "Doc"
-        | "SaveDocParams"
-        | "ApplyChangesetParams"
+        | "UpdateDocParams"
+        | "DocChangeset"
         | "QueryDocParams"
         | "WsDocumentData"
         | "DocError"

+ 10 - 0
rust-lib/flowy-document/Cargo.toml

@@ -30,9 +30,19 @@ strum = "0.21"
 strum_macros = "0.21"
 dashmap = "4.0"
 parking_lot = "0.11"
+bytecount = "0.6.0"
+url = "2.2"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = {version = "1.0"}
+chrono = "0.4.19"
 
 [dev-dependencies]
 flowy-test = { path = "../flowy-test" }
+color-eyre = { version = "0.5", default-features = false }
+criterion = "0.3"
+rand = "0.7.3"
+env_logger = "0.8.2"
+
 
 [features]
 http_server = []

+ 6 - 3
rust-lib/flowy-document/src/entities/doc/doc.rs

@@ -20,19 +20,22 @@ pub struct Doc {
 
     #[pb(index = 2)]
     pub data: Vec<u8>,
+
+    #[pb(index = 3)]
+    pub revision: i64,
 }
 
 #[derive(ProtoBuf, Default, Debug, Clone)]
-pub struct SaveDocParams {
+pub struct UpdateDocParams {
     #[pb(index = 1)]
     pub id: String,
 
     #[pb(index = 2)]
-    pub data: Vec<u8>,
+    pub doc_data: Vec<u8>,
 }
 
 #[derive(ProtoBuf, Default, Debug, Clone)]
-pub struct ApplyChangesetParams {
+pub struct DocChangeset {
     #[pb(index = 1)]
     pub id: String,
 

+ 15 - 0
rust-lib/flowy-document/src/errors.rs

@@ -41,6 +41,9 @@ impl DocError {
     static_doc_error!(not_found, ErrorCode::DocNotfound);
     static_doc_error!(unauthorized, ErrorCode::UserUnauthorized);
     static_doc_error!(ws, ErrorCode::WsConnectError);
+    static_doc_error!(undo, ErrorCode::UndoFail);
+    static_doc_error!(redo, ErrorCode::RedoFail);
+    static_doc_error!(out_of_bound, ErrorCode::OutOfBound);
 }
 
 pub fn internal_error<T>(e: T) -> DocError
@@ -61,6 +64,14 @@ pub enum ErrorCode {
     #[display(fmt = "Document websocket error")]
     WsConnectError   = 10,
 
+    #[display(fmt = "Undo failed")]
+    UndoFail         = 200,
+    #[display(fmt = "Redo failed")]
+    RedoFail         = 201,
+
+    #[display(fmt = "Interval out of bound")]
+    OutOfBound       = 202,
+
     #[display(fmt = "UserUnauthorized")]
     UserUnauthorized = 999,
 
@@ -89,6 +100,10 @@ impl std::convert::From<std::io::Error> for DocError {
     fn from(error: std::io::Error) -> Self { DocError::internal().context(error) }
 }
 
+impl std::convert::From<serde_json::Error> for DocError {
+    fn from(error: serde_json::Error) -> Self { DocError::internal().context(error) }
+}
+
 // impl std::convert::From<::r2d2::Error> for DocError {
 //     fn from(error: r2d2::Error) -> Self {
 // ErrorBuilder::new(ErrorCode::InternalError).error(error).build() } }

+ 1 - 1
rust-lib/flowy-document/src/lib.rs

@@ -3,7 +3,7 @@ pub mod errors;
 pub mod module;
 mod observable;
 pub mod protobuf;
-mod services;
+pub mod services;
 mod sql_tables;
 
 #[macro_use]

+ 16 - 27
rust-lib/flowy-document/src/module.rs

@@ -1,7 +1,7 @@
 use crate::{
-    entities::doc::{ApplyChangesetParams, CreateDocParams, Doc, QueryDocParams},
+    entities::doc::{CreateDocParams, Doc, DocChangeset, QueryDocParams},
     errors::DocError,
-    services::{doc_controller::DocController, open_doc::OpenedDocManager, server::construct_doc_server, ws::WsManager},
+    services::{doc_controller::DocController, server::construct_doc_server, ws::WsManager},
 };
 use bytes::Bytes;
 use diesel::SqliteConnection;
@@ -17,16 +17,13 @@ pub trait DocumentUser: Send + Sync {
 
 pub struct FlowyDocument {
     controller: Arc<DocController>,
-    doc_manager: Arc<OpenedDocManager>,
 }
 
 impl FlowyDocument {
     pub fn new(user: Arc<dyn DocumentUser>, ws_manager: Arc<RwLock<WsManager>>) -> FlowyDocument {
         let server = construct_doc_server();
-        let controller = Arc::new(DocController::new(server.clone(), user.clone()));
-        let doc_manager = Arc::new(OpenedDocManager::new(ws_manager, controller.clone()));
-
-        Self { controller, doc_manager }
+        let controller = Arc::new(DocController::new(server.clone(), user.clone(), ws_manager.clone()));
+        Self { controller }
     }
 
     pub fn create(&self, params: CreateDocParams, conn: &SqliteConnection) -> Result<(), DocError> {
@@ -35,31 +32,23 @@ impl FlowyDocument {
     }
 
     pub fn delete(&self, params: QueryDocParams, conn: &SqliteConnection) -> Result<(), DocError> {
-        let _ = self.doc_manager.close(&params.doc_id)?;
-        let _ = self.controller.delete(params.into(), conn)?;
+        let _ = self.controller.delete(params, conn)?;
         Ok(())
     }
 
     pub async fn open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> {
-        let doc = match self.doc_manager.is_opened(&params.doc_id) {
-            true => {
-                let data = self.doc_manager.read_doc(&params.doc_id).await?;
-                Doc { id: params.doc_id, data }
-            },
-            false => {
-                let doc = self.controller.open(params, pool).await?;
-                let _ = self.doc_manager.open(&doc.id, doc.data.clone())?;
-                doc
-            },
-        };
-
-        Ok(doc)
+        let open_doc = self.controller.open(params, pool).await?;
+        Ok(open_doc.doc())
     }
 
-    pub async fn apply_changeset(&self, params: ApplyChangesetParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> {
-        let _ = self.doc_manager.apply_changeset(&params.id, Bytes::from(params.data), pool).await?;
-        let data = self.doc_manager.read_doc(&params.id).await?;
-        let doc = Doc { id: params.id, data };
-        Ok(doc)
+    pub async fn apply_changeset(&self, params: DocChangeset, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> {
+        // let _ = self.doc_manager.apply_changeset(&params.id,
+        // Bytes::from(params.data), pool).await?;
+        //
+        // // workaround: compare the rust's delta with flutter's delta. Will be removed
+        // // very soon
+        // let doc = self.doc_manager.read_doc(&params.id)?;
+        // Ok(doc)
+        unimplemented!()
     }
 }

+ 137 - 98
rust-lib/flowy-document/src/protobuf/model/doc.rs

@@ -229,6 +229,7 @@ pub struct Doc {
     // message fields
     pub id: ::std::string::String,
     pub data: ::std::vec::Vec<u8>,
+    pub revision: i64,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
@@ -296,6 +297,21 @@ impl Doc {
     pub fn take_data(&mut self) -> ::std::vec::Vec<u8> {
         ::std::mem::replace(&mut self.data, ::std::vec::Vec::new())
     }
+
+    // int64 revision = 3;
+
+
+    pub fn get_revision(&self) -> i64 {
+        self.revision
+    }
+    pub fn clear_revision(&mut self) {
+        self.revision = 0;
+    }
+
+    // Param is passed by value, moved
+    pub fn set_revision(&mut self, v: i64) {
+        self.revision = v;
+    }
 }
 
 impl ::protobuf::Message for Doc {
@@ -313,6 +329,13 @@ impl ::protobuf::Message for Doc {
                 2 => {
                     ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.data)?;
                 },
+                3 => {
+                    if wire_type != ::protobuf::wire_format::WireTypeVarint {
+                        return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type));
+                    }
+                    let tmp = is.read_int64()?;
+                    self.revision = tmp;
+                },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
                 },
@@ -331,6 +354,9 @@ impl ::protobuf::Message for Doc {
         if !self.data.is_empty() {
             my_size += ::protobuf::rt::bytes_size(2, &self.data);
         }
+        if self.revision != 0 {
+            my_size += ::protobuf::rt::value_size(3, self.revision, ::protobuf::wire_format::WireTypeVarint);
+        }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
         my_size
@@ -343,6 +369,9 @@ impl ::protobuf::Message for Doc {
         if !self.data.is_empty() {
             os.write_bytes(2, &self.data)?;
         }
+        if self.revision != 0 {
+            os.write_int64(3, self.revision)?;
+        }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
     }
@@ -391,6 +420,11 @@ impl ::protobuf::Message for Doc {
                 |m: &Doc| { &m.data },
                 |m: &mut Doc| { &mut m.data },
             ));
+            fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeInt64>(
+                "revision",
+                |m: &Doc| { &m.revision },
+                |m: &mut Doc| { &mut m.revision },
+            ));
             ::protobuf::reflect::MessageDescriptor::new_pb_name::<Doc>(
                 "Doc",
                 fields,
@@ -409,6 +443,7 @@ impl ::protobuf::Clear for Doc {
     fn clear(&mut self) {
         self.id.clear();
         self.data.clear();
+        self.revision = 0;
         self.unknown_fields.clear();
     }
 }
@@ -426,23 +461,23 @@ impl ::protobuf::reflect::ProtobufValue for Doc {
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct SaveDocParams {
+pub struct UpdateDocParams {
     // message fields
     pub id: ::std::string::String,
-    pub data: ::std::vec::Vec<u8>,
+    pub doc_data: ::std::vec::Vec<u8>,
     // special fields
     pub unknown_fields: ::protobuf::UnknownFields,
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a SaveDocParams {
-    fn default() -> &'a SaveDocParams {
-        <SaveDocParams as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a UpdateDocParams {
+    fn default() -> &'a UpdateDocParams {
+        <UpdateDocParams as ::protobuf::Message>::default_instance()
     }
 }
 
-impl SaveDocParams {
-    pub fn new() -> SaveDocParams {
+impl UpdateDocParams {
+    pub fn new() -> UpdateDocParams {
         ::std::default::Default::default()
     }
 
@@ -472,34 +507,34 @@ impl SaveDocParams {
         ::std::mem::replace(&mut self.id, ::std::string::String::new())
     }
 
-    // bytes data = 2;
+    // bytes doc_data = 2;
 
 
-    pub fn get_data(&self) -> &[u8] {
-        &self.data
+    pub fn get_doc_data(&self) -> &[u8] {
+        &self.doc_data
     }
-    pub fn clear_data(&mut self) {
-        self.data.clear();
+    pub fn clear_doc_data(&mut self) {
+        self.doc_data.clear();
     }
 
     // Param is passed by value, moved
-    pub fn set_data(&mut self, v: ::std::vec::Vec<u8>) {
-        self.data = v;
+    pub fn set_doc_data(&mut self, v: ::std::vec::Vec<u8>) {
+        self.doc_data = v;
     }
 
     // Mutable pointer to the field.
     // If field is not initialized, it is initialized with default value first.
-    pub fn mut_data(&mut self) -> &mut ::std::vec::Vec<u8> {
-        &mut self.data
+    pub fn mut_doc_data(&mut self) -> &mut ::std::vec::Vec<u8> {
+        &mut self.doc_data
     }
 
     // Take field
-    pub fn take_data(&mut self) -> ::std::vec::Vec<u8> {
-        ::std::mem::replace(&mut self.data, ::std::vec::Vec::new())
+    pub fn take_doc_data(&mut self) -> ::std::vec::Vec<u8> {
+        ::std::mem::replace(&mut self.doc_data, ::std::vec::Vec::new())
     }
 }
 
-impl ::protobuf::Message for SaveDocParams {
+impl ::protobuf::Message for UpdateDocParams {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -512,7 +547,7 @@ impl ::protobuf::Message for SaveDocParams {
                     ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?;
                 },
                 2 => {
-                    ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.data)?;
+                    ::protobuf::rt::read_singular_proto3_bytes_into(wire_type, is, &mut self.doc_data)?;
                 },
                 _ => {
                     ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;
@@ -529,8 +564,8 @@ impl ::protobuf::Message for SaveDocParams {
         if !self.id.is_empty() {
             my_size += ::protobuf::rt::string_size(1, &self.id);
         }
-        if !self.data.is_empty() {
-            my_size += ::protobuf::rt::bytes_size(2, &self.data);
+        if !self.doc_data.is_empty() {
+            my_size += ::protobuf::rt::bytes_size(2, &self.doc_data);
         }
         my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields());
         self.cached_size.set(my_size);
@@ -541,8 +576,8 @@ impl ::protobuf::Message for SaveDocParams {
         if !self.id.is_empty() {
             os.write_string(1, &self.id)?;
         }
-        if !self.data.is_empty() {
-            os.write_bytes(2, &self.data)?;
+        if !self.doc_data.is_empty() {
+            os.write_bytes(2, &self.doc_data)?;
         }
         os.write_unknown_fields(self.get_unknown_fields())?;
         ::std::result::Result::Ok(())
@@ -574,8 +609,8 @@ impl ::protobuf::Message for SaveDocParams {
         Self::descriptor_static()
     }
 
-    fn new() -> SaveDocParams {
-        SaveDocParams::new()
+    fn new() -> UpdateDocParams {
+        UpdateDocParams::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -584,50 +619,50 @@ impl ::protobuf::Message for SaveDocParams {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "id",
-                |m: &SaveDocParams| { &m.id },
-                |m: &mut SaveDocParams| { &mut m.id },
+                |m: &UpdateDocParams| { &m.id },
+                |m: &mut UpdateDocParams| { &mut m.id },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
-                "data",
-                |m: &SaveDocParams| { &m.data },
-                |m: &mut SaveDocParams| { &mut m.data },
+                "doc_data",
+                |m: &UpdateDocParams| { &m.doc_data },
+                |m: &mut UpdateDocParams| { &mut m.doc_data },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<SaveDocParams>(
-                "SaveDocParams",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<UpdateDocParams>(
+                "UpdateDocParams",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static SaveDocParams {
-        static instance: ::protobuf::rt::LazyV2<SaveDocParams> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(SaveDocParams::new)
+    fn default_instance() -> &'static UpdateDocParams {
+        static instance: ::protobuf::rt::LazyV2<UpdateDocParams> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(UpdateDocParams::new)
     }
 }
 
-impl ::protobuf::Clear for SaveDocParams {
+impl ::protobuf::Clear for UpdateDocParams {
     fn clear(&mut self) {
         self.id.clear();
-        self.data.clear();
+        self.doc_data.clear();
         self.unknown_fields.clear();
     }
 }
 
-impl ::std::fmt::Debug for SaveDocParams {
+impl ::std::fmt::Debug for UpdateDocParams {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for SaveDocParams {
+impl ::protobuf::reflect::ProtobufValue for UpdateDocParams {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
 }
 
 #[derive(PartialEq,Clone,Default)]
-pub struct ApplyChangesetParams {
+pub struct DocChangeset {
     // message fields
     pub id: ::std::string::String,
     pub data: ::std::vec::Vec<u8>,
@@ -636,14 +671,14 @@ pub struct ApplyChangesetParams {
     pub cached_size: ::protobuf::CachedSize,
 }
 
-impl<'a> ::std::default::Default for &'a ApplyChangesetParams {
-    fn default() -> &'a ApplyChangesetParams {
-        <ApplyChangesetParams as ::protobuf::Message>::default_instance()
+impl<'a> ::std::default::Default for &'a DocChangeset {
+    fn default() -> &'a DocChangeset {
+        <DocChangeset as ::protobuf::Message>::default_instance()
     }
 }
 
-impl ApplyChangesetParams {
-    pub fn new() -> ApplyChangesetParams {
+impl DocChangeset {
+    pub fn new() -> DocChangeset {
         ::std::default::Default::default()
     }
 
@@ -700,7 +735,7 @@ impl ApplyChangesetParams {
     }
 }
 
-impl ::protobuf::Message for ApplyChangesetParams {
+impl ::protobuf::Message for DocChangeset {
     fn is_initialized(&self) -> bool {
         true
     }
@@ -775,8 +810,8 @@ impl ::protobuf::Message for ApplyChangesetParams {
         Self::descriptor_static()
     }
 
-    fn new() -> ApplyChangesetParams {
-        ApplyChangesetParams::new()
+    fn new() -> DocChangeset {
+        DocChangeset::new()
     }
 
     fn descriptor_static() -> &'static ::protobuf::reflect::MessageDescriptor {
@@ -785,29 +820,29 @@ impl ::protobuf::Message for ApplyChangesetParams {
             let mut fields = ::std::vec::Vec::new();
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>(
                 "id",
-                |m: &ApplyChangesetParams| { &m.id },
-                |m: &mut ApplyChangesetParams| { &mut m.id },
+                |m: &DocChangeset| { &m.id },
+                |m: &mut DocChangeset| { &mut m.id },
             ));
             fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeBytes>(
                 "data",
-                |m: &ApplyChangesetParams| { &m.data },
-                |m: &mut ApplyChangesetParams| { &mut m.data },
+                |m: &DocChangeset| { &m.data },
+                |m: &mut DocChangeset| { &mut m.data },
             ));
-            ::protobuf::reflect::MessageDescriptor::new_pb_name::<ApplyChangesetParams>(
-                "ApplyChangesetParams",
+            ::protobuf::reflect::MessageDescriptor::new_pb_name::<DocChangeset>(
+                "DocChangeset",
                 fields,
                 file_descriptor_proto()
             )
         })
     }
 
-    fn default_instance() -> &'static ApplyChangesetParams {
-        static instance: ::protobuf::rt::LazyV2<ApplyChangesetParams> = ::protobuf::rt::LazyV2::INIT;
-        instance.get(ApplyChangesetParams::new)
+    fn default_instance() -> &'static DocChangeset {
+        static instance: ::protobuf::rt::LazyV2<DocChangeset> = ::protobuf::rt::LazyV2::INIT;
+        instance.get(DocChangeset::new)
     }
 }
 
-impl ::protobuf::Clear for ApplyChangesetParams {
+impl ::protobuf::Clear for DocChangeset {
     fn clear(&mut self) {
         self.id.clear();
         self.data.clear();
@@ -815,13 +850,13 @@ impl ::protobuf::Clear for ApplyChangesetParams {
     }
 }
 
-impl ::std::fmt::Debug for ApplyChangesetParams {
+impl ::std::fmt::Debug for DocChangeset {
     fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
         ::protobuf::text_format::fmt(self, f)
     }
 }
 
-impl ::protobuf::reflect::ProtobufValue for ApplyChangesetParams {
+impl ::protobuf::reflect::ProtobufValue for DocChangeset {
     fn as_ref(&self) -> ::protobuf::reflect::ReflectValueRef {
         ::protobuf::reflect::ReflectValueRef::Message(self)
     }
@@ -988,44 +1023,48 @@ impl ::protobuf::reflect::ProtobufValue for QueryDocParams {
 
 static file_descriptor_proto_data: &'static [u8] = b"\
     \n\tdoc.proto\"5\n\x0fCreateDocParams\x12\x0e\n\x02id\x18\x01\x20\x01(\t\
-    R\x02id\x12\x12\n\x04data\x18\x02\x20\x01(\x0cR\x04data\")\n\x03Doc\x12\
+    R\x02id\x12\x12\n\x04data\x18\x02\x20\x01(\x0cR\x04data\"E\n\x03Doc\x12\
+    \x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04data\x18\x02\x20\x01\
+    (\x0cR\x04data\x12\x1a\n\x08revision\x18\x03\x20\x01(\x03R\x08revision\"\
+    <\n\x0fUpdateDocParams\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x19\
+    \n\x08doc_data\x18\x02\x20\x01(\x0cR\x07docData\"2\n\x0cDocChangeset\x12\
     \x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04data\x18\x02\x20\x01\
-    (\x0cR\x04data\"3\n\rSaveDocParams\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\
-    \x02id\x12\x12\n\x04data\x18\x02\x20\x01(\x0cR\x04data\":\n\x14ApplyChan\
-    gesetParams\x12\x0e\n\x02id\x18\x01\x20\x01(\tR\x02id\x12\x12\n\x04data\
-    \x18\x02\x20\x01(\x0cR\x04data\"'\n\x0eQueryDocParams\x12\x15\n\x06doc_i\
-    d\x18\x01\x20\x01(\tR\x05docIdJ\xf9\x04\n\x06\x12\x04\0\0\x14\x01\n\x08\
-    \n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\
-    \x03\x04\0\x01\x12\x03\x02\x08\x17\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\
-    \x04\x12\n\x0c\n\x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\
-    \x02\0\x01\x12\x03\x03\x0b\r\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x10\
-    \x11\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x13\n\x0c\n\x05\x04\0\x02\
-    \x01\x05\x12\x03\x04\x04\t\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\n\
-    \x0e\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x04\x01\
-    \x12\x04\x06\0\t\x01\n\n\n\x03\x04\x01\x01\x12\x03\x06\x08\x0b\n\x0b\n\
-    \x04\x04\x01\x02\0\x12\x03\x07\x04\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\
-    \x03\x07\x04\n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x07\x0b\r\n\x0c\n\
-    \x05\x04\x01\x02\0\x03\x12\x03\x07\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\
-    \x12\x03\x08\x04\x13\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x08\x04\t\n\
-    \x0c\n\x05\x04\x01\x02\x01\x01\x12\x03\x08\n\x0e\n\x0c\n\x05\x04\x01\x02\
-    \x01\x03\x12\x03\x08\x11\x12\n\n\n\x02\x04\x02\x12\x04\n\0\r\x01\n\n\n\
-    \x03\x04\x02\x01\x12\x03\n\x08\x15\n\x0b\n\x04\x04\x02\x02\0\x12\x03\x0b\
-    \x04\x12\n\x0c\n\x05\x04\x02\x02\0\x05\x12\x03\x0b\x04\n\n\x0c\n\x05\x04\
-    \x02\x02\0\x01\x12\x03\x0b\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\
-    \x0b\x10\x11\n\x0b\n\x04\x04\x02\x02\x01\x12\x03\x0c\x04\x13\n\x0c\n\x05\
-    \x04\x02\x02\x01\x05\x12\x03\x0c\x04\t\n\x0c\n\x05\x04\x02\x02\x01\x01\
-    \x12\x03\x0c\n\x0e\n\x0c\n\x05\x04\x02\x02\x01\x03\x12\x03\x0c\x11\x12\n\
-    \n\n\x02\x04\x03\x12\x04\x0e\0\x11\x01\n\n\n\x03\x04\x03\x01\x12\x03\x0e\
-    \x08\x1c\n\x0b\n\x04\x04\x03\x02\0\x12\x03\x0f\x04\x12\n\x0c\n\x05\x04\
-    \x03\x02\0\x05\x12\x03\x0f\x04\n\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\
-    \x0f\x0b\r\n\x0c\n\x05\x04\x03\x02\0\x03\x12\x03\x0f\x10\x11\n\x0b\n\x04\
-    \x04\x03\x02\x01\x12\x03\x10\x04\x13\n\x0c\n\x05\x04\x03\x02\x01\x05\x12\
-    \x03\x10\x04\t\n\x0c\n\x05\x04\x03\x02\x01\x01\x12\x03\x10\n\x0e\n\x0c\n\
-    \x05\x04\x03\x02\x01\x03\x12\x03\x10\x11\x12\n\n\n\x02\x04\x04\x12\x04\
-    \x12\0\x14\x01\n\n\n\x03\x04\x04\x01\x12\x03\x12\x08\x16\n\x0b\n\x04\x04\
-    \x04\x02\0\x12\x03\x13\x04\x16\n\x0c\n\x05\x04\x04\x02\0\x05\x12\x03\x13\
-    \x04\n\n\x0c\n\x05\x04\x04\x02\0\x01\x12\x03\x13\x0b\x11\n\x0c\n\x05\x04\
-    \x04\x02\0\x03\x12\x03\x13\x14\x15b\x06proto3\
+    (\x0cR\x04data\"'\n\x0eQueryDocParams\x12\x15\n\x06doc_id\x18\x01\x20\
+    \x01(\tR\x05docIdJ\xb0\x05\n\x06\x12\x04\0\0\x15\x01\n\x08\n\x01\x0c\x12\
+    \x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\x04\0\x01\
+    \x12\x03\x02\x08\x17\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x12\n\x0c\n\
+    \x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\
+    \x03\x03\x0b\r\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x10\x11\n\x0b\n\
+    \x04\x04\0\x02\x01\x12\x03\x04\x04\x13\n\x0c\n\x05\x04\0\x02\x01\x05\x12\
+    \x03\x04\x04\t\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\n\x0e\n\x0c\n\
+    \x05\x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x04\x01\x12\x04\x06\
+    \0\n\x01\n\n\n\x03\x04\x01\x01\x12\x03\x06\x08\x0b\n\x0b\n\x04\x04\x01\
+    \x02\0\x12\x03\x07\x04\x12\n\x0c\n\x05\x04\x01\x02\0\x05\x12\x03\x07\x04\
+    \n\n\x0c\n\x05\x04\x01\x02\0\x01\x12\x03\x07\x0b\r\n\x0c\n\x05\x04\x01\
+    \x02\0\x03\x12\x03\x07\x10\x11\n\x0b\n\x04\x04\x01\x02\x01\x12\x03\x08\
+    \x04\x13\n\x0c\n\x05\x04\x01\x02\x01\x05\x12\x03\x08\x04\t\n\x0c\n\x05\
+    \x04\x01\x02\x01\x01\x12\x03\x08\n\x0e\n\x0c\n\x05\x04\x01\x02\x01\x03\
+    \x12\x03\x08\x11\x12\n\x0b\n\x04\x04\x01\x02\x02\x12\x03\t\x04\x17\n\x0c\
+    \n\x05\x04\x01\x02\x02\x05\x12\x03\t\x04\t\n\x0c\n\x05\x04\x01\x02\x02\
+    \x01\x12\x03\t\n\x12\n\x0c\n\x05\x04\x01\x02\x02\x03\x12\x03\t\x15\x16\n\
+    \n\n\x02\x04\x02\x12\x04\x0b\0\x0e\x01\n\n\n\x03\x04\x02\x01\x12\x03\x0b\
+    \x08\x17\n\x0b\n\x04\x04\x02\x02\0\x12\x03\x0c\x04\x12\n\x0c\n\x05\x04\
+    \x02\x02\0\x05\x12\x03\x0c\x04\n\n\x0c\n\x05\x04\x02\x02\0\x01\x12\x03\
+    \x0c\x0b\r\n\x0c\n\x05\x04\x02\x02\0\x03\x12\x03\x0c\x10\x11\n\x0b\n\x04\
+    \x04\x02\x02\x01\x12\x03\r\x04\x17\n\x0c\n\x05\x04\x02\x02\x01\x05\x12\
+    \x03\r\x04\t\n\x0c\n\x05\x04\x02\x02\x01\x01\x12\x03\r\n\x12\n\x0c\n\x05\
+    \x04\x02\x02\x01\x03\x12\x03\r\x15\x16\n\n\n\x02\x04\x03\x12\x04\x0f\0\
+    \x12\x01\n\n\n\x03\x04\x03\x01\x12\x03\x0f\x08\x14\n\x0b\n\x04\x04\x03\
+    \x02\0\x12\x03\x10\x04\x12\n\x0c\n\x05\x04\x03\x02\0\x05\x12\x03\x10\x04\
+    \n\n\x0c\n\x05\x04\x03\x02\0\x01\x12\x03\x10\x0b\r\n\x0c\n\x05\x04\x03\
+    \x02\0\x03\x12\x03\x10\x10\x11\n\x0b\n\x04\x04\x03\x02\x01\x12\x03\x11\
+    \x04\x13\n\x0c\n\x05\x04\x03\x02\x01\x05\x12\x03\x11\x04\t\n\x0c\n\x05\
+    \x04\x03\x02\x01\x01\x12\x03\x11\n\x0e\n\x0c\n\x05\x04\x03\x02\x01\x03\
+    \x12\x03\x11\x11\x12\n\n\n\x02\x04\x04\x12\x04\x13\0\x15\x01\n\n\n\x03\
+    \x04\x04\x01\x12\x03\x13\x08\x16\n\x0b\n\x04\x04\x04\x02\0\x12\x03\x14\
+    \x04\x16\n\x0c\n\x05\x04\x04\x02\0\x05\x12\x03\x14\x04\n\n\x0c\n\x05\x04\
+    \x04\x02\0\x01\x12\x03\x14\x0b\x11\n\x0c\n\x05\x04\x04\x02\0\x03\x12\x03\
+    \x14\x14\x15b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 39 - 23
rust-lib/flowy-document/src/protobuf/model/errors.rs

@@ -218,6 +218,9 @@ pub enum ErrorCode {
     DocIdInvalid = 0,
     DocNotfound = 1,
     WsConnectError = 10,
+    UndoFail = 200,
+    RedoFail = 201,
+    OutOfBound = 202,
     UserUnauthorized = 999,
     InternalError = 1000,
 }
@@ -232,6 +235,9 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
             0 => ::std::option::Option::Some(ErrorCode::DocIdInvalid),
             1 => ::std::option::Option::Some(ErrorCode::DocNotfound),
             10 => ::std::option::Option::Some(ErrorCode::WsConnectError),
+            200 => ::std::option::Option::Some(ErrorCode::UndoFail),
+            201 => ::std::option::Option::Some(ErrorCode::RedoFail),
+            202 => ::std::option::Option::Some(ErrorCode::OutOfBound),
             999 => ::std::option::Option::Some(ErrorCode::UserUnauthorized),
             1000 => ::std::option::Option::Some(ErrorCode::InternalError),
             _ => ::std::option::Option::None
@@ -243,6 +249,9 @@ impl ::protobuf::ProtobufEnum for ErrorCode {
             ErrorCode::DocIdInvalid,
             ErrorCode::DocNotfound,
             ErrorCode::WsConnectError,
+            ErrorCode::UndoFail,
+            ErrorCode::RedoFail,
+            ErrorCode::OutOfBound,
             ErrorCode::UserUnauthorized,
             ErrorCode::InternalError,
         ];
@@ -274,29 +283,36 @@ impl ::protobuf::reflect::ProtobufValue for ErrorCode {
 
 static file_descriptor_proto_data: &'static [u8] = b"\
     \n\x0cerrors.proto\"<\n\x08DocError\x12\x1e\n\x04code\x18\x01\x20\x01(\
-    \x0e2\n.ErrorCodeR\x04code\x12\x10\n\x03msg\x18\x02\x20\x01(\tR\x03msg*m\
-    \n\tErrorCode\x12\x10\n\x0cDocIdInvalid\x10\0\x12\x0f\n\x0bDocNotfound\
-    \x10\x01\x12\x12\n\x0eWsConnectError\x10\n\x12\x15\n\x10UserUnauthorized\
-    \x10\xe7\x07\x12\x12\n\rInternalError\x10\xe8\x07J\xfd\x02\n\x06\x12\x04\
-    \0\0\x0c\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\
-    \0\x05\x01\n\n\n\x03\x04\0\x01\x12\x03\x02\x08\x10\n\x0b\n\x04\x04\0\x02\
-    \0\x12\x03\x03\x04\x17\n\x0c\n\x05\x04\0\x02\0\x06\x12\x03\x03\x04\r\n\
-    \x0c\n\x05\x04\0\x02\0\x01\x12\x03\x03\x0e\x12\n\x0c\n\x05\x04\0\x02\0\
-    \x03\x12\x03\x03\x15\x16\n\x0b\n\x04\x04\0\x02\x01\x12\x03\x04\x04\x13\n\
-    \x0c\n\x05\x04\0\x02\x01\x05\x12\x03\x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\
-    \x01\x12\x03\x04\x0b\x0e\n\x0c\n\x05\x04\0\x02\x01\x03\x12\x03\x04\x11\
-    \x12\n\n\n\x02\x05\0\x12\x04\x06\0\x0c\x01\n\n\n\x03\x05\0\x01\x12\x03\
-    \x06\x05\x0e\n\x0b\n\x04\x05\0\x02\0\x12\x03\x07\x04\x15\n\x0c\n\x05\x05\
-    \0\x02\0\x01\x12\x03\x07\x04\x10\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x07\
-    \x13\x14\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x08\x04\x14\n\x0c\n\x05\x05\0\
-    \x02\x01\x01\x12\x03\x08\x04\x0f\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\
-    \x08\x12\x13\n\x0b\n\x04\x05\0\x02\x02\x12\x03\t\x04\x18\n\x0c\n\x05\x05\
-    \0\x02\x02\x01\x12\x03\t\x04\x12\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\t\
-    \x15\x17\n\x0b\n\x04\x05\0\x02\x03\x12\x03\n\x04\x1b\n\x0c\n\x05\x05\0\
-    \x02\x03\x01\x12\x03\n\x04\x14\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\n\
-    \x17\x1a\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x0b\x04\x19\n\x0c\n\x05\x05\0\
-    \x02\x04\x01\x12\x03\x0b\x04\x11\n\x0c\n\x05\x05\0\x02\x04\x02\x12\x03\
-    \x0b\x14\x18b\x06proto3\
+    \x0e2\n.ErrorCodeR\x04code\x12\x10\n\x03msg\x18\x02\x20\x01(\tR\x03msg*\
+    \x9c\x01\n\tErrorCode\x12\x10\n\x0cDocIdInvalid\x10\0\x12\x0f\n\x0bDocNo\
+    tfound\x10\x01\x12\x12\n\x0eWsConnectError\x10\n\x12\r\n\x08UndoFail\x10\
+    \xc8\x01\x12\r\n\x08RedoFail\x10\xc9\x01\x12\x0f\n\nOutOfBound\x10\xca\
+    \x01\x12\x15\n\x10UserUnauthorized\x10\xe7\x07\x12\x12\n\rInternalError\
+    \x10\xe8\x07J\xf8\x03\n\x06\x12\x04\0\0\x0f\x01\n\x08\n\x01\x0c\x12\x03\
+    \0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x05\x01\n\n\n\x03\x04\0\x01\x12\
+    \x03\x02\x08\x10\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x17\n\x0c\n\x05\
+    \x04\0\x02\0\x06\x12\x03\x03\x04\r\n\x0c\n\x05\x04\0\x02\0\x01\x12\x03\
+    \x03\x0e\x12\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x15\x16\n\x0b\n\x04\
+    \x04\0\x02\x01\x12\x03\x04\x04\x13\n\x0c\n\x05\x04\0\x02\x01\x05\x12\x03\
+    \x04\x04\n\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\x0b\x0e\n\x0c\n\x05\
+    \x04\0\x02\x01\x03\x12\x03\x04\x11\x12\n\n\n\x02\x05\0\x12\x04\x06\0\x0f\
+    \x01\n\n\n\x03\x05\0\x01\x12\x03\x06\x05\x0e\n\x0b\n\x04\x05\0\x02\0\x12\
+    \x03\x07\x04\x15\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x07\x04\x10\n\x0c\n\
+    \x05\x05\0\x02\0\x02\x12\x03\x07\x13\x14\n\x0b\n\x04\x05\0\x02\x01\x12\
+    \x03\x08\x04\x14\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x08\x04\x0f\n\x0c\
+    \n\x05\x05\0\x02\x01\x02\x12\x03\x08\x12\x13\n\x0b\n\x04\x05\0\x02\x02\
+    \x12\x03\t\x04\x18\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\t\x04\x12\n\x0c\
+    \n\x05\x05\0\x02\x02\x02\x12\x03\t\x15\x17\n\x0b\n\x04\x05\0\x02\x03\x12\
+    \x03\n\x04\x13\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\n\x04\x0c\n\x0c\n\
+    \x05\x05\0\x02\x03\x02\x12\x03\n\x0f\x12\n\x0b\n\x04\x05\0\x02\x04\x12\
+    \x03\x0b\x04\x13\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x0b\x04\x0c\n\x0c\
+    \n\x05\x05\0\x02\x04\x02\x12\x03\x0b\x0f\x12\n\x0b\n\x04\x05\0\x02\x05\
+    \x12\x03\x0c\x04\x15\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x0c\x04\x0e\n\
+    \x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x0c\x11\x14\n\x0b\n\x04\x05\0\x02\
+    \x06\x12\x03\r\x04\x1b\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\r\x04\x14\n\
+    \x0c\n\x05\x05\0\x02\x06\x02\x12\x03\r\x17\x1a\n\x0b\n\x04\x05\0\x02\x07\
+    \x12\x03\x0e\x04\x19\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\x0e\x04\x11\n\
+    \x0c\n\x05\x05\0\x02\x07\x02\x12\x03\x0e\x14\x18b\x06proto3\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 4 - 3
rust-lib/flowy-document/src/protobuf/proto/doc.proto

@@ -7,12 +7,13 @@ message CreateDocParams {
 message Doc {
     string id = 1;
     bytes data = 2;
+    int64 revision = 3;
 }
-message SaveDocParams {
+message UpdateDocParams {
     string id = 1;
-    bytes data = 2;
+    bytes doc_data = 2;
 }
-message ApplyChangesetParams {
+message DocChangeset {
     string id = 1;
     bytes data = 2;
 }

+ 3 - 0
rust-lib/flowy-document/src/protobuf/proto/errors.proto

@@ -8,6 +8,9 @@ enum ErrorCode {
     DocIdInvalid = 0;
     DocNotfound = 1;
     WsConnectError = 10;
+    UndoFail = 200;
+    RedoFail = 201;
+    OutOfBound = 202;
     UserUnauthorized = 999;
     InternalError = 1000;
 }

+ 48 - 0
rust-lib/flowy-document/src/services/cache.rs

@@ -0,0 +1,48 @@
+use crate::{
+    entities::doc::Doc,
+    errors::DocError,
+    services::{
+        open_doc::{DocId, OpenedDoc},
+        ws::WsManager,
+    },
+};
+use bytes::Bytes;
+use dashmap::DashMap;
+use flowy_database::ConnectionPool;
+use flowy_ot::{core::Delta, errors::OTError};
+use parking_lot::RwLock;
+use std::{convert::TryInto, fmt::Debug, sync::Arc};
+
+pub(crate) struct DocCache {
+    doc_map: DashMap<DocId, Arc<OpenedDoc>>,
+}
+
+impl DocCache {
+    pub(crate) fn new() -> Self { Self { doc_map: DashMap::new() } }
+
+    pub(crate) fn set(&self, doc: Arc<OpenedDoc>) -> Result<(), DocError> {
+        self.doc_map.insert(doc.id.clone(), doc);
+        Ok(())
+    }
+
+    pub(crate) fn is_opened(&self, doc_id: &str) -> bool {
+        let doc_id: DocId = doc_id.into();
+        self.doc_map.get(&doc_id).is_some()
+    }
+
+    pub(crate) fn get(&self, doc_id: &str) -> Result<Arc<OpenedDoc>, DocError> {
+        if !self.is_opened(&doc_id) {
+            return Err(doc_not_found());
+        }
+        let doc_id: DocId = doc_id.into();
+        let opened_doc = self.doc_map.get(&doc_id).unwrap();
+        Ok(opened_doc.clone())
+    }
+
+    pub(crate) fn remove(&self, id: &str) {
+        let doc_id: DocId = id.into();
+        self.doc_map.remove(&doc_id);
+    }
+}
+
+fn doc_not_found() -> DocError { DocError::not_found().context("Doc is close or you should call open first") }

+ 3 - 3
rust-lib/flowy-ot/src/client/document/data.rs → rust-lib/flowy-document/src/services/doc/document/data.rs

@@ -1,8 +1,8 @@
-use crate::{client::DocumentData, errors::OTError};
+use crate::{errors::DocError, services::doc::DocumentData};
 use serde::{Deserialize, Serialize};
 
 impl<T: AsRef<str>> DocumentData for T {
-    fn into_string(self) -> Result<String, OTError> { Ok(self.as_ref().to_string()) }
+    fn into_string(self) -> Result<String, DocError> { Ok(self.as_ref().to_string()) }
 }
 
 #[derive(Serialize, Deserialize, Debug)]
@@ -11,7 +11,7 @@ pub struct ImageData {
 }
 
 impl DocumentData for ImageData {
-    fn into_string(self) -> Result<String, OTError> {
+    fn into_string(self) -> Result<String, DocError> {
         let s = serde_json::to_string(&self)?;
         Ok(s)
     }

+ 39 - 23
rust-lib/flowy-ot/src/client/document/document.rs → rust-lib/flowy-document/src/services/doc/document/document.rs

@@ -1,12 +1,13 @@
 use crate::{
-    client::{view::View, History, RevId, UndoResult, RECORD_THRESHOLD},
-    core::*,
-    errors::{ErrorBuilder, OTError, OTErrorCode, OTErrorCode::*},
+    errors::DocError,
+    services::doc::{view::View, History, UndoResult, RECORD_THRESHOLD},
 };
+use bytes::Bytes;
+use flowy_ot::core::*;
 use std::convert::TryInto;
 
 pub trait DocumentData {
-    fn into_string(self) -> Result<String, OTError>;
+    fn into_string(self) -> Result<String, DocError>;
 }
 
 pub trait CustomDocument {
@@ -23,6 +24,19 @@ impl CustomDocument for FlowyDoc {
     fn init_delta() -> Delta { DeltaBuilder::new().insert("\n").build() }
 }
 
+#[derive(Debug, Clone)]
+pub struct RevId(pub usize);
+
+#[derive(Debug, Clone)]
+pub struct Revision {
+    rev_id: RevId,
+    pub delta: Delta,
+}
+
+impl Revision {
+    pub fn new(rev_id: RevId, delta: Delta) -> Revision { Self { rev_id, delta } }
+}
+
 pub struct Document {
     delta: Delta,
     history: History,
@@ -44,7 +58,7 @@ impl Document {
         }
     }
 
-    pub fn from_json(json: &str) -> Result<Self, OTError> {
+    pub fn from_json(json: &str) -> Result<Self, DocError> {
         let delta = Delta::from_json(json)?;
         Ok(Self::from_delta(delta))
     }
@@ -55,18 +69,20 @@ impl Document {
 
     pub fn to_string(&self) -> String { self.delta.apply("").unwrap() }
 
-    pub fn apply_changeset<T>(&mut self, changeset: T) -> Result<(), OTError>
-    where
-        T: TryInto<Delta, Error = OTError>,
-    {
-        let new_delta: Delta = changeset.try_into()?;
-        log::debug!("Delta changeset: {}", new_delta);
+    pub fn apply_delta(&mut self, data: Bytes) -> Result<(), DocError> {
+        let new_delta = Delta::from_bytes(data.to_vec())?;
+
+        log::debug!("Apply delta: {}", new_delta);
+
+        let rev_id = self.next_rev_id();
+        let revision = Revision::new(rev_id, new_delta.clone());
+
         let _ = self.add_delta(&new_delta)?;
         log::debug!("Document: {}", self.to_json());
         Ok(())
     }
 
-    pub fn insert<T: DocumentData>(&mut self, index: usize, data: T) -> Result<Delta, OTError> {
+    pub fn insert<T: DocumentData>(&mut self, index: usize, data: T) -> Result<Delta, DocError> {
         let interval = Interval::new(index, index);
         let _ = validate_interval(&self.delta, &interval)?;
 
@@ -77,7 +93,7 @@ impl Document {
         Ok(delta)
     }
 
-    pub fn delete(&mut self, interval: Interval) -> Result<Delta, OTError> {
+    pub fn delete(&mut self, interval: Interval) -> Result<Delta, DocError> {
         let _ = validate_interval(&self.delta, &interval)?;
         debug_assert_eq!(interval.is_empty(), false);
         let delete = self.view.delete(&self.delta, interval)?;
@@ -88,7 +104,7 @@ impl Document {
         Ok(delete)
     }
 
-    pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), OTError> {
+    pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), DocError> {
         let _ = validate_interval(&self.delta, &interval)?;
         log::trace!("format with {} at {}", attribute, interval);
         let format_delta = self.view.format(&self.delta, attribute.clone(), interval).unwrap();
@@ -98,7 +114,7 @@ impl Document {
         Ok(())
     }
 
-    pub fn replace<T: DocumentData>(&mut self, interval: Interval, data: T) -> Result<Delta, OTError> {
+    pub fn replace<T: DocumentData>(&mut self, interval: Interval, data: T) -> Result<Delta, DocError> {
         let _ = validate_interval(&self.delta, &interval)?;
         let mut delta = Delta::default();
         let text = data.into_string()?;
@@ -120,9 +136,9 @@ impl Document {
 
     pub fn can_redo(&self) -> bool { self.history.can_redo() }
 
-    pub fn undo(&mut self) -> Result<UndoResult, OTError> {
+    pub fn undo(&mut self) -> Result<UndoResult, DocError> {
         match self.history.undo() {
-            None => Err(ErrorBuilder::new(UndoFail).msg("Undo stack is empty").build()),
+            None => Err(DocError::undo().context("Undo stack is empty")),
             Some(undo_delta) => {
                 let (new_delta, inverted_delta) = self.invert_change(&undo_delta)?;
                 let result = UndoResult::success(new_delta.target_len as usize);
@@ -134,9 +150,9 @@ impl Document {
         }
     }
 
-    pub fn redo(&mut self) -> Result<UndoResult, OTError> {
+    pub fn redo(&mut self) -> Result<UndoResult, DocError> {
         match self.history.redo() {
-            None => Err(ErrorBuilder::new(RedoFail).build()),
+            None => Err(DocError::redo()),
             Some(redo_delta) => {
                 let (new_delta, inverted_delta) = self.invert_change(&redo_delta)?;
                 let result = UndoResult::success(new_delta.target_len as usize);
@@ -154,7 +170,7 @@ impl Document {
 }
 
 impl Document {
-    fn add_delta(&mut self, delta: &Delta) -> Result<(), OTError> {
+    fn add_delta(&mut self, delta: &Delta) -> Result<(), DocError> {
         let composed_delta = self.delta.compose(delta)?;
         let mut undo_delta = delta.invert(&self.delta);
         self.rev_id_counter += 1;
@@ -181,7 +197,7 @@ impl Document {
         Ok(())
     }
 
-    fn invert_change(&self, change: &Delta) -> Result<(Delta, Delta), OTError> {
+    fn invert_change(&self, change: &Delta) -> Result<(Delta, Delta), DocError> {
         // c = a.compose(b)
         // d = b.invert(a)
         // a = c.compose(d)
@@ -195,10 +211,10 @@ impl Document {
     fn next_rev_id(&self) -> RevId { RevId(self.rev_id_counter) }
 }
 
-fn validate_interval(delta: &Delta, interval: &Interval) -> Result<(), OTError> {
+fn validate_interval(delta: &Delta, interval: &Interval) -> Result<(), DocError> {
     if delta.target_len < interval.end {
         log::error!("{:?} out of bounds. should 0..{}", interval, delta.target_len);
-        return Err(ErrorBuilder::new(OTErrorCode::IntervalOutOfBound).build());
+        return Err(DocError::out_of_bound());
     }
     Ok(())
 }

+ 0 - 0
rust-lib/flowy-ot/src/client/document/mod.rs → rust-lib/flowy-document/src/services/doc/document/mod.rs


+ 0 - 0
rust-lib/flowy-ot/src/client/document/selection.rs → rust-lib/flowy-document/src/services/doc/document/selection.rs


+ 2 - 4
rust-lib/flowy-ot/src/client/extensions/delete/default_delete.rs → rust-lib/flowy-document/src/services/doc/extensions/delete/default_delete.rs

@@ -1,7 +1,5 @@
-use crate::{
-    client::extensions::DeleteExt,
-    core::{Delta, DeltaBuilder, Interval},
-};
+use crate::services::doc::extensions::DeleteExt;
+use flowy_ot::core::{Delta, DeltaBuilder, Interval};
 
 pub struct DefaultDelete {}
 impl DeleteExt for DefaultDelete {

+ 0 - 0
rust-lib/flowy-ot/src/client/extensions/delete/mod.rs → rust-lib/flowy-document/src/services/doc/extensions/delete/mod.rs


+ 2 - 4
rust-lib/flowy-ot/src/client/extensions/delete/preserve_line_format_merge.rs → rust-lib/flowy-document/src/services/doc/extensions/delete/preserve_line_format_merge.rs

@@ -1,7 +1,5 @@
-use crate::{
-    client::{extensions::DeleteExt, util::is_newline},
-    core::{plain_attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval, NEW_LINE},
-};
+use crate::services::doc::{extensions::DeleteExt, util::is_newline};
+use flowy_ot::core::{plain_attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, Interval, NEW_LINE};
 
 pub struct PreserveLineFormatOnMerge {}
 impl DeleteExt for PreserveLineFormatOnMerge {

+ 0 - 0
rust-lib/flowy-ot/src/client/extensions/format/format_at_position.rs → rust-lib/flowy-document/src/services/doc/extensions/format/format_at_position.rs


+ 2 - 4
rust-lib/flowy-ot/src/client/extensions/format/helper.rs → rust-lib/flowy-document/src/services/doc/extensions/format/helper.rs

@@ -1,7 +1,5 @@
-use crate::{
-    client::util::find_newline,
-    core::{plain_attributes, Attribute, AttributeScope, Delta, Operation},
-};
+use crate::services::doc::util::find_newline;
+use flowy_ot::core::{plain_attributes, Attribute, AttributeScope, Delta, Operation};
 
 pub(crate) fn line_break(op: &Operation, attribute: &Attribute, scope: AttributeScope) -> Delta {
     let mut new_delta = Delta::new();

+ 0 - 0
rust-lib/flowy-ot/src/client/extensions/format/mod.rs → rust-lib/flowy-document/src/services/doc/extensions/format/mod.rs


+ 4 - 6
rust-lib/flowy-ot/src/client/extensions/format/resolve_block_format.rs → rust-lib/flowy-document/src/services/doc/extensions/format/resolve_block_format.rs

@@ -1,10 +1,8 @@
-use crate::{
-    client::{
-        extensions::{format::helper::line_break, FormatExt},
-        util::find_newline,
-    },
-    core::{plain_attributes, Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval},
+use crate::services::doc::{
+    extensions::{format::helper::line_break, FormatExt},
+    util::find_newline,
 };
+use flowy_ot::core::{plain_attributes, Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval};
 
 pub struct ResolveBlockFormat {}
 impl FormatExt for ResolveBlockFormat {

+ 4 - 6
rust-lib/flowy-ot/src/client/extensions/format/resolve_inline_format.rs → rust-lib/flowy-document/src/services/doc/extensions/format/resolve_inline_format.rs

@@ -1,10 +1,8 @@
-use crate::{
-    client::{
-        extensions::{format::helper::line_break, FormatExt},
-        util::find_newline,
-    },
-    core::{Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval},
+use crate::services::doc::{
+    extensions::{format::helper::line_break, FormatExt},
+    util::find_newline,
 };
+use flowy_ot::core::{Attribute, AttributeScope, Delta, DeltaBuilder, DeltaIter, Interval};
 
 pub struct ResolveInlineFormat {}
 impl FormatExt for ResolveInlineFormat {

+ 2 - 6
rust-lib/flowy-ot/src/client/extensions/insert/auto_exit_block.rs → rust-lib/flowy-document/src/services/doc/extensions/insert/auto_exit_block.rs

@@ -1,9 +1,5 @@
-use crate::{
-    client::{extensions::InsertExt, util::is_newline},
-    core::{AttributeKey, Delta, DeltaBuilder, DeltaIter},
-};
-
-use crate::core::{attributes_except_header, is_empty_line_at_index};
+use crate::services::doc::{extensions::InsertExt, util::is_newline};
+use flowy_ot::core::{attributes_except_header, is_empty_line_at_index, AttributeKey, Delta, DeltaBuilder, DeltaIter};
 
 pub struct AutoExitBlock {}
 

+ 5 - 9
rust-lib/flowy-ot/src/client/extensions/insert/auto_format.rs → rust-lib/flowy-document/src/services/doc/extensions/insert/auto_format.rs

@@ -1,7 +1,8 @@
-use crate::{
-    client::{extensions::InsertExt, util::is_whitespace},
-    core::{Delta, DeltaIter},
-};
+use crate::services::doc::{extensions::InsertExt, util::is_whitespace};
+use bytecount::num_chars;
+use flowy_ot::core::{plain_attributes, Attribute, Attributes, Delta, DeltaBuilder, DeltaIter};
+use std::cmp::min;
+use url::Url;
 
 pub struct AutoFormatExt {}
 impl InsertExt for AutoFormatExt {
@@ -50,11 +51,6 @@ impl InsertExt for AutoFormatExt {
     }
 }
 
-use crate::core::{plain_attributes, Attribute, Attributes, DeltaBuilder};
-use bytecount::num_chars;
-use std::cmp::min;
-use url::Url;
-
 pub enum AutoFormatter {
     Url(Url),
 }

+ 2 - 4
rust-lib/flowy-ot/src/client/extensions/insert/default_insert.rs → rust-lib/flowy-document/src/services/doc/extensions/insert/default_insert.rs

@@ -1,7 +1,5 @@
-use crate::{
-    client::extensions::InsertExt,
-    core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE},
-};
+use crate::services::doc::extensions::InsertExt;
+use flowy_ot::core::{AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE};
 
 pub struct DefaultInsertAttribute {}
 impl InsertExt for DefaultInsertAttribute {

+ 2 - 2
rust-lib/flowy-ot/src/client/extensions/insert/mod.rs → rust-lib/flowy-document/src/services/doc/extensions/insert/mod.rs

@@ -1,6 +1,8 @@
+use crate::services::doc::extensions::InsertExt;
 pub use auto_exit_block::*;
 pub use auto_format::*;
 pub use default_insert::*;
+use flowy_ot::core::Delta;
 pub use preserve_block_format::*;
 pub use preserve_inline_format::*;
 pub use reset_format_on_new_line::*;
@@ -12,8 +14,6 @@ mod preserve_block_format;
 mod preserve_inline_format;
 mod reset_format_on_new_line;
 
-use crate::{client::extensions::InsertExt, core::Delta};
-
 pub struct InsertEmbedsExt {}
 impl InsertExt for InsertEmbedsExt {
     fn ext_name(&self) -> &str { "InsertEmbedsExt" }

+ 11 - 3
rust-lib/flowy-ot/src/client/extensions/insert/preserve_block_format.rs → rust-lib/flowy-document/src/services/doc/extensions/insert/preserve_block_format.rs

@@ -1,6 +1,14 @@
-use crate::{
-    client::{extensions::InsertExt, util::is_newline},
-    core::{attributes_except_header, plain_attributes, Attribute, AttributeKey, Attributes, Delta, DeltaBuilder, DeltaIter, NEW_LINE},
+use crate::services::doc::{extensions::InsertExt, util::is_newline};
+use flowy_ot::core::{
+    attributes_except_header,
+    plain_attributes,
+    Attribute,
+    AttributeKey,
+    Attributes,
+    Delta,
+    DeltaBuilder,
+    DeltaIter,
+    NEW_LINE,
 };
 
 pub struct PreserveBlockFormatOnInsert {}

+ 4 - 6
rust-lib/flowy-ot/src/client/extensions/insert/preserve_inline_format.rs → rust-lib/flowy-document/src/services/doc/extensions/insert/preserve_inline_format.rs

@@ -1,10 +1,8 @@
-use crate::{
-    client::{
-        extensions::InsertExt,
-        util::{contain_newline, is_newline},
-    },
-    core::{plain_attributes, AttributeKey, Delta, DeltaBuilder, DeltaIter, OpNewline, NEW_LINE},
+use crate::services::doc::{
+    extensions::InsertExt,
+    util::{contain_newline, is_newline},
 };
+use flowy_ot::core::{plain_attributes, AttributeKey, Delta, DeltaBuilder, DeltaIter, OpNewline, NEW_LINE};
 
 pub struct PreserveInlineFormat {}
 impl InsertExt for PreserveInlineFormat {

+ 2 - 4
rust-lib/flowy-ot/src/client/extensions/insert/reset_format_on_new_line.rs → rust-lib/flowy-document/src/services/doc/extensions/insert/reset_format_on_new_line.rs

@@ -1,7 +1,5 @@
-use crate::{
-    client::{extensions::InsertExt, util::is_newline},
-    core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, NEW_LINE},
-};
+use crate::services::doc::{extensions::InsertExt, util::is_newline};
+use flowy_ot::core::{AttributeKey, Attributes, CharMetric, Delta, DeltaBuilder, DeltaIter, NEW_LINE};
 
 pub struct ResetLineFormatOnNewLine {}
 impl InsertExt for ResetLineFormatOnNewLine {

+ 1 - 1
rust-lib/flowy-ot/src/client/extensions/mod.rs → rust-lib/flowy-document/src/services/doc/extensions/mod.rs

@@ -2,7 +2,7 @@ pub use delete::*;
 pub use format::*;
 pub use insert::*;
 
-use crate::core::{Attribute, Delta, Interval};
+use flowy_ot::core::{Attribute, Delta, Interval};
 
 mod delete;
 mod format;

+ 1 - 14
rust-lib/flowy-ot/src/client/history.rs → rust-lib/flowy-document/src/services/doc/history.rs

@@ -1,20 +1,7 @@
-use crate::core::Delta;
+use flowy_ot::core::Delta;
 
 const MAX_UNDOS: usize = 20;
 
-#[derive(Debug, Clone)]
-pub struct RevId(pub usize);
-
-#[derive(Debug, Clone)]
-pub struct Revision {
-    rev_id: RevId,
-    pub delta: Delta,
-}
-
-impl Revision {
-    pub fn new(rev_id: RevId, delta: Delta) -> Revision { Self { rev_id, delta } }
-}
-
 #[derive(Debug, Clone)]
 pub struct UndoResult {
     success: bool,

+ 0 - 0
rust-lib/flowy-ot/src/client/mod.rs → rust-lib/flowy-document/src/services/doc/mod.rs


+ 1 - 1
rust-lib/flowy-ot/src/client/util.rs → rust-lib/flowy-document/src/services/doc/util.rs

@@ -1,4 +1,4 @@
-use crate::core::{NEW_LINE, WHITESPACE};
+use flowy_ot::core::{NEW_LINE, WHITESPACE};
 
 #[inline]
 pub fn find_newline(s: &str) -> Option<usize> {

+ 1 - 1
rust-lib/flowy-ot/src/client/view.rs → rust-lib/flowy-document/src/services/doc/view.rs

@@ -1,5 +1,5 @@
 use super::extensions::*;
-use crate::{
+use flowy_ot::{
     core::{Attribute, Delta, Interval},
     errors::{ErrorBuilder, OTError, OTErrorCode},
 };

+ 76 - 32
rust-lib/flowy-document/src/services/doc_controller.rs

@@ -1,5 +1,5 @@
 use crate::{
-    entities::doc::{CreateDocParams, Doc, QueryDocParams, SaveDocParams},
+    entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams},
     errors::DocError,
     module::DocumentUser,
     services::server::Server,
@@ -7,20 +7,37 @@ use crate::{
 };
 use flowy_database::{ConnectionPool, SqliteConnection};
 
-use crate::{errors::internal_error, services::open_doc::OpenedDocPersistence};
+use crate::{
+    errors::internal_error,
+    services::{
+        cache::DocCache,
+        open_doc::{DocId, OpenedDoc, OpenedDocPersistence},
+        ws::WsManager,
+    },
+};
+use parking_lot::RwLock;
 use std::sync::Arc;
 use tokio::task::JoinHandle;
 
 pub(crate) struct DocController {
     server: Server,
     sql: Arc<DocTableSql>,
+    ws: Arc<RwLock<WsManager>>,
+    cache: Arc<DocCache>,
     user: Arc<dyn DocumentUser>,
 }
 
 impl DocController {
-    pub(crate) fn new(server: Server, user: Arc<dyn DocumentUser>) -> Self {
+    pub(crate) fn new(server: Server, user: Arc<dyn DocumentUser>, ws: Arc<RwLock<WsManager>>) -> Self {
         let sql = Arc::new(DocTableSql {});
-        Self { sql, server, user }
+        let cache = Arc::new(DocCache::new());
+        Self {
+            sql,
+            server,
+            user,
+            ws,
+            cache,
+        }
     }
 
     #[tracing::instrument(skip(self, conn), err)]
@@ -28,22 +45,53 @@ impl DocController {
         let doc = Doc {
             id: params.id,
             data: params.data,
+            revision: 0,
         };
         let _ = self.sql.create_doc_table(DocTable::new(doc), conn)?;
         Ok(())
     }
 
     #[tracing::instrument(level = "debug", skip(self, pool), err)]
-    pub(crate) async fn open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> {
-        match self._open(params.clone(), pool.clone()) {
-            Ok(doc_table) => Ok(doc_table.into()),
-            Err(error) => self.try_read_on_server(params, pool.clone(), error).await,
+    pub(crate) async fn open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Arc<OpenedDoc>, DocError> {
+        if self.cache.is_opened(&params.doc_id) == false {
+            return match self._open(params.clone(), pool.clone()) {
+                Ok(doc) => Ok(doc),
+                Err(error) => Err(error),
+            };
         }
+
+        let doc = self.cache.get(&params.doc_id)?;
+        Ok(doc)
     }
 
+    pub(crate) fn close(&self, doc_id: &str) -> Result<(), DocError> {
+        self.cache.remove(doc_id);
+        self.ws.write().remove_handler(doc_id);
+        Ok(())
+    }
+
+    // #[tracing::instrument(level = "debug", skip(self, changeset, pool), err)]
+    // pub(crate) async fn apply_changeset<T>(&self, id: T, changeset: Bytes, pool:
+    // Arc<ConnectionPool>) -> Result<(), DocError>     where
+    //         T: Into<DocId> + Debug,
+    // {
+    //     let id = id.into();
+    //     match self.doc_map.get(&id) {
+    //         None => Err(doc_not_found()),
+    //         Some(doc) => {
+    //             let _ = doc.apply_delta(changeset, pool)?;
+    //             Ok(())
+    //         },
+    //     }
+    // }
+
     #[tracing::instrument(level = "debug", skip(self, conn), err)]
     pub(crate) fn delete(&self, params: QueryDocParams, conn: &SqliteConnection) -> Result<(), DocError> {
-        let _ = self.sql.delete_doc(&params.doc_id, &*conn)?;
+        let doc_id = &params.doc_id;
+        let _ = self.sql.delete_doc(doc_id, &*conn)?;
+
+        self.cache.remove(doc_id);
+        self.ws.write().remove_handler(doc_id);
         let _ = self.delete_doc_on_server(params)?;
         Ok(())
     }
@@ -51,7 +99,7 @@ impl DocController {
 
 impl DocController {
     #[tracing::instrument(level = "debug", skip(self, params), err)]
-    fn update_doc_on_server(&self, params: SaveDocParams) -> Result<(), DocError> {
+    fn update_doc_on_server(&self, params: UpdateDocParams) -> Result<(), DocError> {
         let token = self.user.token()?;
         let server = self.server.clone();
         tokio::spawn(async move {
@@ -89,15 +137,6 @@ impl DocController {
         }))
     }
 
-    #[tracing::instrument(level = "debug", skip(self), err)]
-    async fn sync_read_doc_from_server(&self, params: QueryDocParams) -> Result<Doc, DocError> {
-        let token = self.user.token()?;
-        match self.server.read_doc(&token, params).await? {
-            None => Err(DocError::not_found()),
-            Some(doc) => Ok(doc),
-        }
-    }
-
     #[tracing::instrument(level = "debug", skip(self), err)]
     fn delete_doc_on_server(&self, params: QueryDocParams) -> Result<(), DocError> {
         let token = self.user.token()?;
@@ -114,25 +153,30 @@ impl DocController {
         Ok(())
     }
 
-    fn _open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> {
-        let doc_table = self.sql.read_doc_table(&params.doc_id, &*(pool.get().map_err(internal_error)?))?;
-        let doc: Doc = doc_table.into();
-        let _ = self.read_doc_from_server(params, pool.clone())?;
-        Ok(doc)
-    }
+    fn _open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Arc<OpenedDoc>, DocError> {
+        match self.sql.read_doc_table(&params.doc_id, &*(pool.get().map_err(internal_error)?)) {
+            Ok(doc_table) => {
+                let doc = Arc::new(OpenedDoc::new(doc_table.into(), self.ws.read().sender.clone())?);
+                self.ws.write().register_handler(doc.id.as_ref(), doc.clone());
+                self.cache.set(doc.clone());
+
+                Ok(doc)
+            },
+            Err(error) => {
+                if error.is_record_not_found() {
+                    log::debug!("Doc:{} don't exist, reading from server", params.doc_id);
+                    // TODO: notify doc update
+                    let _ = self.read_doc_from_server(params, pool);
+                }
 
-    async fn try_read_on_server(&self, params: QueryDocParams, pool: Arc<ConnectionPool>, error: DocError) -> Result<Doc, DocError> {
-        if error.is_record_not_found() {
-            log::debug!("Doc:{} don't exist, reading from server", params.doc_id);
-            self.read_doc_from_server(params, pool)?.await.map_err(internal_error)?
-        } else {
-            Err(error)
+                return Err(error);
+            },
         }
     }
 }
 
 impl OpenedDocPersistence for DocController {
-    fn save(&self, params: SaveDocParams, pool: Arc<ConnectionPool>) -> Result<(), DocError> {
+    fn save(&self, params: UpdateDocParams, pool: Arc<ConnectionPool>) -> Result<(), DocError> {
         let changeset = DocTableChangeset::new(params.clone());
         let _ = self.sql.update_doc_table(changeset, &*(pool.get().map_err(internal_error)?))?;
         Ok(())

+ 3 - 1
rust-lib/flowy-document/src/services/mod.rs

@@ -1,4 +1,6 @@
+mod cache;
+pub mod doc;
 pub(crate) mod doc_controller;
-pub(crate) mod open_doc;
+mod open_doc;
 pub mod server;
 pub mod ws;

+ 28 - 15
rust-lib/flowy-document/src/services/open_doc/open_doc.rs → rust-lib/flowy-document/src/services/open_doc.rs

@@ -1,16 +1,19 @@
 use crate::{
     entities::{
-        doc::SaveDocParams,
+        doc::{Doc, UpdateDocParams},
         ws::{WsDocumentData, WsSource},
     },
     errors::DocError,
-    services::ws::{WsHandler, WsSender},
+    services::{
+        doc::Document,
+        ws::{WsHandler, WsSender},
+    },
 };
 use bytes::Bytes;
 use flowy_database::ConnectionPool;
-use flowy_ot::{client::Document, core::Delta};
+use flowy_ot::core::Delta;
 use parking_lot::RwLock;
-use std::sync::Arc;
+use std::{convert::TryInto, sync::Arc};
 
 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
 pub struct DocId(pub(crate) String);
@@ -25,32 +28,42 @@ where
 }
 
 pub(crate) trait OpenedDocPersistence: Send + Sync {
-    fn save(&self, params: SaveDocParams, pool: Arc<ConnectionPool>) -> Result<(), DocError>;
+    fn save(&self, params: UpdateDocParams, pool: Arc<ConnectionPool>) -> Result<(), DocError>;
 }
 
 pub(crate) struct OpenedDoc {
     pub(crate) id: DocId,
+    pub(crate) revision: i64,
     document: RwLock<Document>,
     ws_sender: Arc<dyn WsSender>,
-    persistence: Arc<dyn OpenedDocPersistence>,
 }
 
 impl OpenedDoc {
-    pub(crate) fn new(id: DocId, delta: Delta, persistence: Arc<dyn OpenedDocPersistence>, ws_sender: Arc<dyn WsSender>) -> Self {
+    pub(crate) fn new(doc: Doc, ws_sender: Arc<dyn WsSender>) -> Result<Self, DocError> {
+        let id: DocId = doc.id.into();
+        let revision = doc.revision;
+        let delta: Delta = doc.data.try_into()?;
         let document = RwLock::new(Document::from_delta(delta));
-        Self {
+
+        Ok(Self {
             id,
+            revision,
             document,
             ws_sender,
-            persistence,
-        }
+        })
     }
 
-    pub(crate) fn data(&self) -> Vec<u8> { self.document.read().to_bytes() }
+    pub(crate) fn doc(&self) -> Doc {
+        Doc {
+            id: self.id.0.clone(),
+            data: self.document.read().to_bytes(),
+            revision: self.revision,
+        }
+    }
 
     pub(crate) fn apply_delta(&self, data: Bytes, pool: Arc<ConnectionPool>) -> Result<(), DocError> {
         let mut write_guard = self.document.write();
-        let _ = write_guard.apply_changeset(data.clone())?;
+        let _ = write_guard.apply_delta(data.clone())?;
 
         match self.ws_sender.send_data(data) {
             Ok(_) => {},
@@ -61,11 +74,11 @@ impl OpenedDoc {
         }
 
         // Opti: strategy to save the document
-        let save = SaveDocParams {
+        let save = UpdateDocParams {
             id: self.id.0.clone(),
-            data: write_guard.to_bytes(),
+            doc_data: write_guard.to_bytes(),
         };
-        let _ = self.persistence.save(save, pool)?;
+        // let _ = self.persistence.save(save, pool)?;
 
         Ok(())
     }

+ 0 - 94
rust-lib/flowy-document/src/services/open_doc/manager.rs

@@ -1,94 +0,0 @@
-use crate::{
-    errors::DocError,
-    services::{
-        open_doc::{DocId, OpenedDoc, OpenedDocPersistence},
-        ws::WsManager,
-    },
-};
-use bytes::Bytes;
-use dashmap::DashMap;
-use flowy_database::ConnectionPool;
-use flowy_ot::{core::Delta, errors::OTError};
-use parking_lot::RwLock;
-use std::{convert::TryInto, fmt::Debug, sync::Arc};
-
-pub(crate) struct OpenedDocManager {
-    doc_map: DashMap<DocId, Arc<OpenedDoc>>,
-    ws_manager: Arc<RwLock<WsManager>>,
-    persistence: Arc<dyn OpenedDocPersistence>,
-}
-
-impl OpenedDocManager {
-    pub(crate) fn new(ws_manager: Arc<RwLock<WsManager>>, persistence: Arc<dyn OpenedDocPersistence>) -> Self {
-        Self {
-            doc_map: DashMap::new(),
-            ws_manager,
-            persistence,
-        }
-    }
-
-    #[tracing::instrument(level = "debug", skip(self, data), err)]
-    pub(crate) fn open<T, D>(&self, id: T, data: D) -> Result<(), DocError>
-    where
-        T: Into<DocId> + Debug,
-        D: TryInto<Delta, Error = OTError>,
-    {
-        let doc = Arc::new(OpenedDoc::new(
-            id.into(),
-            data.try_into()?,
-            self.persistence.clone(),
-            self.ws_manager.read().sender.clone(),
-        ));
-        self.ws_manager.write().register_handler(doc.id.as_ref(), doc.clone());
-        self.doc_map.insert(doc.id.clone(), doc.clone());
-        Ok(())
-    }
-
-    pub(crate) fn is_opened<T>(&self, id: T) -> bool
-    where
-        T: Into<DocId>,
-    {
-        let doc_id = id.into();
-        self.doc_map.get(&doc_id).is_some()
-    }
-
-    #[tracing::instrument(level = "debug", skip(self, changeset, pool), err)]
-    pub(crate) async fn apply_changeset<T>(&self, id: T, changeset: Bytes, pool: Arc<ConnectionPool>) -> Result<(), DocError>
-    where
-        T: Into<DocId> + Debug,
-    {
-        let id = id.into();
-        match self.doc_map.get(&id) {
-            None => Err(doc_not_found()),
-            Some(doc) => {
-                let _ = doc.apply_delta(changeset, pool)?;
-                Ok(())
-            },
-        }
-    }
-
-    pub(crate) async fn read_doc<T>(&self, id: T) -> Result<Vec<u8>, DocError>
-    where
-        T: Into<DocId> + Clone,
-    {
-        if !self.is_opened(id.clone()) {
-            return Err(doc_not_found());
-        }
-
-        let doc_id = id.into();
-        let doc = self.doc_map.get(&doc_id).unwrap();
-        Ok(doc.data())
-    }
-
-    pub(crate) fn close<T>(&self, id: T) -> Result<(), DocError>
-    where
-        T: Into<DocId>,
-    {
-        let doc_id = id.into();
-        self.doc_map.remove(&doc_id);
-        self.ws_manager.write().remove_handler(doc_id.as_ref());
-        Ok(())
-    }
-}
-
-fn doc_not_found() -> DocError { DocError::not_found().context("Doc is close or you should call open first") }

+ 0 - 5
rust-lib/flowy-document/src/services/open_doc/mod.rs

@@ -1,5 +0,0 @@
-mod manager;
-mod open_doc;
-
-pub(crate) use manager::*;
-pub use open_doc::*;

+ 2 - 2
rust-lib/flowy-document/src/services/server/mod.rs

@@ -5,7 +5,7 @@ mod server_api_mock;
 pub use server_api::*;
 // TODO: ignore mock files in production
 use crate::{
-    entities::doc::{CreateDocParams, Doc, QueryDocParams, SaveDocParams},
+    entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams},
     errors::DocError,
 };
 use flowy_infra::future::ResultFuture;
@@ -18,7 +18,7 @@ pub trait DocumentServerAPI {
 
     fn read_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<Option<Doc>, DocError>;
 
-    fn update_doc(&self, token: &str, params: SaveDocParams) -> ResultFuture<(), DocError>;
+    fn update_doc(&self, token: &str, params: UpdateDocParams) -> ResultFuture<(), DocError>;
 
     fn delete_doc(&self, token: &str, params: QueryDocParams) -> ResultFuture<(), DocError>;
 }

+ 3 - 3
rust-lib/flowy-document/src/services/server/server_api.rs

@@ -1,5 +1,5 @@
 use crate::{
-    entities::doc::{CreateDocParams, Doc, QueryDocParams, SaveDocParams},
+    entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams},
     errors::DocError,
     services::server::DocumentServerAPI,
 };
@@ -19,7 +19,7 @@ impl DocumentServerAPI for DocServer {
         ResultFuture::new(async move { read_doc_request(&token, params, DOC_URL.as_ref()).await })
     }
 
-    fn update_doc(&self, token: &str, params: SaveDocParams) -> ResultFuture<(), DocError> {
+    fn update_doc(&self, token: &str, params: UpdateDocParams) -> ResultFuture<(), DocError> {
         let token = token.to_owned();
         ResultFuture::new(async move { update_doc_request(&token, params, DOC_URL.as_ref()).await })
     }
@@ -53,7 +53,7 @@ pub async fn read_doc_request(token: &str, params: QueryDocParams, url: &str) ->
     Ok(doc)
 }
 
-pub async fn update_doc_request(token: &str, params: SaveDocParams, url: &str) -> Result<(), DocError> {
+pub async fn update_doc_request(token: &str, params: UpdateDocParams, url: &str) -> Result<(), DocError> {
     let _ = request_builder()
         .patch(&url.to_owned())
         .header(HEADER_TOKEN, token)

+ 2 - 2
rust-lib/flowy-document/src/services/server/server_api_mock.rs

@@ -1,5 +1,5 @@
 use crate::{
-    entities::doc::{CreateDocParams, Doc, QueryDocParams, SaveDocParams},
+    entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams},
     errors::DocError,
     services::server::DocumentServerAPI,
 };
@@ -13,7 +13,7 @@ impl DocumentServerAPI for DocServerMock {
         ResultFuture::new(async { Ok(None) })
     }
 
-    fn update_doc(&self, _token: &str, _params: SaveDocParams) -> ResultFuture<(), DocError> { ResultFuture::new(async { Ok(()) }) }
+    fn update_doc(&self, _token: &str, _params: UpdateDocParams) -> ResultFuture<(), DocError> { ResultFuture::new(async { Ok(()) }) }
 
     fn delete_doc(&self, _token: &str, _params: QueryDocParams) -> ResultFuture<(), DocError> { ResultFuture::new(async { Ok(()) }) }
 }

+ 6 - 5
rust-lib/flowy-document/src/sql_tables/doc/doc_table.rs

@@ -1,4 +1,4 @@
-use crate::entities::doc::{Doc, SaveDocParams};
+use crate::entities::doc::{Doc, UpdateDocParams};
 use flowy_database::schema::doc_table;
 
 #[derive(PartialEq, Clone, Debug, Queryable, Identifiable, Insertable, Associations)]
@@ -6,7 +6,7 @@ use flowy_database::schema::doc_table;
 pub(crate) struct DocTable {
     pub id: String,
     pub data: Vec<u8>,
-    pub version: i64,
+    pub revision: i64,
 }
 
 impl DocTable {
@@ -14,7 +14,7 @@ impl DocTable {
         Self {
             id: doc.id,
             data: doc.data,
-            version: 0,
+            revision: 0,
         }
     }
 }
@@ -27,10 +27,10 @@ pub(crate) struct DocTableChangeset {
 }
 
 impl DocTableChangeset {
-    pub(crate) fn new(params: SaveDocParams) -> Self {
+    pub(crate) fn new(params: UpdateDocParams) -> Self {
         Self {
             id: params.id,
-            data: params.data,
+            data: params.doc_data,
         }
     }
 }
@@ -40,6 +40,7 @@ impl std::convert::Into<Doc> for DocTable {
         Doc {
             id: self.id,
             data: self.data,
+            revision: self.revision,
         }
     }
 }

+ 3 - 7
rust-lib/flowy-ot/tests/attribute_test.rs → rust-lib/flowy-document/tests/editor/attribute_test.rs

@@ -1,10 +1,6 @@
-pub mod helper;
-
-use crate::helper::{TestOp::*, *};
-use flowy_ot::{
-    client::{FlowyDoc, PlainDoc},
-    core::{Interval, NEW_LINE, WHITESPACE},
-};
+use crate::editor::{TestBuilder, TestOp::*};
+use flowy_document::services::doc::{FlowyDoc, PlainDoc};
+use flowy_ot::core::{Interval, NEW_LINE, WHITESPACE};
 
 #[test]
 fn attributes_bold_added() {

+ 0 - 1
rust-lib/flowy-document/tests/editor/main.rs

@@ -1 +0,0 @@
-

+ 7 - 4
rust-lib/flowy-ot/tests/helper/mod.rs → rust-lib/flowy-document/tests/editor/mod.rs

@@ -1,8 +1,11 @@
+mod attribute_test;
+mod op_test;
+mod serde_test;
+mod undo_redo_test;
+
 use derive_more::Display;
-use flowy_ot::{
-    client::{CustomDocument, Document},
-    core::*,
-};
+use flowy_document::services::doc::{CustomDocument, Document};
+use flowy_ot::core::*;
 use rand::{prelude::*, Rng as WrappedRng};
 use std::{sync::Once, time::Duration};
 

+ 3 - 5
rust-lib/flowy-ot/tests/op_test.rs → rust-lib/flowy-document/tests/editor/op_test.rs

@@ -1,9 +1,7 @@
-pub mod helper;
-
-use crate::helper::TestOp::*;
+use crate::editor::{Rng, TestBuilder, TestOp::*};
 use bytecount::num_chars;
-use flowy_ot::{client::PlainDoc, core::*};
-use helper::*;
+use flowy_document::services::doc::PlainDoc;
+use flowy_ot::core::*;
 
 #[test]
 fn attributes_insert_text() {

+ 2 - 4
rust-lib/flowy-ot/tests/serde_test.rs → rust-lib/flowy-document/tests/editor/serde_test.rs

@@ -1,7 +1,5 @@
-use flowy_ot::{
-    client::{Document, PlainDoc},
-    core::*,
-};
+use flowy_document::services::doc::{Document, PlainDoc};
+use flowy_ot::core::*;
 
 #[test]
 fn operation_insert_serialize_test() {

+ 3 - 7
rust-lib/flowy-ot/tests/undo_redo_test.rs → rust-lib/flowy-document/tests/editor/undo_redo_test.rs

@@ -1,10 +1,6 @@
-pub mod helper;
-
-use crate::helper::{TestOp::*, *};
-use flowy_ot::{
-    client::{FlowyDoc, PlainDoc, RECORD_THRESHOLD},
-    core::{Interval, NEW_LINE, WHITESPACE},
-};
+use crate::editor::{TestBuilder, TestOp::*};
+use flowy_document::services::doc::{FlowyDoc, PlainDoc, RECORD_THRESHOLD};
+use flowy_ot::core::{Interval, NEW_LINE, WHITESPACE};
 
 #[test]
 fn history_insert_undo() {

+ 1 - 0
rust-lib/flowy-document/tests/main.rs

@@ -0,0 +1 @@
+mod editor;

+ 0 - 7
rust-lib/flowy-ot/Cargo.toml

@@ -11,17 +11,10 @@ serde = { version = "1.0", features = ["derive"] }
 serde_json = {version = "1.0"}
 derive_more = {version = "0.99", features = ["display"]}
 log = "0.4"
-color-eyre = { version = "0.5", default-features = false }
-chrono = "0.4.19"
 lazy_static = "1.4.0"
-url = "2.2"
 strum = "0.21"
 strum_macros = "0.21"
 bytes = "1.0"
 
 
-[dev-dependencies]
-criterion = "0.3"
-rand = "0.7.3"
-env_logger = "0.8.2"
 

+ 0 - 2
rust-lib/flowy-ot/src/lib.rs

@@ -1,4 +1,2 @@
-pub mod client;
 pub mod core;
 pub mod errors;
-pub mod server;

+ 0 - 1
rust-lib/flowy-ot/src/server/mod.rs

@@ -1 +0,0 @@
-

+ 10 - 7
rust-lib/flowy-workspace/src/entities/view/view_update.rs

@@ -3,7 +3,7 @@ use crate::{
     errors::WorkspaceError,
 };
 use flowy_derive::ProtoBuf;
-use flowy_document::entities::doc::{ApplyChangesetParams, SaveDocParams};
+use flowy_document::entities::doc::{DocChangeset, UpdateDocParams};
 use std::convert::TryInto;
 
 #[derive(Default, ProtoBuf)]
@@ -109,16 +109,19 @@ pub struct SaveViewDataRequest {
     pub data: Vec<u8>,
 }
 
-impl TryInto<SaveDocParams> for SaveViewDataRequest {
+impl TryInto<UpdateDocParams> for SaveViewDataRequest {
     type Error = WorkspaceError;
 
-    fn try_into(self) -> Result<SaveDocParams, Self::Error> {
+    fn try_into(self) -> Result<UpdateDocParams, Self::Error> {
         let view_id = ViewId::parse(self.view_id).map_err(|e| WorkspaceError::view_id().context(e))?.0;
 
         // Opti: Vec<u8> -> Delta -> Vec<u8>
         let data = DeltaData::parse(self.data).map_err(|e| WorkspaceError::view_data().context(e))?.0;
 
-        Ok(SaveDocParams { id: view_id, data })
+        Ok(UpdateDocParams {
+            id: view_id,
+            doc_data: data,
+        })
     }
 }
 
@@ -131,15 +134,15 @@ pub struct ApplyChangesetRequest {
     pub data: Vec<u8>,
 }
 
-impl TryInto<ApplyChangesetParams> for ApplyChangesetRequest {
+impl TryInto<DocChangeset> for ApplyChangesetRequest {
     type Error = WorkspaceError;
 
-    fn try_into(self) -> Result<ApplyChangesetParams, Self::Error> {
+    fn try_into(self) -> Result<DocChangeset, Self::Error> {
         let view_id = ViewId::parse(self.view_id).map_err(|e| WorkspaceError::view_id().context(e))?.0;
 
         // Opti: Vec<u8> -> Delta -> Vec<u8>
         let data = DeltaData::parse(self.data).map_err(|e| WorkspaceError::view_data().context(e))?.0;
 
-        Ok(ApplyChangesetParams { id: view_id, data })
+        Ok(DocChangeset { id: view_id, data })
     }
 }

+ 1 - 1
rust-lib/flowy-workspace/src/event.rs

@@ -49,6 +49,6 @@ pub enum WorkspaceEvent {
     #[event(input = "OpenViewRequest", output = "Doc")]
     OpenView          = 205,
 
-    #[event(input = "ApplyChangesetRequest", output = "Doc")]
+    #[event(input = "DocChangeset", output = "Doc")]
     ApplyChangeset    = 206,
 }

+ 2 - 2
rust-lib/flowy-workspace/src/handlers/view_handler.rs

@@ -16,7 +16,7 @@ use crate::{
     services::ViewController,
 };
 use flowy_dispatch::prelude::{data_result, Data, DataResult, Unit};
-use flowy_document::entities::doc::{ApplyChangesetParams, Doc, QueryDocParams};
+use flowy_document::entities::doc::{Doc, DocChangeset, QueryDocParams};
 use std::{convert::TryInto, sync::Arc};
 
 #[tracing::instrument(skip(data, controller), err)]
@@ -60,7 +60,7 @@ pub(crate) async fn apply_changeset_handler(
     data: Data<ApplyChangesetRequest>,
     controller: Unit<Arc<ViewController>>,
 ) -> DataResult<Doc, WorkspaceError> {
-    let params: ApplyChangesetParams = data.into_inner().try_into()?;
+    let params: DocChangeset = data.into_inner().try_into()?;
     let doc = controller.apply_changeset(params).await?;
     data_result(doc)
 }

+ 2 - 2
rust-lib/flowy-workspace/src/services/view_controller.rs

@@ -14,7 +14,7 @@ use crate::{
 };
 use flowy_database::SqliteConnection;
 use flowy_document::{
-    entities::doc::{ApplyChangesetParams, CreateDocParams, Doc, QueryDocParams},
+    entities::doc::{CreateDocParams, Doc, DocChangeset, QueryDocParams},
     module::FlowyDocument,
 };
 use std::sync::Arc;
@@ -125,7 +125,7 @@ impl ViewController {
         Ok(())
     }
 
-    pub(crate) async fn apply_changeset(&self, params: ApplyChangesetParams) -> Result<Doc, WorkspaceError> {
+    pub(crate) async fn apply_changeset(&self, params: DocChangeset) -> Result<Doc, WorkspaceError> {
         let pool = self.database.db_pool()?;
         let doc = self.document.apply_changeset(params, pool).await?;
         Ok(doc)