Browse Source

save doc & switch doc

appflowy 3 năm trước cách đây
mục cha
commit
29f5f1ac27

+ 1 - 7
app_flowy/lib/startup/tasks/rust_sdk_init_task.dart

@@ -40,7 +40,7 @@ class ApplicationBlocObserver extends BlocObserver {
   @override
   // ignore: unnecessary_overrides
   void onTransition(Bloc bloc, Transition transition) {
-    // Log.debug(transition);
+    Log.debug(transition);
     super.onTransition(bloc, transition);
   }
 
@@ -50,9 +50,3 @@ class ApplicationBlocObserver extends BlocObserver {
     super.onError(bloc, error, stackTrace);
   }
 }
-
-class EngineBlocConfig {
-  static void setup() {
-    Bloc.observer = ApplicationBlocObserver();
-  }
-}

+ 0 - 2
app_flowy/lib/workspace/application/doc/doc_bloc.dart

@@ -14,7 +14,6 @@ class DocBloc extends Bloc<DocEvent, DocState> {
   Stream<DocState> mapEventToState(DocEvent event) async* {
     yield* event.map(
       initial: (e) async* {},
-      save: (Save value) async* {},
       close: (Close value) async* {},
     );
   }
@@ -23,7 +22,6 @@ class DocBloc extends Bloc<DocEvent, DocState> {
 @freezed
 abstract class DocEvent with _$DocEvent {
   const factory DocEvent.initial() = Initial;
-  const factory DocEvent.save(String jsonStr) = Save;
   const factory DocEvent.close() = Close;
 }
 

+ 0 - 134
app_flowy/lib/workspace/application/doc/doc_bloc.freezed.dart

@@ -20,12 +20,6 @@ class _$DocEventTearOff {
     return const Initial();
   }
 
-  Save save(String jsonStr) {
-    return Save(
-      jsonStr,
-    );
-  }
-
   Close close() {
     return const Close();
   }
@@ -39,14 +33,12 @@ mixin _$DocEvent {
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
-    required TResult Function(String jsonStr) save,
     required TResult Function() close,
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
-    TResult Function(String jsonStr)? save,
     TResult Function()? close,
     required TResult orElse(),
   }) =>
@@ -54,14 +46,12 @@ mixin _$DocEvent {
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
     required TResult Function(Initial value) initial,
-    required TResult Function(Save value) save,
     required TResult Function(Close value) close,
   }) =>
       throw _privateConstructorUsedError;
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
     TResult Function(Initial value)? initial,
-    TResult Function(Save value)? save,
     TResult Function(Close value)? close,
     required TResult orElse(),
   }) =>
