feat: delta to markdown

This commit is contained in:
Lucas.Xu 2022-11-08 14:47:10 +08:00
parent ab664ebb2f
commit c85ab276e9
7 changed files with 232 additions and 0 deletions

View File

@ -111,6 +111,47 @@ class _MyHomePageState extends State<MyHomePage> {
if (!darkMode) ...lightEditorStyleExtension,
if (!darkMode) ...lightPlguinStyleExtension,
]);
final delta = Delta();
delta.add(TextInsert('Hello '));
delta.add(
TextInsert(
'World',
attributes: {
BuiltInAttributeKey.bold: true,
BuiltInAttributeKey.italic: true,
},
),
);
delta.add(
TextInsert(
' ',
),
);
delta.add(
TextInsert(
'Again',
attributes: {
BuiltInAttributeKey.italic: true,
},
),
);
delta.add(
TextInsert(
' ',
),
);
delta.add(
TextInsert(
'Again',
attributes: {
BuiltInAttributeKey.href: 'https://google.com',
BuiltInAttributeKey.italic: true,
BuiltInAttributeKey.bold: true,
BuiltInAttributeKey.strikethrough: true,
},
),
);
final result = DeltaMarkdownEncoder().convert(delta);
return Container(
color: darkMode ? Colors.black : Colors.white,
width: MediaQuery.of(context).size.width,

View File

@ -33,3 +33,4 @@ export 'src/render/selection_menu/selection_menu_widget.dart';
export 'src/l10n/l10n.dart';
export 'src/render/style/plugin_styles.dart';
export 'src/render/style/editor_style.dart';
export 'src/plugins/markdown/delta_markdown_encoder.dart';

View File

@ -0,0 +1,88 @@
import 'dart:convert';
import 'package:appflowy_editor/appflowy_editor.dart';
/// A [Delta] encoder that encodes a [Delta] to Markdown.
///
/// Only support inline styles, like bold, italic, underline, strike, code.
class DeltaMarkdownEncoder extends Converter<Delta, String> {
@override
String convert(Delta input) {
final buffer = StringBuffer();
final iterator = input.iterator;
while (iterator.moveNext()) {
final op = iterator.current;
if (op is TextInsert) {
final attributes = op.attributes;
if (attributes != null) {
buffer.write(_prefixSyntax(attributes));
buffer.write(op.text);
buffer.write(_suffixSyntax(attributes));
} else {
buffer.write(op.text);
}
}
}
return buffer.toString();
}
String _prefixSyntax(Attributes attributes) {
var syntax = '';
if (attributes[BuiltInAttributeKey.bold] == true &&
attributes[BuiltInAttributeKey.italic] == true) {
syntax += '***';
} else if (attributes[BuiltInAttributeKey.bold] == true) {
syntax += '**';
} else if (attributes[BuiltInAttributeKey.italic] == true) {
syntax += '_';
}
if (attributes[BuiltInAttributeKey.strikethrough] == true) {
syntax += '~~';
}
if (attributes[BuiltInAttributeKey.underline] == true) {
syntax += '<u>';
}
if (attributes[BuiltInAttributeKey.code] == true) {
syntax += '`';
}
if (attributes[BuiltInAttributeKey.href] != null) {
syntax += '[';
}
return syntax;
}
String _suffixSyntax(Attributes attributes) {
var syntax = '';
if (attributes[BuiltInAttributeKey.href] != null) {
syntax += '](${attributes[BuiltInAttributeKey.href]})';
}
if (attributes[BuiltInAttributeKey.code] == true) {
syntax += '`';
}
if (attributes[BuiltInAttributeKey.underline] == true) {
syntax += '</u>';
}
if (attributes[BuiltInAttributeKey.strikethrough] == true) {
syntax += '~~';
}
if (attributes[BuiltInAttributeKey.bold] == true &&
attributes[BuiltInAttributeKey.italic] == true) {
syntax += '***';
} else if (attributes[BuiltInAttributeKey.bold] == true) {
syntax += '**';
} else if (attributes[BuiltInAttributeKey.italic] == true) {
syntax += '_';
}
return syntax;
}
}

View File

@ -27,6 +27,7 @@ dependencies:
intl:
flutter_localizations:
sdk: flutter
markdown: ^6.0.1
dev_dependencies:
flutter_test:

View File

@ -0,0 +1,100 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter_test/flutter_test.dart';
void main() async {
group('delta_markdown_encoder.dart', () {
test('bold', () {
final delta = Delta(operations: [
TextInsert('Welcome to '),
TextInsert('AppFlowy', attributes: {
BuiltInAttributeKey.bold: true,
}),
]);
final result = DeltaMarkdownEncoder().convert(delta);
expect(result, 'Welcome to **AppFlowy**');
});
test('italic', () {
final delta = Delta(operations: [
TextInsert('Welcome to '),
TextInsert('AppFlowy', attributes: {
BuiltInAttributeKey.italic: true,
}),
]);
final result = DeltaMarkdownEncoder().convert(delta);
expect(result, 'Welcome to _AppFlowy_');
});
test('underline', () {
final delta = Delta(operations: [
TextInsert('Welcome to '),
TextInsert('AppFlowy', attributes: {
BuiltInAttributeKey.underline: true,
}),
]);
final result = DeltaMarkdownEncoder().convert(delta);
expect(result, 'Welcome to <u>AppFlowy</u>');
});
test('strikethrough', () {
final delta = Delta(operations: [
TextInsert('Welcome to '),
TextInsert('AppFlowy', attributes: {
BuiltInAttributeKey.strikethrough: true,
}),
]);
final result = DeltaMarkdownEncoder().convert(delta);
expect(result, 'Welcome to ~~AppFlowy~~');
});
test('href', () {
final delta = Delta(operations: [
TextInsert('Welcome to '),
TextInsert('AppFlowy', attributes: {
BuiltInAttributeKey.href: 'https://appflowy.io',
}),
]);
final result = DeltaMarkdownEncoder().convert(delta);
expect(result, 'Welcome to [AppFlowy](https://appflowy.io)');
});
test('code', () {
final delta = Delta(operations: [
TextInsert('Welcome to '),
TextInsert('AppFlowy', attributes: {
BuiltInAttributeKey.code: true,
}),
]);
final result = DeltaMarkdownEncoder().convert(delta);
expect(result, 'Welcome to `AppFlowy`');
});
test('composition', () {
final delta = Delta(operations: [
TextInsert('Welcome', attributes: {
BuiltInAttributeKey.code: true,
BuiltInAttributeKey.italic: true,
BuiltInAttributeKey.bold: true,
BuiltInAttributeKey.underline: 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 = DeltaMarkdownEncoder().convert(delta);
expect(
result,
'***<u>`Welcome`</u>*** ***~~to~~*** ***[AppFlowy](https://appflowy.io)***',
);
});
});
}