Merge pull request #1209 from alemoreau/double_tilde_to_strikethrough

feat: double tildes to strikethrough text
This commit is contained in:
Lucas.Xu 2022-10-06 10:26:58 +08:00 committed by GitHub
commit e67f91b380
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 243 additions and 66 deletions

View File

@ -22,21 +22,21 @@ class MessageLookup extends MessageLookupByLibrary {
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"bold": MessageLookupByLibrary.simpleMessage(""),
"bulletedList": MessageLookupByLibrary.simpleMessage(""),
"checkbox": MessageLookupByLibrary.simpleMessage(""),
"embedCode": MessageLookupByLibrary.simpleMessage(""),
"heading1": MessageLookupByLibrary.simpleMessage(""),
"heading2": MessageLookupByLibrary.simpleMessage(""),
"heading3": MessageLookupByLibrary.simpleMessage(""),
"highlight": MessageLookupByLibrary.simpleMessage(""),
"image": MessageLookupByLibrary.simpleMessage(""),
"italic": MessageLookupByLibrary.simpleMessage(""),
"link": MessageLookupByLibrary.simpleMessage(""),
"numberedList": MessageLookupByLibrary.simpleMessage(""),
"quote": MessageLookupByLibrary.simpleMessage(""),
"strikethrough": MessageLookupByLibrary.simpleMessage(""),
"text": MessageLookupByLibrary.simpleMessage(""),
"underline": MessageLookupByLibrary.simpleMessage("")
"bold": MessageLookupByLibrary.simpleMessage("gras"),
"bulletedList": MessageLookupByLibrary.simpleMessage("liste à puces"),
"checkbox": MessageLookupByLibrary.simpleMessage("case à cocher"),
"embedCode": MessageLookupByLibrary.simpleMessage("incorporer Code"),
"heading1": MessageLookupByLibrary.simpleMessage("en-tête1"),
"heading2": MessageLookupByLibrary.simpleMessage("en-tête2"),
"heading3": MessageLookupByLibrary.simpleMessage("en-tête3"),
"highlight": MessageLookupByLibrary.simpleMessage("mettre en évidence"),
"image": MessageLookupByLibrary.simpleMessage("limage"),
"italic": MessageLookupByLibrary.simpleMessage("italique"),
"link": MessageLookupByLibrary.simpleMessage("lien"),
"numberedList": MessageLookupByLibrary.simpleMessage("liste numérotée"),
"quote": MessageLookupByLibrary.simpleMessage("citation"),
"strikethrough": MessageLookupByLibrary.simpleMessage("barré"),
"text": MessageLookupByLibrary.simpleMessage("texte"),
"underline": MessageLookupByLibrary.simpleMessage("souligner")
};
}

View File

@ -22,21 +22,21 @@ class MessageLookup extends MessageLookupByLibrary {
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"bold": MessageLookupByLibrary.simpleMessage(""),
"bulletedList": MessageLookupByLibrary.simpleMessage(""),
"checkbox": MessageLookupByLibrary.simpleMessage(""),
"embedCode": MessageLookupByLibrary.simpleMessage(""),
"heading1": MessageLookupByLibrary.simpleMessage(""),
"heading2": MessageLookupByLibrary.simpleMessage(""),
"heading3": MessageLookupByLibrary.simpleMessage(""),
"highlight": MessageLookupByLibrary.simpleMessage(""),
"image": MessageLookupByLibrary.simpleMessage(""),
"italic": MessageLookupByLibrary.simpleMessage(""),
"link": MessageLookupByLibrary.simpleMessage(""),
"numberedList": MessageLookupByLibrary.simpleMessage(""),
"quote": MessageLookupByLibrary.simpleMessage(""),
"strikethrough": MessageLookupByLibrary.simpleMessage(""),
"text": MessageLookupByLibrary.simpleMessage(""),
"underline": MessageLookupByLibrary.simpleMessage("")
"bold": MessageLookupByLibrary.simpleMessage("bátor"),
"bulletedList": MessageLookupByLibrary.simpleMessage("pontozott lista"),
"checkbox": MessageLookupByLibrary.simpleMessage("jelölőnégyzetet"),
"embedCode": MessageLookupByLibrary.simpleMessage("Beágyazás"),
"heading1": MessageLookupByLibrary.simpleMessage("címsor1"),
"heading2": MessageLookupByLibrary.simpleMessage("címsor2"),
"heading3": MessageLookupByLibrary.simpleMessage("címsor3"),
"highlight": MessageLookupByLibrary.simpleMessage("Kiemel"),
"image": MessageLookupByLibrary.simpleMessage("kép"),
"italic": MessageLookupByLibrary.simpleMessage("dőlt"),
"link": MessageLookupByLibrary.simpleMessage("link"),
"numberedList": MessageLookupByLibrary.simpleMessage("számozottLista"),
"quote": MessageLookupByLibrary.simpleMessage("idézet"),
"strikethrough": MessageLookupByLibrary.simpleMessage("áthúzott"),
"text": MessageLookupByLibrary.simpleMessage("szöveg"),
"underline": MessageLookupByLibrary.simpleMessage("aláhúzás")
};
}

