mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: paste multiple lines in codeblock (#3151)
* fix: paste multiple lines in codeblock * fix: works with non collapsed selection * chore: localize code block * test: multiline paste in codeblock * chore: remove unused import * fix: only hanlde code block paste command if all the selected nodes are code block --------- Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
parent
4e863bc87f
commit
b88aed887a
@ -0,0 +1,60 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('paste in codeblock', () {
|
||||
testWidgets('paste multiple lines in codeblock', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName();
|
||||
|
||||
// mock the clipboard
|
||||
const lines = 3;
|
||||
final text = List.generate(lines, (index) => 'line $index').join('\n');
|
||||
AppFlowyClipboard.mockSetData(
|
||||
AppFlowyClipboardData(
|
||||
text: text,
|
||||
),
|
||||
);
|
||||
|
||||
await insertCodeBlockInDocument(tester);
|
||||
|
||||
// paste the text
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyV,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
expect(editorState.document.root.children.length, 1);
|
||||
expect(
|
||||
editorState.getNodeAtPath([0])!.delta!.toPlainText(),
|
||||
text,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Inserts an codeBlock in the document
|
||||
Future<void> insertCodeBlockInDocument(WidgetTester tester) async {
|
||||
// open the actions menu and insert the codeBlock
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName(
|
||||
LocaleKeys.document_selectionMenu_codeBlock.tr(),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
@ -11,6 +11,7 @@ import 'document_with_toggle_list_test.dart' as document_with_toggle_list_test;
|
||||
import 'edit_document_test.dart' as document_edit_test;
|
||||
import 'document_with_outline_block_test.dart' as document_with_outline_block;
|
||||
import 'document_copy_and_paste_test.dart' as document_copy_and_paste_test;
|
||||
import 'document_codeblock_paste_test.dart' as document_codeblock_paste_test;
|
||||
|
||||
void startTesting() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@ -25,4 +26,5 @@ void startTesting() {
|
||||
document_with_outline_block.main();
|
||||
document_with_toggle_list_test.main();
|
||||
document_copy_and_paste_test.main();
|
||||
document_codeblock_paste_test.main();
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/selectable_item_list_menu.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/string_extension.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:highlight/highlight.dart' as highlight;
|
||||
@ -40,7 +42,7 @@ Node codeBlockNode({
|
||||
|
||||
// defining the callout block menu item for selection
|
||||
SelectionMenuItem codeBlockItem = SelectionMenuItem.node(
|
||||
name: 'Code Block',
|
||||
name: LocaleKeys.document_selectionMenu_codeBlock.tr(),
|
||||
iconData: Icons.abc,
|
||||
keywords: ['code', 'codeblock'],
|
||||
nodeBuilder: (editorState, _) => codeBlockNode(),
|
||||
|
@ -9,9 +9,10 @@ final List<CharacterShortcutEvent> codeBlockCharacterEvents = [
|
||||
|
||||
final List<CommandShortcutEvent> codeBlockCommands = [
|
||||
insertNewParagraphNextToCodeBlockCommand,
|
||||
pasteInCodeblock,
|
||||
selectAllInCodeBlockCommand,
|
||||
tabToInsertSpacesInCodeBlockCommand,
|
||||
tabToDeleteSpacesInCodeBlockCommand,
|
||||
selectAllInCodeBlockCommand,
|
||||
];
|
||||
|
||||
/// press the enter key in code block to insert a new line in it.
|
||||
@ -97,6 +98,18 @@ final CommandShortcutEvent selectAllInCodeBlockCommand = CommandShortcutEvent(
|
||||
handler: _selectAllInCodeBlockCommandHandler,
|
||||
);
|
||||
|
||||
/// ctrl + v to paste text in code block.
|
||||
///
|
||||
/// - support
|
||||
/// - desktop
|
||||
/// - web
|
||||
final CommandShortcutEvent pasteInCodeblock = CommandShortcutEvent(
|
||||
key: 'paste in codeblock',
|
||||
command: 'ctrl+v',
|
||||
macOSCommand: 'cmd+v',
|
||||
handler: _pasteInCodeBlock,
|
||||
);
|
||||
|
||||
CharacterShortcutEventHandler _enterInCodeBlockCommandHandler =
|
||||
(editorState) async {
|
||||
final selection = editorState.selection;
|
||||
@ -267,3 +280,49 @@ CommandShortcutEventHandler _selectAllInCodeBlockCommandHandler =
|
||||
|
||||
return KeyEventResult.handled;
|
||||
};
|
||||
|
||||
CommandShortcutEventHandler _pasteInCodeBlock = (editorState) {
|
||||
var selection = editorState.selection;
|
||||
|
||||
if (selection == null) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
if (editorState.getNodesInSelection(selection).length != 1) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
final node = editorState.getNodeAtPath(selection.end.path);
|
||||
if (node == null || node.type != CodeBlockKeys.type) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
// delete the selection first.
|
||||
if (!selection.isCollapsed) {
|
||||
editorState.deleteSelection(selection);
|
||||
}
|
||||
|
||||
// fetch selection again.
|
||||
selection = editorState.selection;
|
||||
if (selection == null) {
|
||||
return KeyEventResult.skipRemainingHandlers;
|
||||
}
|
||||
assert(selection.isCollapsed);
|
||||
|
||||
() async {
|
||||
final data = await AppFlowyClipboard.getData();
|
||||
final text = data.text;
|
||||
if (text != null && text.isNotEmpty) {
|
||||
final transaction = editorState.transaction
|
||||
..insertText(
|
||||
node,
|
||||
selection!.end.offset,
|
||||
text,
|
||||
);
|
||||
|
||||
await editorState.apply(transaction);
|
||||
}
|
||||
}();
|
||||
|
||||
return KeyEventResult.handled;
|
||||
};
|
||||
|
@ -477,7 +477,8 @@
|
||||
}
|
||||
},
|
||||
"selectionMenu": {
|
||||
"outline": "Outline"
|
||||
"outline": "Outline",
|
||||
"codeBlock": "Code Block"
|
||||
},
|
||||
"plugins": {
|
||||
"referencedBoard": "Referenced Board",
|
||||
|
Loading…
Reference in New Issue
Block a user