Skip to content

Commit 9eaa79b

Browse files
committed
feat: document to markdown
1 parent c85ab27 commit 9eaa79b

File tree

8 files changed

+123
-2
lines changed

8 files changed

+123
-2
lines changed

frontend/app_flowy/packages/appflowy_editor/lib/appflowy_editor.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,8 @@ export 'src/render/selection_menu/selection_menu_widget.dart';
3333
export 'src/l10n/l10n.dart';
3434
export 'src/render/style/plugin_styles.dart';
3535
export 'src/render/style/editor_style.dart';
36-
export 'src/plugins/markdown/delta_markdown_encoder.dart';
36+
export 'src/plugins/markdown/encoder/delta_markdown_encoder.dart';
37+
export 'src/plugins/markdown/encoder/document_markdown_encoder.dart';
38+
export 'src/plugins/markdown/encoder/parser/node_parser.dart';
39+
export 'src/plugins/markdown/encoder/parser/text_node_parser.dart';
40+
export 'src/plugins/markdown/encoder/parser/image_node_parser.dart';

frontend/app_flowy/packages/appflowy_editor/lib/src/plugins/markdown/document_markdown_encoder.dart

Whitespace-only changes.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import 'dart:convert';
2+
3+
import 'package:appflowy_editor/src/core/document/document.dart';
4+
import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/image_node_parser.dart';
5+
import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/node_parser.dart';
6+
import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/text_node_parser.dart';
7+
8+
class DocumentMarkdownEncoder extends Converter<Document, String> {
9+
DocumentMarkdownEncoder({
10+
this.parsers = const [
11+
TextNodeParser(),
12+
ImageNodeParser(),
13+
],
14+
});
15+
16+
final List<NodeParser> parsers;
17+
18+
@override
19+
String convert(Document input) {
20+
final buffer = StringBuffer();
21+
for (final node in input.root.children) {
22+
NodeParser? parser =
23+
parsers.firstWhereOrNull((element) => element.id == node.type);
24+
if (parser != null) {
25+
buffer.write(parser.transform(node));
26+
}
27+
}
28+
return buffer.toString();
29+
}
30+
}
31+
32+
extension IterableExtension<T> on Iterable<T> {
33+
T? firstWhereOrNull(bool Function(T element) test) {
34+
for (var element in this) {
35+
if (test(element)) return element;
36+
}
37+
return null;
38+
}
39+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import 'package:appflowy_editor/src/core/document/node.dart';
2+
import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/node_parser.dart';
3+
4+
class ImageNodeParser extends NodeParser {
5+
const ImageNodeParser();
6+
7+
@override
8+
String get id => 'image';
9+
10+
@override
11+
String transform(Node node) {
12+
return '![](${node.attributes['image_src']})';
13+
}
14+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import 'package:appflowy_editor/src/core/document/node.dart';
2+
3+
abstract class NodeParser {
4+
const NodeParser();
5+
6+
String get id;
7+
String transform(Node node);
8+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import 'package:appflowy_editor/src/core/document/node.dart';
2+
import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart';
3+
import 'package:appflowy_editor/src/plugins/markdown/encoder/delta_markdown_encoder.dart';
4+
import 'package:appflowy_editor/src/plugins/markdown/encoder/parser/node_parser.dart';
5+
6+
class TextNodeParser extends NodeParser {
7+
const TextNodeParser();
8+
9+
@override
10+
String get id => 'text';
11+
12+
@override
13+
String transform(Node node) {
14+
assert(node is TextNode);
15+
final textNode = node as TextNode;
16+
final markdown = DeltaMarkdownEncoder().convert(textNode.delta);
17+
final attributes = textNode.attributes;
18+
var result = markdown;
19+
var suffix = '\n';
20+
if (attributes.isNotEmpty &&
21+
attributes.containsKey(BuiltInAttributeKey.subtype)) {
22+
final subtype = attributes[BuiltInAttributeKey.subtype];
23+
if (subtype == 'heading') {
24+
final heading = attributes[BuiltInAttributeKey.heading];
25+
if (heading == 'h1') {
26+
result = '# $markdown';
27+
} else if (heading == 'h2') {
28+
result = '## $markdown';
29+
} else if (heading == 'h3') {
30+
result = '### $markdown';
31+
} else if (heading == 'h4') {
32+
result = '#### $markdown';
33+
} else if (heading == 'h5') {
34+
result = '##### $markdown';
35+
} else if (heading == 'h6') {
36+
result = '###### $markdown';
37+
}
38+
} else if (subtype == 'quote') {
39+
result = '> $markdown';
40+
} else if (subtype == 'code-block') {
41+
result = '```\n$markdown\n```';
42+
} else if (subtype == 'bulleted-list') {
43+
result = '* $markdown';
44+
} else if (subtype == 'number-list') {
45+
final number = attributes['number'];
46+
result = '$number. $markdown';
47+
} else if (subtype == 'checkbox') {
48+
if (attributes[BuiltInAttributeKey.checkbox] == true) {
49+
result = '- [x] $markdown';
50+
} else {
51+
result = '- [ ] $markdown';
52+
}
53+
}
54+
}
55+
return '$result$suffix';
56+
}
57+
}

frontend/app_flowy/packages/appflowy_editor/lib/src/plugins/markdown/markdown_encoder.dart

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)