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:
Mayur Mahajan 2023-08-30 07:28:56 +05:30 committed by GitHub
parent 4e863bc87f
commit b88aed887a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 127 additions and 3 deletions

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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(),

View File

@ -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;
};

View File

@ -477,7 +477,8 @@
}
},
"selectionMenu": {
"outline": "Outline"
"outline": "Outline",
"codeBlock": "Code Block"
},
"plugins": {
"referencedBoard": "Referenced Board",