Bläddra i källkod

fix: workaround infinity formatting

Andrei Dolgov 2 år sedan
förälder
incheckning
6a902a2b21

+ 1 - 0
frontend/app_flowy/packages/appflowy_editor/lib/src/document/built_in_attribute_keys.dart

@@ -37,6 +37,7 @@ class BuiltInAttributeKey {
   static String checkbox = 'checkbox';
   static String checkbox = 'checkbox';
   static String code = 'code';
   static String code = 'code';
   static String number = 'number';
   static String number = 'number';
+  static String defaultFormating = 'defaultFormating';
 
 
   static List<String> partialStyleKeys = [
   static List<String> partialStyleKeys = [
     BuiltInAttributeKey.bold,
     BuiltInAttributeKey.bold,

+ 40 - 9
frontend/app_flowy/packages/appflowy_editor/lib/src/operation/transaction_builder.dart

@@ -1,6 +1,7 @@
 import 'dart:collection';
 import 'dart:collection';
 import 'dart:math';
 import 'dart:math';
 
 
+import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:appflowy_editor/src/document/attributes.dart';
 import 'package:appflowy_editor/src/document/attributes.dart';
 import 'package:appflowy_editor/src/document/node.dart';
 import 'package:appflowy_editor/src/document/node.dart';
 import 'package:appflowy_editor/src/document/path.dart';
 import 'package:appflowy_editor/src/document/path.dart';
@@ -114,21 +115,17 @@ class TransactionBuilder {
 
 
   /// Inserts content at a specified index.
   /// Inserts content at a specified index.
   /// Optionally, you may specify formatting attributes that are applied to the inserted string.
   /// Optionally, you may specify formatting attributes that are applied to the inserted string.
-  /// By default, the formatting attributes before the insert position will be used.
+  /// When no formatting attributes specified, the formating attributes before the insert position will be used if they don't have defaultFormatting flag set
+  /// When defaultFormatting flag is set before the insert position, it will be cleared.
+  /// When insert position is within a text having defaultFormatting flag set, the flag will be ignored and clear (formatting attributes of the text will be applied)
   insertText(
   insertText(
     TextNode node,
     TextNode node,
     int index,
     int index,
     String content, {
     String content, {
     Attributes? attributes,
     Attributes? attributes,
   }) {
   }) {
-    var newAttributes = attributes;
-    if (index != 0 && attributes == null) {
-      newAttributes =
-          node.delta.slice(max(index - 1, 0), index).first.attributes;
-      if (newAttributes != null) {
-        newAttributes = Attributes.from(newAttributes);
-      }
-    }
+    final newAttributes = attributes ?? _getAttributesAt(node, index);
+
     textEdit(
     textEdit(
       node,
       node,
       () => Delta()
       () => Delta()
@@ -227,4 +224,38 @@ class TransactionBuilder {
       afterSelection: afterSelection,
       afterSelection: afterSelection,
     );
     );
   }
   }
+
+  Attributes? _getAttributesAt(TextNode node, int index) {
+    if (index == 0) {
+      return null;
+    }
+
+    final previousAttributes =
+        node.delta.slice(index - 1, index).first.attributes;
+
+    final nextAttributes = node.delta.length > index
+        ? node.delta.slice(index, index + 1).first.attributes
+        : null;
+
+    if (previousAttributes == null) {
+      return null;
+    }
+
+    if (previousAttributes.containsKey(BuiltInAttributeKey.defaultFormating)) {
+      Attributes newAttributes = Map.from(previousAttributes)
+        ..removeWhere((key, _) => key == BuiltInAttributeKey.defaultFormating);
+
+      if (node.previous != null) {
+        updateNode(node.next!, newAttributes);
+        if (previousAttributes == nextAttributes) {
+          updateNode(node.next!, newAttributes);
+          return newAttributes;
+        }
+      }
+
+      return null;
+    }
+
+    return Attributes.from(previousAttributes);
+  }
 }
 }

+ 3 - 1
frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text_handler.dart

@@ -1,4 +1,5 @@
 import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:appflowy_editor/appflowy_editor.dart';
+import 'package:appflowy_editor/src/extensions/path_extensions.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/material.dart';
 
 
 // convert **abc** to bold abc.
 // convert **abc** to bold abc.
@@ -51,6 +52,7 @@ ShortcutEventHandler doubleAsterisksToBold = (editorState, event) {
       selection.end.offset - thirdToLastAsteriskIndex - 2,
       selection.end.offset - thirdToLastAsteriskIndex - 2,
       {
       {
         BuiltInAttributeKey.bold: true,
         BuiltInAttributeKey.bold: true,
+        BuiltInAttributeKey.defaultFormating: true,
       },
       },
     )
     )
     ..afterSelection = Selection.collapsed(
     ..afterSelection = Selection.collapsed(
@@ -116,6 +118,7 @@ ShortcutEventHandler doubleUnderscoresToBold = (editorState, event) {
       selection.end.offset - thirdToLastUnderscoreIndex - 2,
       selection.end.offset - thirdToLastUnderscoreIndex - 2,
       {
       {
         BuiltInAttributeKey.bold: true,
         BuiltInAttributeKey.bold: true,
+        BuiltInAttributeKey.defaultFormating: true,
       },
       },
     )
     )
     ..afterSelection = Selection.collapsed(
     ..afterSelection = Selection.collapsed(
@@ -125,7 +128,6 @@ ShortcutEventHandler doubleUnderscoresToBold = (editorState, event) {
       ),
       ),
     )
     )
     ..commit();
     ..commit();
-  editorState.editorStyle == EditorStyle.defaultStyle();
 
 
   return KeyEventResult.handled;
   return KeyEventResult.handled;
 };
 };

