-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
193 additions
and
0 deletions.
There are no files selected for viewing
65 changes: 65 additions & 0 deletions
65
...wy_editor/lib/src/service/internal_key_event_handlers/markdown_syntax_to_styled_text.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import 'package:appflowy_editor/appflowy_editor.dart'; | ||
import 'package:flutter/material.dart'; | ||
|
||
// convert **abc** to bold abc. | ||
ShortcutEventHandler doubleAsterisksToBold = (editorState, event) { | ||
final selectionService = editorState.service.selectionService; | ||
final selection = selectionService.currentSelection.value; | ||
final textNodes = selectionService.currentSelectedNodes.whereType<TextNode>(); | ||
if (selection == null || !selection.isSingle || textNodes.length != 1) { | ||
return KeyEventResult.ignored; | ||
} | ||
|
||
final textNode = textNodes.first; | ||
final text = textNode.toRawString().substring(0, selection.end.offset); | ||
|
||
// make sure the last two characters are **. | ||
if (text.length < 2 || text[selection.end.offset - 1] != '*') { | ||
return KeyEventResult.ignored; | ||
} | ||
|
||
// find all the index of `*`. | ||
final asteriskIndexes = <int>[]; | ||
for (var i = 0; i < text.length; i++) { | ||
if (text[i] == '*') { | ||
asteriskIndexes.add(i); | ||
} | ||
} | ||
|
||
if (asteriskIndexes.length < 3) { | ||
return KeyEventResult.ignored; | ||
} | ||
|
||
// make sure the second to last and third to last asterisks are connected. | ||
final thirdToLastAsteriskIndex = asteriskIndexes[asteriskIndexes.length - 3]; | ||
final secondToLastAsteriskIndex = asteriskIndexes[asteriskIndexes.length - 2]; | ||
final lastAsterisIndex = asteriskIndexes[asteriskIndexes.length - 1]; | ||
if (secondToLastAsteriskIndex != thirdToLastAsteriskIndex + 1 || | ||
lastAsterisIndex == secondToLastAsteriskIndex + 1) { | ||
return KeyEventResult.ignored; | ||
} | ||
|
||
// delete the last three asterisks. | ||
// update the style of the text surround by `** **` to bold. | ||
// and update the cursor position. | ||
TransactionBuilder(editorState) | ||
..deleteText(textNode, lastAsterisIndex, 1) | ||
..deleteText(textNode, thirdToLastAsteriskIndex, 2) | ||
..formatText( | ||
textNode, | ||
thirdToLastAsteriskIndex, | ||
selection.end.offset - thirdToLastAsteriskIndex - 2, | ||
{ | ||
BuiltInAttributeKey.bold: true, | ||
}, | ||
) | ||
..afterSelection = Selection.collapsed( | ||
Position( | ||
path: textNode.path, | ||
offset: selection.end.offset - 3, | ||
), | ||
) | ||
..commit(); | ||
|
||
return KeyEventResult.handled; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
119 changes: 119 additions & 0 deletions
119
..._editor/test/service/internal_key_event_handlers/markdown_syntax_to_styled_text_test.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import 'package:appflowy_editor/appflowy_editor.dart'; | ||
import 'package:appflowy_editor/src/extensions/text_node_extensions.dart'; | ||
import 'package:flutter/services.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
import '../../infra/test_editor.dart'; | ||
|
||
void main() async { | ||
setUpAll(() { | ||
TestWidgetsFlutterBinding.ensureInitialized(); | ||
}); | ||
|
||
group('markdown_syntax_to_styled_text.dart', () { | ||
group('convert double asterisks to bold', () { | ||
Future<void> insertAsterisk( | ||
EditorWidgetTester editor, { | ||
int repeat = 1, | ||
}) async { | ||
for (var i = 0; i < repeat; i++) { | ||
await editor.pressLogicKey( | ||
LogicalKeyboardKey.asterisk, | ||
isShiftPressed: true, | ||
); | ||
} | ||
} | ||
|
||
testWidgets('**AppFlowy** to bold AppFlowy', (tester) async { | ||
const text = '**AppFlowy*'; | ||
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 < text.length; i++) { | ||
await editor.insertText(textNode, text[i], i); | ||
} | ||
await insertAsterisk(editor); | ||
final allBold = textNode.allSatisfyBoldInSelection( | ||
Selection.single( | ||
path: [0], | ||
startOffset: 0, | ||
endOffset: textNode.toRawString().length, | ||
), | ||
); | ||
expect(allBold, true); | ||
expect(textNode.toRawString(), 'AppFlowy'); | ||
}); | ||
|
||
testWidgets('App**Flowy** to bold AppFlowy', (tester) async { | ||
const text = 'App**Flowy*'; | ||
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 < text.length; i++) { | ||
await editor.insertText(textNode, text[i], i); | ||
} | ||
await insertAsterisk(editor); | ||
final allBold = textNode.allSatisfyBoldInSelection( | ||
Selection.single( | ||
path: [0], | ||
startOffset: 3, | ||
endOffset: textNode.toRawString().length, | ||
), | ||
); | ||
expect(allBold, true); | ||
expect(textNode.toRawString(), 'AppFlowy'); | ||
}); | ||
|
||
testWidgets('***AppFlowy** to bold *AppFlowy', (tester) async { | ||
const text = '***AppFlowy*'; | ||
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 < text.length; i++) { | ||
await editor.insertText(textNode, text[i], i); | ||
} | ||
await insertAsterisk(editor); | ||
final allBold = textNode.allSatisfyBoldInSelection( | ||
Selection.single( | ||
path: [0], | ||
startOffset: 1, | ||
endOffset: textNode.toRawString().length, | ||
), | ||
); | ||
expect(allBold, true); | ||
expect(textNode.toRawString(), '*AppFlowy'); | ||
}); | ||
|
||
testWidgets('**** nothing changes', (tester) async { | ||
const text = '***'; | ||
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 < text.length; i++) { | ||
await editor.insertText(textNode, text[i], i); | ||
} | ||
await insertAsterisk(editor); | ||
final allBold = textNode.allSatisfyBoldInSelection( | ||
Selection.single( | ||
path: [0], | ||
startOffset: 0, | ||
endOffset: textNode.toRawString().length, | ||
), | ||
); | ||
expect(allBold, false); | ||
expect(textNode.toRawString(), text); | ||
}); | ||
}); | ||
}); | ||
} |