mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: markdown to delta
This commit is contained in:
parent
f6e1f2185e
commit
fc35f74751
@ -38,3 +38,4 @@ export 'src/plugins/markdown/encoder/document_markdown_encoder.dart';
|
||||
export 'src/plugins/markdown/encoder/parser/node_parser.dart';
|
||||
export 'src/plugins/markdown/encoder/parser/text_node_parser.dart';
|
||||
export 'src/plugins/markdown/encoder/parser/image_node_parser.dart';
|
||||
export 'src/plugins/markdown/decoder/delta_markdown_decoder.dart';
|
||||
|
@ -62,7 +62,7 @@ class TextInsert extends TextOperation {
|
||||
|
||||
return other is TextInsert &&
|
||||
other.text == text &&
|
||||
mapEquals(_attributes, other._attributes);
|
||||
_mapEquals(_attributes, other._attributes);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -99,7 +99,7 @@ class TextRetain extends TextOperation {
|
||||
|
||||
return other is TextRetain &&
|
||||
other.length == length &&
|
||||
mapEquals(_attributes, other._attributes);
|
||||
_mapEquals(_attributes, other._attributes);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -181,7 +181,7 @@ class Delta extends Iterable<TextOperation> {
|
||||
lastOp.length += textOperation.length;
|
||||
return;
|
||||
}
|
||||
if (mapEquals(lastOp.attributes, textOperation.attributes)) {
|
||||
if (_mapEquals(lastOp.attributes, textOperation.attributes)) {
|
||||
if (lastOp is TextInsert && textOperation is TextInsert) {
|
||||
lastOp.text += textOperation.text;
|
||||
return;
|
||||
@ -539,3 +539,10 @@ class _OpIterator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _mapEquals<T, U>(Map<T, U>? a, Map<T, U>? b) {
|
||||
if ((a == null || a.isEmpty) && (b == null || b.isEmpty)) {
|
||||
return true;
|
||||
}
|
||||
return mapEquals(a, b);
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:markdown/markdown.dart' as md;
|
||||
|
||||
class DeltaMarkdownDecoder extends Converter<String, Delta>
|
||||
with md.NodeVisitor {
|
||||
final _delta = Delta();
|
||||
final Attributes _attributes = {};
|
||||
|
||||
@override
|
||||
Delta convert(String input) {
|
||||
final document =
|
||||
md.Document(extensionSet: md.ExtensionSet.gitHubWeb).parseInline(input);
|
||||
for (final node in document) {
|
||||
node.accept(this);
|
||||
}
|
||||
return _delta;
|
||||
}
|
||||
|
||||
@override
|
||||
void visitElementAfter(md.Element element) {
|
||||
_removeAttributeKey(element);
|
||||
}
|
||||
|
||||
@override
|
||||
bool visitElementBefore(md.Element element) {
|
||||
_addAttributeKey(element);
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
void visitText(md.Text text) {
|
||||
_delta.add(TextInsert(text.text, attributes: {..._attributes}));
|
||||
}
|
||||
|
||||
void _addAttributeKey(md.Element element) {
|
||||
if (element.tag == 'strong') {
|
||||
_attributes[BuiltInAttributeKey.bold] = true;
|
||||
} else if (element.tag == 'em') {
|
||||
_attributes[BuiltInAttributeKey.italic] = true;
|
||||
} else if (element.tag == 'code') {
|
||||
_attributes[BuiltInAttributeKey.code] = true;
|
||||
} else if (element.tag == 'del') {
|
||||
_attributes[BuiltInAttributeKey.strikethrough] = true;
|
||||
} else if (element.tag == 'a') {
|
||||
_attributes[BuiltInAttributeKey.href] = element.attributes['href'];
|
||||
}
|
||||
}
|
||||
|
||||
void _removeAttributeKey(md.Element element) {
|
||||
if (element.tag == 'strong') {
|
||||
_attributes.remove(BuiltInAttributeKey.bold);
|
||||
} else if (element.tag == 'em') {
|
||||
_attributes.remove(BuiltInAttributeKey.italic);
|
||||
} else if (element.tag == 'code') {
|
||||
_attributes.remove(BuiltInAttributeKey.code);
|
||||
} else if (element.tag == 'del') {
|
||||
_attributes.remove(BuiltInAttributeKey.strikethrough);
|
||||
} else if (element.tag == 'a') {
|
||||
_attributes.remove(BuiltInAttributeKey.href);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() async {
|
||||
group('delta_markdown_decoder.dart', () {
|
||||
test('bold', () {
|
||||
final delta = Delta(operations: [
|
||||
TextInsert('Welcome to '),
|
||||
TextInsert('AppFlowy', attributes: {
|
||||
BuiltInAttributeKey.bold: true,
|
||||
}),
|
||||
]);
|
||||
final result = DeltaMarkdownDecoder().convert('Welcome to **AppFlowy**');
|
||||
expect(result, delta);
|
||||
});
|
||||
|
||||
test('italic', () {
|
||||
final delta = Delta(operations: [
|
||||
TextInsert('Welcome to '),
|
||||
TextInsert('AppFlowy', attributes: {
|
||||
BuiltInAttributeKey.italic: true,
|
||||
}),
|
||||
]);
|
||||
final result = DeltaMarkdownDecoder().convert('Welcome to _AppFlowy_');
|
||||
expect(result, delta);
|
||||
});
|
||||
|
||||
test('strikethrough', () {
|
||||
final delta = Delta(operations: [
|
||||
TextInsert('Welcome to '),
|
||||
TextInsert('AppFlowy', attributes: {
|
||||
BuiltInAttributeKey.strikethrough: true,
|
||||
}),
|
||||
]);
|
||||
final result = DeltaMarkdownDecoder().convert('Welcome to ~~AppFlowy~~');
|
||||
expect(result, delta);
|
||||
});
|
||||
|
||||
test('href', () {
|
||||
final delta = Delta(operations: [
|
||||
TextInsert('Welcome to '),
|
||||
TextInsert('AppFlowy', attributes: {
|
||||
BuiltInAttributeKey.href: 'https://appflowy.io',
|
||||
}),
|
||||
]);
|
||||
final result = DeltaMarkdownDecoder()
|
||||
.convert('Welcome to [AppFlowy](https://appflowy.io)');
|
||||
expect(result, delta);
|
||||
});
|
||||
|
||||
test('code', () {
|
||||
final delta = Delta(operations: [
|
||||
TextInsert('Welcome to '),
|
||||
TextInsert('AppFlowy', attributes: {
|
||||
BuiltInAttributeKey.code: true,
|
||||
}),
|
||||
]);
|
||||
final result = DeltaMarkdownDecoder().convert('Welcome to `AppFlowy`');
|
||||
expect(result, delta);
|
||||
});
|
||||
|
||||
test('bold', () {
|
||||
const markdown =
|
||||
'***<u>`Welcome`</u>*** ***~~to~~*** ***[AppFlowy](https://appflowy.io)***';
|
||||
final delta = Delta(operations: [
|
||||
TextInsert('<u>', attributes: {
|
||||
BuiltInAttributeKey.italic: true,
|
||||
BuiltInAttributeKey.bold: true,
|
||||
}),
|
||||
TextInsert('Welcome', attributes: {
|
||||
BuiltInAttributeKey.code: true,
|
||||
BuiltInAttributeKey.italic: true,
|
||||
BuiltInAttributeKey.bold: true,
|
||||
}),
|
||||
TextInsert('</u>', attributes: {
|
||||
BuiltInAttributeKey.italic: true,
|
||||
BuiltInAttributeKey.bold: true,
|
||||
}),
|
||||
TextInsert(' '),
|
||||
TextInsert('to', attributes: {
|
||||
BuiltInAttributeKey.italic: true,
|
||||
BuiltInAttributeKey.bold: true,
|
||||
BuiltInAttributeKey.strikethrough: true,
|
||||
}),
|
||||
TextInsert(' '),
|
||||
TextInsert('AppFlowy', attributes: {
|
||||
BuiltInAttributeKey.href: 'https://appflowy.io',
|
||||
BuiltInAttributeKey.bold: true,
|
||||
BuiltInAttributeKey.italic: true,
|
||||
}),
|
||||
]);
|
||||
final result = DeltaMarkdownDecoder().convert(markdown);
|
||||
expect(result, delta);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user