Skip to content

Commit

Permalink
fix: paste links
Browse files Browse the repository at this point in the history
  • Loading branch information
Xazin committed Aug 6, 2023
1 parent 6706292 commit 69a6161
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ CommandShortcutEventHandler _pasteCommandHandler = (editorState) {
if (html != null && html.isNotEmpty) {
pasteHTML(editorState, html);
} else if (text != null && text.isNotEmpty) {
handlePastePlainText(editorState, data.text!);
handlePastePlainText(editorState, text);
}
}();

Expand Down
70 changes: 43 additions & 27 deletions lib/src/plugins/markdown/decoder/document_markdown_decoder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ class DocumentMarkdownDecoder extends Converter<String, Document> {
final htmlRegex = RegExp('^(http|https)://');
final numberedlistRegex = RegExp(r'^\d+\. ');

// Reference: https://stackoverflow.com/a/6041965
final linkRegExp = RegExp(
r"(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])",
);

@override
Document convert(String input) {
final lines = input.split('\n');
Expand Down Expand Up @@ -62,36 +67,32 @@ class DocumentMarkdownDecoder extends Converter<String, Document> {
if (line.startsWith('### ')) {
return headingNode(
level: 3,
attributes: {'delta': decoder.convert(line.substring(4)).toJson()},
delta: decoder.convert(line.substring(4)),
);
} else if (line.startsWith('## ')) {
return headingNode(
level: 2,
attributes: {'delta': decoder.convert(line.substring(3)).toJson()},
delta: decoder.convert(line.substring(3)),
);
} else if (line.startsWith('# ')) {
return headingNode(
level: 1,
attributes: {'delta': decoder.convert(line.substring(2)).toJson()},
delta: decoder.convert(line.substring(2)),
);
} else if (line.startsWith('- [ ] ')) {
return todoListNode(
checked: false,
attributes: {'delta': decoder.convert(line.substring(6)).toJson()},
delta: decoder.convert(line.substring(6)),
);
} else if (line.startsWith('- [x] ')) {
return todoListNode(
checked: true,
attributes: {'delta': decoder.convert(line.substring(6)).toJson()},
delta: decoder.convert(line.substring(6)),
);
} else if (line.startsWith('> ')) {
return quoteNode(
attributes: {'delta': decoder.convert(line.substring(2)).toJson()},
);
return quoteNode(delta: decoder.convert(line.substring(2)));
} else if (line.startsWith('- ') || line.startsWith('* ')) {
return bulletedListNode(
attributes: {'delta': decoder.convert(line.substring(2)).toJson()},
);
return bulletedListNode(delta: decoder.convert(line.substring(2)));
} else if (line.isNotEmpty && RegExp('^-*').stringMatch(line) == line) {
return Node(type: 'divider');
} else if (line.startsWith('```') && line.endsWith('```')) {
Expand All @@ -112,22 +113,18 @@ class DocumentMarkdownDecoder extends Converter<String, Document> {
}
} else if (numberedlistRegex.hasMatch(line)) {
return numberedListNode(
attributes: {
'delta':
decoder.convert(line.substring(line.indexOf('.') + 1)).toJson()
},
delta: decoder.convert(line.substring(line.indexOf('.') + 1)),
);
} else if (linkRegExp.hasMatch(line)) {
final delta = deltaFromLineWithLinks(line);
return paragraphNode(delta: delta);
}

if (line.isNotEmpty) {
return paragraphNode(
attributes: {'delta': decoder.convert(line).toJson()},
);
return paragraphNode(delta: decoder.convert(line));
}

return paragraphNode(
attributes: {'delta': Delta().toJson()},
);
return paragraphNode(delta: Delta());
}

String? extractImagePath(String text) {
Expand All @@ -140,6 +137,29 @@ class DocumentMarkdownDecoder extends Converter<String, Document> {
return match?.group(1);
}

Delta deltaFromLineWithLinks(String line, [Delta? delta]) {
final matches = linkRegExp.allMatches(line).toList();
final nodeDelta = delta ?? Delta();

int lastUrlOffset = 0;
for (final match in matches) {
if (lastUrlOffset < match.start) {
nodeDelta.insert(line.substring(lastUrlOffset, match.start));
}

final link = line.substring(match.start, match.end);
nodeDelta.insert(link, attributes: {AppFlowyRichTextKeys.href: link});
lastUrlOffset = match.end;
}

final lastMatch = matches.last;
if (lastMatch.end - 1 < line.length - 1) {
nodeDelta.insert(line.substring(lastMatch.end));
}

return nodeDelta;
}

Node _codeBlockNodeFromMarkdown(
String markdown,
DeltaMarkdownDecoder decoder,
Expand All @@ -149,9 +169,7 @@ class DocumentMarkdownDecoder extends Converter<String, Document> {
// so this is treated like a normal paragraph
if (!markdown.contains('\n') &&
markdown.split('`').length - 1 == markdown.length) {
return paragraphNode(
attributes: {'delta': decoder.convert(markdown).toJson()},
);
return paragraphNode(delta: decoder.convert(markdown));
}
const codeMarker = '```';
int codeStartIndex = markdown.indexOf(codeMarker);
Expand All @@ -162,9 +180,7 @@ class DocumentMarkdownDecoder extends Converter<String, Document> {
// This if condition is for handling cases like ```\n`
// In this case codeStartIndex = 0 and codeEndIndex = -1
if (codeEndIndex < codeStartIndex) {
return paragraphNode(
attributes: {'delta': decoder.convert(markdown).toJson()},
);
return paragraphNode(delta: decoder.convert(markdown));
}
String codeBlock = markdown.substring(
codeStartIndex + codeMarker.length,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/plugins/markdown/decoder/document_markdown_decoder.dart';
import 'package:flutter/widgets.dart';

int _textLengthOfNode(Node node) => node.delta?.length ?? 0;
Expand All @@ -9,15 +10,27 @@ void _pasteSingleLine(
String line,
) {
assert(selection.isCollapsed);

final markdownDecoder = DocumentMarkdownDecoder();
final transaction = editorState.transaction;
final node = editorState.getNodeAtPath(selection.end.path)!;
final transaction = editorState.transaction
..insertText(node, selection.startIndex, line)
..afterSelection = (Selection.collapsed(
Position(
path: selection.end.path,
offset: selection.startIndex + line.length,
),
));

if (markdownDecoder.linkRegExp.hasMatch(line)) {
final delta = markdownDecoder.deltaFromLineWithLinks(line, node.delta);
transaction.insertNode(
node.path,
paragraphNode(delta: delta),
);
} else {
transaction.insertText(node, selection.startIndex, line);
}

transaction.afterSelection = Selection.collapsed(
Position(
path: selection.end.path,
offset: selection.startIndex + line.length,
),
);
editorState.apply(transaction);
}

Expand Down

0 comments on commit 69a6161

Please sign in to comment.