@@ -121,7 +111,6 @@ class _$Initial implements Initial {
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
-    required TResult Function(String jsonStr) save,
     required TResult Function() close,
   }) {
     return initial();
@@ -131,7 +120,6 @@ class _$Initial implements Initial {
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
-    TResult Function(String jsonStr)? save,
     TResult Function()? close,
     required TResult orElse(),
   }) {
@@ -145,7 +133,6 @@ class _$Initial implements Initial {
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
     required TResult Function(Initial value) initial,
-    required TResult Function(Save value) save,
     required TResult Function(Close value) close,
   }) {
     return initial(this);
@@ -155,7 +142,6 @@ class _$Initial implements Initial {
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
     TResult Function(Initial value)? initial,
-    TResult Function(Save value)? save,
     TResult Function(Close value)? close,
     required TResult orElse(),
   }) {
@@ -170,122 +156,6 @@ abstract class Initial implements DocEvent {
   const factory Initial() = _$Initial;
 }
 
-/// @nodoc
-abstract class $SaveCopyWith<$Res> {
-  factory $SaveCopyWith(Save value, $Res Function(Save) then) =
-      _$SaveCopyWithImpl<$Res>;
-  $Res call({String jsonStr});
-}
-
-/// @nodoc
-class _$SaveCopyWithImpl<$Res> extends _$DocEventCopyWithImpl<$Res>
-    implements $SaveCopyWith<$Res> {
-  _$SaveCopyWithImpl(Save _value, $Res Function(Save) _then)
-      : super(_value, (v) => _then(v as Save));
-
-  @override
-  Save get _value => super._value as Save;
-
-  @override
-  $Res call({
-    Object? jsonStr = freezed,
-  }) {
-    return _then(Save(
-      jsonStr == freezed
-          ? _value.jsonStr
-          : jsonStr // ignore: cast_nullable_to_non_nullable
-              as String,
-    ));
-  }
-}
-
-/// @nodoc
-
-class _$Save implements Save {
-  const _$Save(this.jsonStr);
-
-  @override
-  final String jsonStr;
-
-  @override
-  String toString() {
-    return 'DocEvent.save(jsonStr: $jsonStr)';
-  }
-
-  @override
-  bool operator ==(dynamic other) {
-    return identical(this, other) ||
-        (other is Save &&
-            (identical(other.jsonStr, jsonStr) ||
-                const DeepCollectionEquality().equals(other.jsonStr, jsonStr)));
-  }
-
-  @override
-  int get hashCode =>
-      runtimeType.hashCode ^ const DeepCollectionEquality().hash(jsonStr);
-
-  @JsonKey(ignore: true)
-  @override
-  $SaveCopyWith<Save> get copyWith =>
-      _$SaveCopyWithImpl<Save>(this, _$identity);
-
-  @override
-  @optionalTypeArgs
-  TResult when<TResult extends Object?>({
-    required TResult Function() initial,
-    required TResult Function(String jsonStr) save,
-    required TResult Function() close,
-  }) {
-    return save(jsonStr);
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult maybeWhen<TResult extends Object?>({
-    TResult Function()? initial,
-    TResult Function(String jsonStr)? save,
-    TResult Function()? close,
-    required TResult orElse(),
-  }) {
-    if (save != null) {
-      return save(jsonStr);
-    }
-    return orElse();
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult map<TResult extends Object?>({
-    required TResult Function(Initial value) initial,
-    required TResult Function(Save value) save,
-    required TResult Function(Close value) close,
-  }) {
-    return save(this);
-  }
-
-  @override
-  @optionalTypeArgs
-  TResult maybeMap<TResult extends Object?>({
-    TResult Function(Initial value)? initial,
-    TResult Function(Save value)? save,
-    TResult Function(Close value)? close,
-    required TResult orElse(),
-  }) {
-    if (save != null) {
-      return save(this);
-    }
-    return orElse();
-  }
-}
-
-abstract class Save implements DocEvent {
-  const factory Save(String jsonStr) = _$Save;
-
-  String get jsonStr => throw _privateConstructorUsedError;
-  @JsonKey(ignore: true)
-  $SaveCopyWith<Save> get copyWith => throw _privateConstructorUsedError;
-}
-
 /// @nodoc
 abstract class $CloseCopyWith<$Res> {
   factory $CloseCopyWith(Close value, $Res Function(Close) then) =
@@ -324,7 +194,6 @@ class _$Close implements Close {
   @optionalTypeArgs
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
-    required TResult Function(String jsonStr) save,
     required TResult Function() close,
   }) {
     return close();
@@ -334,7 +203,6 @@ class _$Close implements Close {
   @optionalTypeArgs
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
-    TResult Function(String jsonStr)? save,
     TResult Function()? close,
     required TResult orElse(),
   }) {
@@ -348,7 +216,6 @@ class _$Close implements Close {
   @optionalTypeArgs
   TResult map<TResult extends Object?>({
     required TResult Function(Initial value) initial,
-    required TResult Function(Save value) save,
     required TResult Function(Close value) close,
   }) {
     return close(this);
@@ -358,7 +225,6 @@ class _$Close implements Close {
   @optionalTypeArgs
   TResult maybeMap<TResult extends Object?>({
     TResult Function(Initial value)? initial,
-    TResult Function(Save value)? save,
     TResult Function(Close value)? close,
     required TResult orElse(),
   }) {

+ 2 - 1
app_flowy/lib/workspace/domain/page_stack/page_stack.dart

@@ -63,7 +63,8 @@ List<Widget> _buildStackWidget(HomeStackView stackView) {
         case ViewType.Blank:
           return BlankPage(stackView: stackView as BlankStackView);
         case ViewType.Doc:
-          return DocPage(stackView: stackView as DocPageStackView);
+          final docView = stackView as DocPageStackView;
+          return DocPage(key: ValueKey(docView.view.id), stackView: docView);
         default:
           return BlankPage(stackView: stackView as BlankStackView);
       }

+ 4 - 0
app_flowy/lib/workspace/infrastructure/deps_resolver.dart

@@ -15,6 +15,7 @@ import 'package:app_flowy/workspace/infrastructure/repos/app_repo.dart';
 import 'package:app_flowy/workspace/infrastructure/repos/doc_repo.dart';
 import 'package:app_flowy/workspace/infrastructure/repos/view_repo.dart';
 import 'package:app_flowy/workspace/infrastructure/repos/workspace_repo.dart';
+import 'package:flowy_editor/flowy_editor.dart';
 import 'package:get_it/get_it.dart';
 
 import 'i_view_impl.dart';
@@ -66,6 +67,9 @@ class HomeDepsResolver {
     getIt.registerFactoryParam<DocBloc, String, void>(
         (docId, _) => DocBloc(getIt<IDoc>(param1: docId)));
 
+    // editor
+    getIt.registerFactoryParam<EditorPersistence, String, void>(
+        (docId, _) => EditorPersistenceImpl(repo: DocRepository(docId: docId)));
     // getIt.registerFactoryParam<ViewBloc, String, void>(
     //     (viewId, _) => ViewBloc(iViewImpl: getIt<IView>(param1: viewId)));
   }

+ 22 - 3
app_flowy/lib/workspace/infrastructure/i_doc_impl.dart

@@ -1,10 +1,11 @@
 import 'dart:convert';
 
-import 'package:app_flowy/workspace/domain/i_doc.dart';
-import 'package:app_flowy/workspace/infrastructure/repos/doc_repo.dart';
+import 'package:dartz/dartz.dart';
 import 'package:flowy_editor/flowy_editor.dart';
 import 'package:flowy_sdk/protobuf/flowy-editor/errors.pb.dart';
-import 'package:dartz/dartz.dart';
+
+import 'package:app_flowy/workspace/domain/i_doc.dart';
+import 'package:app_flowy/workspace/infrastructure/repos/doc_repo.dart';
 
 class IDocImpl extends IDoc {
   DocRepository repo;
@@ -49,3 +50,21 @@ class IDocImpl extends IDoc {
     return document;
   }
 }
+
+class EditorPersistenceImpl extends EditorPersistence {
+  DocRepository repo;
+  EditorPersistenceImpl({
+    required this.repo,
+  });
+
+  @override
+  Future<bool> save(List<dynamic> jsonList) async {
+    final json = jsonEncode(jsonList);
+    return repo.updateDoc(text: json).then((result) {
+      return result.fold(
+        (l) => true,
+        (r) => false,
+      );
+    });
+  }
+}

+ 15 - 0
app_flowy/lib/workspace/presentation/doc/doc_page.dart

@@ -36,6 +36,21 @@ class _DocPageState extends State<DocPage> {
       }),
     );
   }
+
+  @override
+  void dispose() {
+    super.dispose();
+  }
+
+  @override
+  void deactivate() {
+    super.deactivate();
+  }
+
+  @override
+  void didUpdateWidget(covariant DocPage oldWidget) {
+    super.didUpdateWidget(oldWidget);
+  }
 }
 
 class DocPageStackView extends HomeStackView {

+ 11 - 6
app_flowy/lib/workspace/presentation/doc/editor_widget.dart

@@ -16,6 +16,7 @@ class EditorWdiget extends StatelessWidget {
     controller = EditorController(
       document: doc.data,
       selection: const TextSelection.collapsed(offset: 0),
+      persistence: getIt<EditorPersistence>(param1: doc.info.id),
     );
   }
 
@@ -23,12 +24,16 @@ class EditorWdiget extends StatelessWidget {
   Widget build(BuildContext context) {
     return BlocProvider(
       create: (context) => getIt<DocBloc>(param1: doc.info.id),
-      child: Column(
-        mainAxisAlignment: MainAxisAlignment.spaceBetween,
-        children: [
-          _renderEditor(controller),
-          _renderToolbar(controller),
-        ],
+      child: BlocBuilder<DocBloc, DocState>(
+        builder: (ctx, state) {
+          return Column(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            children: [
+              _renderEditor(controller),
+              _renderToolbar(controller),
+            ],
+          );
+        },
       ),
     );
   }

+ 1 - 1
app_flowy/macos/Podfile.lock

@@ -49,4 +49,4 @@ SPEC CHECKSUMS:
 
 PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
 
-COCOAPODS: 1.9.3
+COCOAPODS: 1.10.1

+ 29 - 11
app_flowy/packages/flowy_editor/lib/src/service/controller.dart

@@ -10,10 +10,16 @@ import '../model/document/document.dart';
 import '../model/document/style.dart';
 import '../model/document/node/embed.dart';
 
+abstract class EditorPersistence {
+  Future<bool> save(List<dynamic> jsonList);
+}
+
 class EditorController extends ChangeNotifier {
+  final EditorPersistence? persistence;
   EditorController({
     required this.document,
     required this.selection,
+    this.persistence,
   });
 
   factory EditorController.basic() {
@@ -38,7 +44,8 @@ class EditorController extends ChangeNotifier {
       );
 
   Style getSelectionStyle() =>
-      document.collectStyle(selection.start, selection.end - selection.start)..mergeAll(toggledStyle);
+      document.collectStyle(selection.start, selection.end - selection.start)
+        ..mergeAll(toggledStyle);
 
   bool get hasUndo => document.hasUndo;
 
@@ -58,10 +65,10 @@ class EditorController extends ChangeNotifier {
     }
   }
 
-  Future<bool> save() async {
-    document.toDelta().toJson();
-    // TODO: vedon - Save document to database
-    return true;
+  void save() {
+    if (persistence != null) {
+      persistence!.save(document.toDelta().toJson());
+    }
   }
 
   @override
@@ -80,7 +87,9 @@ class EditorController extends ChangeNotifier {
   }
 
   void formatText(int index, int length, Attribute? attribute) {
-    if (length == 0 && attribute!.isInline && attribute.key != Attribute.link.key) {
+    if (length == 0 &&
+        attribute!.isInline &&
+        attribute.key != Attribute.link.key) {
       toggledStyle = toggledStyle.put(attribute);
     }
 
@@ -95,16 +104,24 @@ class EditorController extends ChangeNotifier {
     notifyListeners();
   }
 
-  void replaceText(int index, int length, Object? data, TextSelection? textSelection) {
+  void replaceText(
+      int index, int length, Object? data, TextSelection? textSelection) {
     assert(data is String || data is Embeddable);
 
     Delta? delta;
     if (length > 0 || data is! String || data.isNotEmpty) {
       delta = document.replace(index, length, data);
-      var shouldRetainDelta = toggledStyle.isNotEmpty && delta.isNotEmpty && delta.length <= 2 && delta.last.isInsert;
-      if (shouldRetainDelta && toggledStyle.isNotEmpty && delta.length == 2 && delta.last.data == '\n') {
+      var shouldRetainDelta = toggledStyle.isNotEmpty &&
+          delta.isNotEmpty &&
+          delta.length <= 2 &&
+          delta.last.isInsert;
+      if (shouldRetainDelta &&
+          toggledStyle.isNotEmpty &&
+          delta.length == 2 &&
+          delta.last.data == '\n') {
         // if all attributes are inline, shouldRetainDelta should be false
-        final anyAttributeNotInline = toggledStyle.values.any((attr) => !attr.isInline);
+        final anyAttributeNotInline =
+            toggledStyle.values.any((attr) => !attr.isInline);
         shouldRetainDelta &= anyAttributeNotInline;
       }
       if (shouldRetainDelta) {
@@ -146,7 +163,8 @@ class EditorController extends ChangeNotifier {
 
     textSelection = selection.copyWith(
       baseOffset: delta.transformPosition(selection.baseOffset, force: false),
-      extentOffset: delta.transformPosition(selection.extentOffset, force: false),
+      extentOffset:
+          delta.transformPosition(selection.extentOffset, force: false),
     );
     if (selection != textSelection) {
       _updateSelection(textSelection, source);

+ 136 - 64
app_flowy/packages/flowy_editor/lib/src/widget/raw_editor.dart

@@ -60,7 +60,8 @@ class RawEditor extends StatefulWidget {
     this.embedBuilder,
   )   : assert(maxHeight == null || maxHeight > 0, 'maxHeight cannot be null'),
         assert(minHeight == null || minHeight >= 0, 'minHeight cannot be null'),
-        assert(maxHeight == null || minHeight == null || maxHeight >= minHeight),
+        assert(
+            maxHeight == null || minHeight == null || maxHeight >= minHeight),
         showCursor = showCursor ?? true,
         super(key: key);
 
@@ -111,7 +112,10 @@ abstract class EditorState extends State<RawEditor> {
 }
 
 class _RawEditorState extends EditorState
-    with AutomaticKeepAliveClientMixin<RawEditor>, WidgetsBindingObserver, TickerProviderStateMixin<RawEditor>
+    with
+        AutomaticKeepAliveClientMixin<RawEditor>,
+        WidgetsBindingObserver,
+        TickerProviderStateMixin<RawEditor>
     implements TextSelectionDelegate, TextInputClient {
   final GlobalKey _editorKey = GlobalKey();
   final List<TextEditingValue> _sentRemoteValues = [];
@@ -129,7 +133,8 @@ class _RawEditorState extends EditorState
   bool _didAutoFocus = false;
   bool _keyboardVisible = false;
   DefaultStyles? _styles;
-  final ClipboardStatusNotifier? _clipboardStatus = kIsWeb ? null : ClipboardStatusNotifier();
+  final ClipboardStatusNotifier? _clipboardStatus =
+      kIsWeb ? null : ClipboardStatusNotifier();
   final LayerLink _toolbarLayerLink = LayerLink();
   final LayerLink _startHandleLayerLink = LayerLink();
   final LayerLink _endHandleLayerLink = LayerLink();
@@ -177,57 +182,78 @@ class _RawEditorState extends EditorState
         downKey = key == LogicalKeyboardKey.arrowDown;
 
     if ((rightKey || leftKey) && !(rightKey && leftKey)) {
-      newSelection =
-          _jumpToBeginOrEndOfWord(newSelection, wordModifier, leftKey, rightKey, plainText, lineModifier, shift);
+      newSelection = _jumpToBeginOrEndOfWord(newSelection, wordModifier,
+          leftKey, rightKey, plainText, lineModifier, shift);
     }
 
     if (downKey || upKey) {
-      newSelection = _handleMovingCursorVertically(upKey, downKey, shift, selection, newSelection, plainText);
+      newSelection = _handleMovingCursorVertically(
+          upKey, downKey, shift, selection, newSelection, plainText);
     }
 
     if (!shift) {
-      newSelection = _placeCollapsedSelection(selection, newSelection, leftKey, rightKey);
+      newSelection =
+          _placeCollapsedSelection(selection, newSelection, leftKey, rightKey);
     }
 
     widget.controller.updateSelection(newSelection, ChangeSource.LOCAL);
   }
 
-  TextSelection _placeCollapsedSelection(
-      TextSelection selection, TextSelection newSelection, bool leftKey, bool rightKey) {
+  TextSelection _placeCollapsedSelection(TextSelection selection,
+      TextSelection newSelection, bool leftKey, bool rightKey) {
     var newOffset = newSelection.extentOffset;
     if (!selection.isCollapsed) {
       if (leftKey) {
-        newOffset =
-            newSelection.baseOffset < newSelection.extentOffset ? newSelection.baseOffset : newSelection.extentOffset;
+        newOffset = newSelection.baseOffset < newSelection.extentOffset
+            ? newSelection.baseOffset
+            : newSelection.extentOffset;
       } else if (rightKey) {
-        newOffset =
-            newSelection.baseOffset > newSelection.extentOffset ? newSelection.baseOffset : newSelection.extentOffset;
+        newOffset = newSelection.baseOffset > newSelection.extentOffset
+            ? newSelection.baseOffset
+            : newSelection.extentOffset;
       }
     }
     return TextSelection.fromPosition(TextPosition(offset: newOffset));
   }
 
   TextSelection _handleMovingCursorVertically(
-      bool upKey, bool downKey, bool shift, TextSelection selection, TextSelection newSelection, String plainText) {
-    final originPosition = TextPosition(offset: upKey ? selection.baseOffset : selection.extentOffset);
+      bool upKey,
+      bool downKey,
+      bool shift,
+      TextSelection selection,
+      TextSelection newSelection,
+      String plainText) {
+    final originPosition = TextPosition(
+        offset: upKey ? selection.baseOffset : selection.extentOffset);
 
     final child = getRenderEditor()!.childAtPosition(originPosition);
-    final localPosition = TextPosition(offset: originPosition.offset - child.container.documentOffset);
+    final localPosition = TextPosition(
+        offset: originPosition.offset - child.container.documentOffset);
 
-    var position = upKey ? child.getPositionAbove(localPosition) : child.getPositionBelow(localPosition);
+    var position = upKey
+        ? child.getPositionAbove(localPosition)
+        : child.getPositionBelow(localPosition);
 
     if (position == null) {
-      final sibling = upKey ? getRenderEditor()!.childBefore(child) : getRenderEditor()!.childAfter(child);
+      final sibling = upKey
+          ? getRenderEditor()!.childBefore(child)
+          : getRenderEditor()!.childAfter(child);
       if (sibling == null) {
         position = TextPosition(offset: upKey ? 0 : plainText.length - 1);
       } else {
-        final finalOffset = Offset(child.getOffsetForCaret(localPosition).dx,
-            sibling.getOffsetForCaret(TextPosition(offset: upKey ? sibling.container.length - 1 : 0)).dy);
+        final finalOffset = Offset(
+            child.getOffsetForCaret(localPosition).dx,
+            sibling
+                .getOffsetForCaret(TextPosition(
+                    offset: upKey ? sibling.container.length - 1 : 0))
+                .dy);
         final siblingPosition = sibling.getPositionForOffset(finalOffset);
-        position = TextPosition(offset: sibling.container.documentOffset + siblingPosition.offset);
+        position = TextPosition(
+            offset: sibling.container.documentOffset + siblingPosition.offset);
       }
     } else {
-      position = TextPosition(offset: child.container.documentOffset + position.offset);
+      position = TextPosition(
+          offset: child.container.documentOffset + position.offset);
     }
 
     if (position.offset == newSelection.extentOffset) {
@@ -250,33 +276,47 @@ class _RawEditorState extends EditorState
     return newSelection;
   }
 
-  TextSelection _jumpToBeginOrEndOfWord(TextSelection newSelection, bool wordModifier, bool leftKey, bool rightKey,
-      String plainText, bool lineModifier, bool shift) {
+  TextSelection _jumpToBeginOrEndOfWord(
+      TextSelection newSelection,
+      bool wordModifier,
+      bool leftKey,
+      bool rightKey,
+      String plainText,
+      bool lineModifier,
+      bool shift) {
     if (wordModifier) {
       if (leftKey) {
         final textSelection = getRenderEditor()!.selectWordAtPosition(
-            TextPosition(offset: _previousCharacter(newSelection.extentOffset, plainText, false)));
+            TextPosition(
+                offset: _previousCharacter(
+                    newSelection.extentOffset, plainText, false)));
         return newSelection.copyWith(extentOffset: textSelection.baseOffset);
       }
-      final textSelection = getRenderEditor()!
-          .selectWordAtPosition(TextPosition(offset: _nextCharacter(newSelection.extentOffset, plainText, false)));
+      final textSelection = getRenderEditor()!.selectWordAtPosition(
+          TextPosition(
+              offset:
+                  _nextCharacter(newSelection.extentOffset, plainText, false)));
       return newSelection.copyWith(extentOffset: textSelection.extentOffset);
     } else if (lineModifier) {
       if (leftKey) {
         final textSelection = getRenderEditor()!.selectLineAtPosition(
-            TextPosition(offset: _previousCharacter(newSelection.extentOffset, plainText, false)));
+            TextPosition(
+                offset: _previousCharacter(
+                    newSelection.extentOffset, plainText, false)));
         return newSelection.copyWith(extentOffset: textSelection.baseOffset);
       }
       final startPoint = newSelection.extentOffset;
       if (startPoint < plainText.length) {
-        final textSelection = getRenderEditor()!.selectLineAtPosition(TextPosition(offset: startPoint));
+        final textSelection = getRenderEditor()!
+            .selectLineAtPosition(TextPosition(offset: startPoint));
         return newSelection.copyWith(extentOffset: textSelection.extentOffset);
       }
       return newSelection;
     }
 
     if (rightKey && newSelection.extentOffset < plainText.length) {
-      final nextExtent = _nextCharacter(newSelection.extentOffset, plainText, true);
+      final nextExtent =
+          _nextCharacter(newSelection.extentOffset, plainText, true);
       final distance = nextExtent - newSelection.extentOffset;
       newSelection = newSelection.copyWith(extentOffset: nextExtent);
       if (shift) {
@@ -286,7 +326,8 @@ class _RawEditorState extends EditorState
     }
 
     if (leftKey && newSelection.extentOffset > 0) {
-      final previousExtent = _previousCharacter(newSelection.extentOffset, plainText, true);
+      final previousExtent =
+          _previousCharacter(newSelection.extentOffset, plainText, true);
       final distance = newSelection.extentOffset - previousExtent;
       newSelection = newSelection.copyWith(extentOffset: previousExtent);
       if (shift) {
@@ -326,7 +367,9 @@ class _RawEditorState extends EditorState
     var count = 0;
     int? lastNonWhitespace;
     for (final currentString in string.characters) {
-      if (!includeWhitespace && !WHITE_SPACE.contains(currentString.characters.first.toString().codeUnitAt(0))) {
+      if (!includeWhitespace &&
+          !WHITE_SPACE.contains(
+              currentString.characters.first.toString().codeUnitAt(0))) {
         lastNonWhitespace = count;
       }
       if (count + currentString.length >= index) {
@@ -337,7 +380,8 @@ class _RawEditorState extends EditorState
     return 0;
   }
 
-  bool get hasConnection => _textInputConnection != null && _textInputConnection!.attached;
+  bool get hasConnection =>
+      _textInputConnection != null && _textInputConnection!.attached;
 
   void openConnectionIfNeeded() {
     if (!shouldCreateInputConnection) {
@@ -388,7 +432,8 @@ class _RawEditorState extends EditorState
       return;
     }
 
-    final shouldRemember = textEditingValue.text != _lastKnownRemoteTextEditingValue!.text;
+    final shouldRemember =
+        textEditingValue.text != _lastKnownRemoteTextEditingValue!.text;
     _lastKnownRemoteTextEditingValue = actualValue;
     _textInputConnection!.setEditingState(actualValue);
     if (shouldRemember) {
@@ -397,7 +442,8 @@ class _RawEditorState extends EditorState
   }
 
   @override
-  TextEditingValue? get currentTextEditingValue => _lastKnownRemoteTextEditingValue;
+  TextEditingValue? get currentTextEditingValue =>
+      _lastKnownRemoteTextEditingValue;
 
   @override
   AutofillScope? get currentAutofillScope => null;
@@ -429,7 +475,8 @@ class _RawEditorState extends EditorState
     final text = value.text;
     final cursorPosition = value.selection.extentOffset;
     final diff = getDiff(oldText, text, cursorPosition);
-    widget.controller.replaceText(diff.start, diff.deleted.length, diff.inserted, value.selection);
+    widget.controller.replaceText(
+        diff.start, diff.deleted.length, diff.inserted, value.selection);
   }
 
   @override
@@ -479,8 +526,11 @@ class _RawEditorState extends EditorState
     super.build(context);
 
     var _doc = widget.controller.document;
-    if (_doc.isEmpty() && !widget.focusNode.hasFocus && widget.placeholder != null) {
-      _doc = Document.fromJson(jsonDecode('[{"attributes":{"placeholder":true},"insert":"${widget.placeholder}\\n"}]'));
+    if (_doc.isEmpty() &&
+        !widget.focusNode.hasFocus &&
+        widget.placeholder != null) {
+      _doc = Document.fromJson(jsonDecode(
+          '[{"attributes":{"placeholder":true},"insert":"${widget.placeholder}\\n"}]'));
     }
 
     Widget child = CompositedTransformTarget(
@@ -503,7 +553,8 @@ class _RawEditorState extends EditorState
     );
 
     if (widget.scrollable) {
-      final baselinePadding = EdgeInsets.only(top: _styles!.paragraph!.verticalSpacing.item1);
+      final baselinePadding =
+          EdgeInsets.only(top: _styles!.paragraph!.verticalSpacing.item1);
       child = BaselineProxy(
         textStyle: _styles!.paragraph!.style,
         padding: baselinePadding,
@@ -534,7 +585,8 @@ class _RawEditorState extends EditorState
     );
   }
 
-  void _handleSelectionChanged(TextSelection selection, SelectionChangedCause cause) {
+  void _handleSelectionChanged(
+      TextSelection selection, SelectionChangedCause cause) {
     widget.controller.updateSelection(selection, ChangeSource.LOCAL);
 
     _selectionOverlay?.handlesVisible = _shouldShowSelectionHandles();
@@ -563,7 +615,9 @@ class _RawEditorState extends EditorState
             _styles,
             widget.enableInteractiveSelection,
             _hasFocus,
-            attrs.containsKey(Attribute.codeBlock.key) ? const EdgeInsets.all(16) : null,
+            attrs.containsKey(Attribute.codeBlock.key)
+                ? const EdgeInsets.all(16)
+                : null,
             widget.embedBuilder,
             _cursorController,
             indentLevelCounts);
@@ -575,7 +629,8 @@ class _RawEditorState extends EditorState
     return result;
   }
 
-  EditableTextLine _getEditableTextLineFromNode(Line node, BuildContext context) {
+  EditableTextLine _getEditableTextLineFromNode(
+      Line node, BuildContext context) {
     final textLine = TextLine(
       line: node,
       textDirection: _textDirection,
@@ -598,7 +653,8 @@ class _RawEditorState extends EditorState
     return editableTextLine;
   }
 
-  Tuple2<double, double> _getVerticalSpacingForLine(Line line, DefaultStyles? defaultStyles) {
+  Tuple2<double, double> _getVerticalSpacingForLine(
+      Line line, DefaultStyles? defaultStyles) {
     final attrs = line.style.attributes;
     if (attrs.containsKey(Attribute.header.key)) {
       final int? level = attrs[Attribute.header.key]!.value;
@@ -623,7 +679,8 @@ class _RawEditorState extends EditorState
     return defaultStyles!.paragraph!.verticalSpacing;
   }
 
-  Tuple2<double, double> _getVerticalSpacingForBlock(Block node, DefaultStyles? defaultStyles) {
+  Tuple2<double, double> _getVerticalSpacingForBlock(
+      Block node, DefaultStyles? defaultStyles) {
     final attrs = node.style.attributes;
     if (attrs.containsKey(Attribute.quoteBlock.key)) {
       return defaultStyles!.quote!.verticalSpacing;
@@ -666,7 +723,8 @@ class _RawEditorState extends EditorState
     } else {
       _keyboardVisibilityController = KeyboardVisibilityController();
       _keyboardVisible = _keyboardVisibilityController!.isVisible;
-      _keyboardVisibilitySubscription = _keyboardVisibilityController?.onChange.listen((visible) {
+      _keyboardVisibilitySubscription =
+          _keyboardVisibilityController?.onChange.listen((visible) {
         _keyboardVisible = visible;
         if (visible) {
           _onChangeTextEditingValue();
@@ -689,7 +747,9 @@ class _RawEditorState extends EditorState
     super.didChangeDependencies();
     final parentStyles = EditorStyles.getStyles(context, true);
     final defaultStyles = DefaultStyles.getInstance(context);
-    _styles = (parentStyles != null) ? defaultStyles.merge(parentStyles) : defaultStyles;
+    _styles = (parentStyles != null)
+        ? defaultStyles.merge(parentStyles)
+        : defaultStyles;
 
     if (widget.customStyles != null) {
       _styles = _styles!.merge(widget.customStyles!);
@@ -749,7 +809,8 @@ class _RawEditorState extends EditorState
   }
 
   bool _shouldShowSelectionHandles() {
-    return widget.showSelectionHandles && !widget.controller.selection.isCollapsed;
+    return widget.showSelectionHandles &&
+        !widget.controller.selection.isCollapsed;
   }
 
   void handleDelete(bool forward) {
@@ -760,7 +821,8 @@ class _RawEditorState extends EditorState
     var textAfter = selection.textAfter(plainText);
     if (selection.isCollapsed) {
       if (!forward && textBefore.isNotEmpty) {
-        final characterBoundary = _previousCharacter(textBefore.length, textBefore, true);
+        final characterBoundary =
+            _previousCharacter(textBefore.length, textBefore, true);
         textBefore = textBefore.substring(0, characterBoundary);
         cursorPosition = characterBoundary;
       }
@@ -784,15 +846,13 @@ class _RawEditorState extends EditorState
     final selection = widget.controller.selection;
     final plainText = textEditingValue.text;
     if (shortcut == InputShortcut.SAVE) {
-      bool saved = await widget.controller.save();
-      if (!saved) {
-        log('Unabled to save document.');
-      }
+      widget.controller.save();
       return;
     }
     if (shortcut == InputShortcut.COPY) {
       if (!selection.isCollapsed) {
-        await Clipboard.setData(ClipboardData(text: selection.textInside(plainText)));
+        await Clipboard.setData(
+            ClipboardData(text: selection.textInside(plainText)));
       }
       return;
     }
@@ -809,7 +869,8 @@ class _RawEditorState extends EditorState
         );
 
         textEditingValue = TextEditingValue(
-          text: selection.textBefore(plainText) + selection.textAfter(plainText),
+          text:
+              selection.textBefore(plainText) + selection.textAfter(plainText),
           selection: TextSelection.collapsed(offset: selection.start),
         );
       }
@@ -827,7 +888,8 @@ class _RawEditorState extends EditorState
       }
       return;
     }
-    if (shortcut == InputShortcut.SELECT_ALL && widget.enableInteractiveSelection) {
+    if (shortcut == InputShortcut.SELECT_ALL &&
+        widget.enableInteractiveSelection) {
       widget.controller.updateSelection(
           selection.copyWith(
             baseOffset: 0,
@@ -881,14 +943,16 @@ class _RawEditorState extends EditorState
   void _onChangeTextEditingValue() {
     _showCaretOnScreen();
     updateRemoteValueIfNeeded();
-    _cursorController.startOrStopCursorTimerIfNeeded(_hasFocus, widget.controller.selection);
+    _cursorController.startOrStopCursorTimerIfNeeded(
+        _hasFocus, widget.controller.selection);
     if (hasConnection) {
       _cursorController
         ..stopCursorTimer(resetCharTicks: false)
         ..startCursorTimer();
     }
 
-    SchedulerBinding.instance!.addPostFrameCallback((_) => _updateOrDisposeSelectionOverlayIfNeeded());
+    SchedulerBinding.instance!.addPostFrameCallback(
+        (_) => _updateOrDisposeSelectionOverlayIfNeeded());
     if (mounted) {
       setState(() {
         // Use widget.controller.value in build()
@@ -931,7 +995,8 @@ class _RawEditorState extends EditorState
 
   void _handleFocusChanged() {
     openOrCloseConnection();
-    _cursorController.startOrStopCursorTimerIfNeeded(_hasFocus, widget.controller.selection);
+    _cursorController.startOrStopCursorTimerIfNeeded(
+        _hasFocus, widget.controller.selection);
     _updateOrDisposeSelectionOverlayIfNeeded();
     if (_hasFocus) {
       WidgetsBinding.instance!.addObserver(this);
@@ -962,7 +1027,8 @@ class _RawEditorState extends EditorState
       _showCaretOnScreenScheduled = false;
 
       final viewport = RenderAbstractViewport.of(getRenderEditor())!;
-      final editorOffset = getRenderEditor()!.localToGlobal(const Offset(0, 0), ancestor: viewport);
+      final editorOffset = getRenderEditor()!
+          .localToGlobal(const Offset(0, 0), ancestor: viewport);
       final offsetInViewport = _scrollController!.offset + editorOffset.dy;
 
       final offset = getRenderEditor()!.getOffsetToRevealCursor(
@@ -1045,7 +1111,8 @@ class _RawEditorState extends EditorState
       final value = textEditingValue;
       final data = await Clipboard.getData(Clipboard.kTextPlain);
       if (data != null) {
-        final length = textEditingValue.selection.end - textEditingValue.selection.start;
+        final length =
+            textEditingValue.selection.end - textEditingValue.selection.start;
         widget.controller.replaceText(
           value.selection.start,
           length,
@@ -1054,7 +1121,9 @@ class _RawEditorState extends EditorState
         );
         // move cursor to the end of pasted text selection
         widget.controller.updateSelection(
-            TextSelection.collapsed(offset: value.selection.start + data.text!.length), ChangeSource.LOCAL);
+            TextSelection.collapsed(
+                offset: value.selection.start + data.text!.length),
+            ChangeSource.LOCAL);
       }
     }
   }
@@ -1064,7 +1133,8 @@ class _RawEditorState extends EditorState
     if (data == null) {
       return false;
     }
-    return textEditingValue.text.length - value.text.length == data.text!.length;
+    return textEditingValue.text.length - value.text.length ==
+        data.text!.length;
   }
 
   @override
@@ -1097,7 +1167,8 @@ class _RawEditorState extends EditorState
   }
 
   @override
-  void userUpdateTextEditingValue(TextEditingValue value, SelectionChangedCause cause) {
+  void userUpdateTextEditingValue(
+      TextEditingValue value, SelectionChangedCause cause) {
     // TODO: implement userUpdateTextEditingValue
   }
 }
@@ -1147,7 +1218,8 @@ class _Editor extends MultiChildRenderObjectWidget {
   }
 
   @override
-  void updateRenderObject(BuildContext context, covariant RenderEditor renderObject) {
+  void updateRenderObject(
+      BuildContext context, covariant RenderEditor renderObject) {
     renderObject
       ..document = document
       ..container = document.root