View File

@ -22,21 +22,21 @@ class MessageLookup extends MessageLookupByLibrary {
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"bold": MessageLookupByLibrary.simpleMessage(""),
"bulletedList": MessageLookupByLibrary.simpleMessage(""),
"checkbox": MessageLookupByLibrary.simpleMessage(""),
"embedCode": MessageLookupByLibrary.simpleMessage(""),
"heading1": MessageLookupByLibrary.simpleMessage(""),
"heading2": MessageLookupByLibrary.simpleMessage(""),
"heading3": MessageLookupByLibrary.simpleMessage(""),
"highlight": MessageLookupByLibrary.simpleMessage(""),
"image": MessageLookupByLibrary.simpleMessage(""),
"italic": MessageLookupByLibrary.simpleMessage(""),
"link": MessageLookupByLibrary.simpleMessage(""),
"numberedList": MessageLookupByLibrary.simpleMessage(""),
"quote": MessageLookupByLibrary.simpleMessage(""),
"strikethrough": MessageLookupByLibrary.simpleMessage(""),
"text": MessageLookupByLibrary.simpleMessage(""),
"underline": MessageLookupByLibrary.simpleMessage("")
"bold": MessageLookupByLibrary.simpleMessage("berani"),
"bulletedList": MessageLookupByLibrary.simpleMessage("daftar berpoin"),
"checkbox": MessageLookupByLibrary.simpleMessage("kotak centang"),
"embedCode": MessageLookupByLibrary.simpleMessage("menyematkan Kode"),
"heading1": MessageLookupByLibrary.simpleMessage("pos1"),
"heading2": MessageLookupByLibrary.simpleMessage("pos2"),
"heading3": MessageLookupByLibrary.simpleMessage("pos3"),
"highlight": MessageLookupByLibrary.simpleMessage("menyorot"),
"image": MessageLookupByLibrary.simpleMessage("gambar"),
"italic": MessageLookupByLibrary.simpleMessage("miring"),
"link": MessageLookupByLibrary.simpleMessage("tautan"),
"numberedList": MessageLookupByLibrary.simpleMessage("daftar bernomor"),
"quote": MessageLookupByLibrary.simpleMessage("mengutip"),
"strikethrough": MessageLookupByLibrary.simpleMessage("coret"),
"text": MessageLookupByLibrary.simpleMessage("teks"),
"underline": MessageLookupByLibrary.simpleMessage("menggarisbawahi")
};
}

View File

@ -22,21 +22,22 @@ class MessageLookup extends MessageLookupByLibrary {
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
"bold": MessageLookupByLibrary.simpleMessage(""),
"bulletedList": MessageLookupByLibrary.simpleMessage(""),
"checkbox": MessageLookupByLibrary.simpleMessage(""),
"embedCode": MessageLookupByLibrary.simpleMessage(""),
"heading1": MessageLookupByLibrary.simpleMessage(""),
"heading2": MessageLookupByLibrary.simpleMessage(""),
"heading3": MessageLookupByLibrary.simpleMessage(""),
"highlight": MessageLookupByLibrary.simpleMessage(""),
"image": MessageLookupByLibrary.simpleMessage(""),
"italic": MessageLookupByLibrary.simpleMessage(""),
"link": MessageLookupByLibrary.simpleMessage(""),
"numberedList": MessageLookupByLibrary.simpleMessage(""),
"quote": MessageLookupByLibrary.simpleMessage(""),
"strikethrough": MessageLookupByLibrary.simpleMessage(""),
"text": MessageLookupByLibrary.simpleMessage(""),
"underline": MessageLookupByLibrary.simpleMessage("")
"bold": MessageLookupByLibrary.simpleMessage("negrito"),
"bulletedList":
MessageLookupByLibrary.simpleMessage("lista com marcadores"),
"checkbox": MessageLookupByLibrary.simpleMessage("caixa de seleção"),
"embedCode": MessageLookupByLibrary.simpleMessage("Código embutido"),
"heading1": MessageLookupByLibrary.simpleMessage("Cabeçallho 1"),
"heading2": MessageLookupByLibrary.simpleMessage("Cabeçallho 2"),
"heading3": MessageLookupByLibrary.simpleMessage("Cabeçallho 3"),
"highlight": MessageLookupByLibrary.simpleMessage("realçar"),
"image": MessageLookupByLibrary.simpleMessage("imagem"),
"italic": MessageLookupByLibrary.simpleMessage("itálico"),
"link": MessageLookupByLibrary.simpleMessage("link"),
"numberedList": MessageLookupByLibrary.simpleMessage("lista numerada"),
"quote": MessageLookupByLibrary.simpleMessage("citar"),
"strikethrough": MessageLookupByLibrary.simpleMessage("tachado"),
"text": MessageLookupByLibrary.simpleMessage("texto"),
"underline": MessageLookupByLibrary.simpleMessage("sublinhado")
};
}

