mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: markdown to document
This commit is contained in:
parent
fc35f74751
commit
4622a412b7
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user