+ 1 - 1
frontend/app_flowy/packages/appflowy_editor/test/infra/test_raw_key_event.dart

@@ -143,7 +143,7 @@ extension on LogicalKeyboardKey {
       return PhysicalKeyboardKey.digit8;
       return PhysicalKeyboardKey.digit8;
     }
     }
     if (this == LogicalKeyboardKey.underscore) {
     if (this == LogicalKeyboardKey.underscore) {
-      return PhysicalKeyboardKey.minus gg;
+      return PhysicalKeyboardKey.minus;
     }
     }
     throw UnimplementedError();
     throw UnimplementedError();
   }
   }

+ 78 - 0
frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_handler_test.dart

@@ -92,6 +92,45 @@ void main() async {
         expect(textNode.toRawString(), '*AppFlowy');
         expect(textNode.toRawString(), '*AppFlowy');
       });
       });
 
 
+      testWidgets('**AppFlowy** application to bold AppFlowy only',
+          (tester) async {
+        const boldText = '**AppFlowy*';
+        const normalText = ' application';
+        final editor = tester.editor..insertTextNode('');
+        await editor.startTesting();
+        await editor.updateSelection(
+          Selection.single(path: [0], startOffset: 0),
+        );
+        final textNode = editor.nodeAtPath([0]) as TextNode;
+
+        for (var i = 0; i < boldText.length; i++) {
+          await editor.insertText(textNode, boldText[i], i);
+        }
+        await insertAsterisk(editor);
+        for (var i = 0; i < normalText.length; i++) {
+          await editor.insertText(
+              textNode, normalText[i], i + boldText.length - 3);
+        }
+        final boldTextLength = boldText.replaceAll('*', '').length;
+        final appFlowyBold = textNode.allSatisfyBoldInSelection(
+          Selection.single(
+            path: [0],
+            startOffset: 0,
+            endOffset: boldTextLength,
+          ),
+        );
+        final applicationNormal = textNode.allSatisfyBoldInSelection(
+          Selection.single(
+            path: [0],
+            startOffset: boldTextLength,
+            endOffset: textNode.toRawString().length,
+          ),
+        );
+        expect(appFlowyBold, true);
+        expect(applicationNormal, false);
+        expect(textNode.toRawString(), 'AppFlowy application');
+      });
+
       testWidgets('**** nothing changes', (tester) async {
       testWidgets('**** nothing changes', (tester) async {
         const text = '***';
         const text = '***';
         final editor = tester.editor..insertTextNode('');
         final editor = tester.editor..insertTextNode('');
@@ -198,6 +237,45 @@ void main() async {
         expect(textNode.toRawString(), '_AppFlowy');
         expect(textNode.toRawString(), '_AppFlowy');
       });
       });
 
 
+      testWidgets('__AppFlowy__ application to bold AppFlowy only',
+          (tester) async {
+        const boldText = '__AppFlowy_';
+        const normalText = ' application';
+        final editor = tester.editor..insertTextNode('');
+        await editor.startTesting();
+        await editor.updateSelection(
+          Selection.single(path: [0], startOffset: 0),
+        );
+        final textNode = editor.nodeAtPath([0]) as TextNode;
+
+        for (var i = 0; i < boldText.length; i++) {
+          await editor.insertText(textNode, boldText[i], i);
+        }
+        await insertUnderscore(editor);
+        for (var i = 0; i < normalText.length; i++) {
+          await editor.insertText(
+              textNode, normalText[i], i + boldText.length - 3);
+        }
+        final boldTextLength = boldText.replaceAll('_', '').length;
+        final appFlowyBold = textNode.allSatisfyBoldInSelection(
+          Selection.single(
+            path: [0],
+            startOffset: 0,
+            endOffset: boldTextLength,
+          ),
+        );
+        final applicationNormal = textNode.allSatisfyBoldInSelection(
+          Selection.single(
+            path: [0],
+            startOffset: boldTextLength,
+            endOffset: textNode.toRawString().length,
+          ),
+        );
+        expect(appFlowyBold, true);
+        expect(applicationNormal, false);
+        expect(textNode.toRawString(), 'AppFlowy application');
+      });
+
       testWidgets('____ nothing changes', (tester) async {
       testWidgets('____ nothing changes', (tester) async {
         const text = '___';
         const text = '___';
         final editor = tester.editor..insertTextNode('');
         final editor = tester.editor..insertTextNode('');