View File

@ -1,4 +1,3 @@
import "dart:math";
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/extensions/text_node_extensions.dart';
import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart';
@ -49,7 +48,7 @@ ShortcutEventHandler backquoteToCodeHandler = (editorState, event) {
.substring(selection.start.offset, selection.end.offset);
// toggle code style when selected some text
if (selectionText.length > 0) {
if (selectionText.isNotEmpty) {
formatEmbedCode(editorState);
return KeyEventResult.handled;
}
@ -125,6 +124,69 @@ ShortcutEventHandler backquoteToCodeHandler = (editorState, event) {
return KeyEventResult.handled;
};
// convert ~~abc~~ to strikethrough abc.
ShortcutEventHandler doubleTildeToStrikethrough = (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 tildeIndexes = <int>[];
for (var i = 0; i < text.length; i++) {
if (text[i] == '~') {
tildeIndexes.add(i);
}
}
if (tildeIndexes.length < 3) {
return KeyEventResult.ignored;
}
// make sure the second to last and third to last tildes are connected.
final thirdToLastTildeIndex = tildeIndexes[tildeIndexes.length - 3];
final secondToLastTildeIndex = tildeIndexes[tildeIndexes.length - 2];
final lastTildeIndex = tildeIndexes[tildeIndexes.length - 1];
if (secondToLastTildeIndex != thirdToLastTildeIndex + 1 ||
lastTildeIndex == secondToLastTildeIndex + 1) {
return KeyEventResult.ignored;
}
// delete the last three tildes.
// update the style of the text surround by `~~ ~~` to strikethrough.
// and update the cursor position.
TransactionBuilder(editorState)
..deleteText(textNode, lastTildeIndex, 1)
..deleteText(textNode, thirdToLastTildeIndex, 2)
..formatText(
textNode,
thirdToLastTildeIndex,
selection.end.offset - thirdToLastTildeIndex - 2,
{
BuiltInAttributeKey.strikethrough: true,
},
)
..afterSelection = Selection.collapsed(
Position(
path: textNode.path,
offset: selection.end.offset - 3,
),
)
..commit();
return KeyEventResult.handled;
};
/// To create a link, enclose the link text in brackets (e.g., [link text]).
/// Then, immediately follow it with the URL in parentheses (e.g., (https://example.com)).
ShortcutEventHandler markdownLinkToLinkHandler = (editorState, event) {

View File

@ -257,6 +257,11 @@ List<ShortcutEvent> builtInShortcutEvents = [
command: 'backquote',
handler: backquoteToCodeHandler,
),
ShortcutEvent(
key: 'Double tilde to strikethrough',
command: 'shift+tilde',
handler: doubleTildeToStrikethrough,
),
ShortcutEvent(
key: 'Markdown link to link',
command: 'shift+parenthesis right',

View File

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

View File

@ -150,5 +150,111 @@ void main() async {
expect(textNode.toRawString(), text);
});
});
group('convert double tilde to strikethrough', () {
Future<void> insertTilde(
EditorWidgetTester editor, {
int repeat = 1,
}) async {
for (var i = 0; i < repeat; i++) {
await editor.pressLogicKey(
LogicalKeyboardKey.tilde,
isShiftPressed: true,
);
}
}
testWidgets('~~AppFlowy~~ to strikethrough 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 insertTilde(editor);
final allStrikethrough = textNode.allSatisfyStrikethroughInSelection(
Selection.single(
path: [0],
startOffset: 0,
endOffset: textNode.toRawString().length,
),
);
expect(allStrikethrough, true);
expect(textNode.toRawString(), 'AppFlowy');
});
testWidgets('App~~Flowy~~ to strikethrough 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 insertTilde(editor);
final allStrikethrough = textNode.allSatisfyStrikethroughInSelection(
Selection.single(
path: [0],
startOffset: 3,
endOffset: textNode.toRawString().length,
),
);
expect(allStrikethrough, 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 insertTilde(editor);
final allStrikethrough = textNode.allSatisfyStrikethroughInSelection(
Selection.single(
path: [0],
startOffset: 1,
endOffset: textNode.toRawString().length,
),
);
expect(allStrikethrough, 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 insertTilde(editor);
final allStrikethrough = textNode.allSatisfyStrikethroughInSelection(
Selection.single(
path: [0],
startOffset: 0,
endOffset: textNode.toRawString().length,
),
);
expect(allStrikethrough, false);
expect(textNode.toRawString(), text);
});
});
});
}