feat: markdown to document

This commit is contained in:
Lucas.Xu 2022-11-08 20:10:09 +08:00
parent fc35f74751
commit 4622a412b7
5 changed files with 251 additions and 3 deletions

View File

@ -50,7 +50,7 @@ class TextInsert extends TextOperation {
final result = <String, dynamic>{
'insert': text,
};
if (_attributes != null) {
if (_attributes != null && _attributes!.isNotEmpty) {
result['attributes'] = attributes;
}
return result;
@ -87,7 +87,7 @@ class TextRetain extends TextOperation {
final result = <String, dynamic>{
'retain': length,
};
if (_attributes != null) {
if (_attributes != null && _attributes!.isNotEmpty) {
result['attributes'] = attributes;
}
return result;

View File

@ -1,6 +1,8 @@
import 'dart:convert';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/core/document/attributes.dart';
import 'package:appflowy_editor/src/core/document/text_delta.dart';
import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart';
import 'package:markdown/markdown.dart' as md;
class DeltaMarkdownDecoder extends Converter<String, Delta>

View File

@ -0,0 +1,91 @@
import 'dart:convert';
import 'package:appflowy_editor/appflowy_editor.dart';
class DocumentMarkdownDecoder extends Converter<String, Document> {
@override
Document convert(String input) {
final lines = input.split('\n');
final document = Document.empty();
var i = 0;
for (final line in lines) {
document.insert([i++], [_convertLineToNode(line)]);
}
return document;
}
Node _convertLineToNode(String text) {
final decoder = DeltaMarkdownDecoder();
// Heading Style
if (text.startsWith('### ')) {
return TextNode(
delta: decoder.convert(text.substring(4)),
attributes: {
BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading,
BuiltInAttributeKey.heading: BuiltInAttributeKey.h3,
},
);
} else if (text.startsWith('## ')) {
return TextNode(
delta: decoder.convert(text.substring(3)),
attributes: {
BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading,
BuiltInAttributeKey.heading: BuiltInAttributeKey.h2,
},
);
} else if (text.startsWith('# ')) {
return TextNode(
delta: decoder.convert(text.substring(2)),
attributes: {
BuiltInAttributeKey.subtype: BuiltInAttributeKey.heading,
BuiltInAttributeKey.heading: BuiltInAttributeKey.h1,
},
);
} else if (text.startsWith('- [ ] ')) {
return TextNode(
delta: decoder.convert(text.substring(6)),
attributes: {
BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox,
BuiltInAttributeKey.checkbox: false,
},
);
} else if (text.startsWith('- [x] ')) {
return TextNode(
delta: decoder.convert(text.substring(6)),
attributes: {
BuiltInAttributeKey.subtype: BuiltInAttributeKey.checkbox,
BuiltInAttributeKey.checkbox: true,
},
);
} else if (text.startsWith('> ')) {
return TextNode(
delta: decoder.convert(text.substring(2)),
attributes: {
BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote,
},
);
} else if (text.startsWith('- ') || text.startsWith('* ')) {
return TextNode(
delta: decoder.convert(text.substring(2)),
attributes: {
BuiltInAttributeKey.subtype: BuiltInAttributeKey.bulletedList,
},
);
} else if (text.startsWith('> ')) {
return TextNode(
delta: decoder.convert(text.substring(2)),
attributes: {
BuiltInAttributeKey.subtype: BuiltInAttributeKey.quote,
},
);
}
if (text.isNotEmpty) {
return TextNode(delta: decoder.convert(text));
}
return TextNode(delta: Delta());
}
}

View File

@ -0,0 +1,29 @@
library delta_markdown;
import 'dart:convert';
import 'package:appflowy_editor/src/core/document/document.dart';
import 'package:appflowy_editor/src/plugins/markdown/encoder/document_markdown_encoder.dart';
/// Codec used to convert between Markdown and AppFlowy Editor Document.
const AppFlowyEditorMarkdownCodec _kCodec = AppFlowyEditorMarkdownCodec();
Document markdownToDocument(String markdown) {
return _kCodec.decode(markdown);
}
String documentToMarkdown(Document document) {
return _kCodec.encode(document);
}
class AppFlowyEditorMarkdownCodec extends Codec<Document, String> {
const AppFlowyEditorMarkdownCodec();
@override
Converter<String, Document> get decoder => throw UnimplementedError();
@override
Converter<Document, String> get encoder {
return DocumentMarkdownEncoder();
}
}

View File

@ -0,0 +1,126 @@
import 'dart:convert';
import 'package:appflowy_editor/src/plugins/markdown/decoder/document_markdown_decoder.dart';
import 'package:flutter_test/flutter_test.dart';
void main() async {
group('document_markdown_decoder.dart', () {
const example = '''
{
"document": {
"type": "editor",
"children": [
{
"type": "text",
"attributes": {"subtype": "heading", "heading": "h2"},
"delta": [
{"insert": "👋 "},
{"insert": "Welcome to", "attributes": {"bold": true}},
{"insert": " "},
{
"insert": "AppFlowy Editor",
"attributes": {"italic": true, "bold": true, "href": "appflowy.io"}
}
]
},
{"type": "text", "delta": []},
{
"type": "text",
"delta": [
{"insert": "AppFlowy Editor is a "},
{"insert": "highly customizable", "attributes": {"bold": true}},
{"insert": " "},
{"insert": "rich-text editor", "attributes": {"italic": true}}
]
},
{
"type": "text",
"attributes": {"subtype": "checkbox", "checkbox": true},
"delta": [{"insert": "Customizable"}]
},
{
"type": "text",
"attributes": {"subtype": "checkbox", "checkbox": true},
"delta": [{"insert": "Test-covered"}]
},
{
"type": "text",
"attributes": {"subtype": "checkbox", "checkbox": false},
"delta": [{"insert": "more to come!"}]
},
{"type": "text", "delta": []},
{
"type": "text",
"attributes": {"subtype": "quote"},
"delta": [{"insert": "Here is an example you can give a try"}]
},
{"type": "text", "delta": []},
{
"type": "text",
"delta": [
{"insert": "You can also use "},
{
"insert": "AppFlowy Editor",
"attributes": {"italic": true, "bold": true}
},
{"insert": " as a component to build your own app."}
]
},
{"type": "text", "delta": []},
{
"type": "text",
"attributes": {"subtype": "bulleted-list"},
"delta": [{"insert": "Use / to insert blocks"}]
},
{
"type": "text",
"attributes": {"subtype": "bulleted-list"},
"delta": [
{
"insert": "Select text to trigger to the toolbar to format your notes."
}
]
},
{"type": "text", "delta": []},
{
"type": "text",
"delta": [
{
"insert": "If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!"
}
]
},
{"type": "text", "delta": []},
{"type": "text", "delta": [{"insert": ""}]}
]
}
}
''';
setUpAll(() {
TestWidgetsFlutterBinding.ensureInitialized();
});
test('parser document', () async {
const markdown = '''
## 👋 **Welcome to** ***[AppFlowy Editor](appflowy.io)***
AppFlowy Editor is a **highly customizable** _rich-text editor_
- [x] Customizable
- [x] Test-covered
- [ ] more to come!
> Here is an example you can give a try
You can also use ***AppFlowy Editor*** as a component to build your own app.
* Use / to insert blocks
* Select text to trigger to the toolbar to format your notes.
If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!
''';
final result = DocumentMarkdownDecoder().convert(markdown);
final data = Map<String, Object>.from(json.decode(example));
expect(result.toJson(), data);
});
});
}