feat: #1061 Support markdown to create a blockquote

This commit is contained in:
Lucas.Xu 2023-01-28 16:09:33 +08:00
parent 53118a6c51
commit b2bc59c6a1
6 changed files with 87 additions and 96 deletions

View File

@ -188,23 +188,6 @@ ShortcutEventHandler doubleTildeToStrikethrough = (editorState, event) {
return KeyEventResult.handled;
};
ShortcutEventHandler greaterToBlockquote = (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;
}
//only convert > at the start of a paragraph
if (selection.startIndex != 0) {
return KeyEventResult.ignored;
}
formatQuote(editorState);
return KeyEventResult.handled;
};
ShortcutEventHandler markdownLinkOrImageHandler = (editorState, event) {
final selectionService = editorState.service.selectionService;
final selection = selectionService.currentSelection.value;

View File

@ -20,6 +20,8 @@ const _bulletedListSymbols = ['*', '-'];
const _checkboxListSymbols = ['[x]', '-[x]'];
const _unCheckboxListSymbols = ['[]', '-[]'];
const _quoteSymbols = ['>'];
final _numberRegex = RegExp(r'^(\d+)\.');
ShortcutEventHandler whiteSpaceHandler = (editorState, event) {
@ -49,6 +51,8 @@ ShortcutEventHandler whiteSpaceHandler = (editorState, event) {
return _toBulletedList(editorState, textNode);
} else if (_countOfSign(text, selection) != 0) {
return _toHeadingStyle(editorState, textNode, selection);
} else if (_quoteSymbols.contains(text)) {
return _toQuoteStyle(editorState, textNode);
} else if (numberMatch != null) {
final matchText = numberMatch.group(0);
final numText = numberMatch.group(1);
@ -196,3 +200,22 @@ int _countOfSign(String text, Selection selection) {
}
return 0;
}
KeyEventResult _toQuoteStyle(EditorState editorState, TextNode textNode) {
if (textNode.subtype == BuiltInAttributeKey.quote) {
return KeyEventResult.ignored;
}
final transaction = editorState.transaction
..deleteText(textNode, 0, 1)
..updateNode(textNode, {
BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote,
})
..afterSelection = Selection.collapsed(
Position(
path: textNode.path,
offset: 0,
),
);
editorState.apply(transaction);
return KeyEventResult.handled;
}

View File

@ -285,11 +285,6 @@ List<ShortcutEvent> builtInShortcutEvents = [
command: 'shift+tilde',
handler: doubleTildeToStrikethrough,
),
ShortcutEvent(
key: 'Greater to blockquote',
command: 'shift+greater',
handler: greaterToBlockquote,
),
ShortcutEvent(
key: 'Markdown link or image',
command: 'shift+parenthesis right',

View File

@ -148,9 +148,6 @@ extension on LogicalKeyboardKey {
if (this == LogicalKeyboardKey.tilde) {
return PhysicalKeyboardKey.backquote;
}
if (this == LogicalKeyboardKey.greater) {
return PhysicalKeyboardKey.intlBackslash;
}
throw UnimplementedError();
}
}

View File

@ -256,76 +256,5 @@ void main() async {
expect(textNode.toPlainText(), text);
});
});
group('convert geater to blockquote', () {
Future<void> insertGreater(
EditorWidgetTester editor, {
int repeat = 1,
}) async {
for (var i = 0; i < repeat; i++) {
await editor.pressLogicKey(
LogicalKeyboardKey.greater,
isShiftPressed: true,
);
}
}
testWidgets('>AppFlowy to blockquote AppFlowy', (tester) async {
const text = 'AppFlowy';
final editor = tester.editor..insertTextNode('');
await editor.startTesting();
await editor.updateSelection(
Selection.single(path: [0], startOffset: 0),
);
await insertGreater(editor);
final textNode = editor.nodeAtPath([0]) as TextNode;
for (var i = 0; i < text.length; i++) {
await editor.insertText(textNode, text[i], i);
}
final isQuote = textNode.subtype == BuiltInAttributeKey.quote;
expect(isQuote, true);
expect(textNode.toPlainText(), 'AppFlowy');
});
testWidgets('AppFlowy> nothing changes', (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 insertGreater(editor);
final isQuote = textNode.subtype == BuiltInAttributeKey.quote;
expect(isQuote, false);
expect(textNode.toPlainText(), text);
});
testWidgets('> in front of text to blockquote', (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 editor.updateSelection(
Selection.single(path: [0], startOffset: 0),
);
await insertGreater(editor);
final isQuote = textNode.subtype == BuiltInAttributeKey.quote;
expect(isQuote, true);
expect(textNode.toPlainText(), text);
});
});
});
}

View File

@ -184,6 +184,10 @@ void main() async {
Selection.single(path: [0], startOffset: 0),
);
await editor.insertText(textNode, '>', 0);
await editor.pressLogicKey(LogicalKeyboardKey.space);
expect(textNode.subtype, BuiltInAttributeKey.quote);
await editor.insertText(textNode, '*', 0);
await editor.pressLogicKey(LogicalKeyboardKey.space);
expect(textNode.subtype, BuiltInAttributeKey.bulletedList);
@ -227,5 +231,65 @@ void main() async {
expect(textNode.subtype, null);
expect(textNode.toPlainText(), text);
});
group('convert geater to blockquote', () {
testWidgets('> AppFlowy to blockquote 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;
await editor.insertText(textNode, '>', 0);
await editor.pressLogicKey(LogicalKeyboardKey.space);
expect(textNode.subtype, BuiltInAttributeKey.quote);
for (var i = 0; i < text.length; i++) {
await editor.insertText(textNode, text[i], i);
}
expect(textNode.toPlainText(), 'AppFlowy');
});
testWidgets('AppFlowy > nothing changes', (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 editor.pressLogicKey(LogicalKeyboardKey.space);
final isQuote = textNode.subtype == BuiltInAttributeKey.quote;
expect(isQuote, false);
expect(textNode.toPlainText(), text);
});
testWidgets('> in front of text to blockquote', (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 editor.updateSelection(
Selection.single(path: [0], startOffset: 0),
);
await editor.insertText(textNode, '>', 0);
await editor.pressLogicKey(LogicalKeyboardKey.space);
final isQuote = textNode.subtype == BuiltInAttributeKey.quote;
expect(isQuote, true);
expect(textNode.toPlainText(), text);
});
});
});
}